Geometry Nodes: Run nodes once on unique instance data

As described in T91672, often it can be much more efficient to run each
node only on the unique geometry of the instances, rather than realizing
all instances and potentially processing redundant data. Sometimes the
performance difference can be completely smooth vs. completely unusable.

Geometry nodes used to hide that choice from users by always realizing
instances, but recently we have decided to expose it. So this commit
makes nodes run once per unique reference in the entire tree of nested
instances in their input geometries, continuing the work started in
rB0559971ab377 and rBf94164d89629f0d2. For the old behavior, a realize
instances node can be added before the nodes, which is done in the
versioning code.

Differential Revision: https://developer.blender.org/D12656
This commit is contained in:
Hans Goudey 2021-09-28 11:36:28 -05:00
parent b32b38b380
commit 44e4f077a9
Notes: blender-bot 2023-05-18 21:10:43 +02:00
Referenced by commit fc06a471f1, Geometry Nodes: Only use realized geometry in mesh boolean node
17 changed files with 279 additions and 131 deletions

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 27
#define BLENDER_FILE_SUBVERSION 28
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -330,6 +330,7 @@ struct GeometrySet {
bool has_volume() const;
bool has_curve() const;
bool has_realized_data() const;
bool is_empty() const;
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;

View File

@ -318,6 +318,16 @@ bool GeometrySet::has_realized_data() const
return this->get_component_for_read<InstancesComponent>() == nullptr;
}
/* Return true if the geometry set has any component that isn't empty. */
bool GeometrySet::is_empty() const
{
if (components_.is_empty()) {
return true;
}
return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() ||
this->has_instances());
}
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{

View File

@ -449,6 +449,73 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis
#undef SEQ_SPEED_COMPRESS_IPO_Y
}
static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket)
{
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tosock == in_socket) {
return link;
}
}
return NULL;
}
static void add_realize_instances_before_socket(bNodeTree *ntree,
bNode *node,
bNodeSocket *geometry_socket)
{
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
bNodeLink *link = find_connected_link(ntree, geometry_socket);
if (link == NULL) {
return;
}
/* If the realize instances node is already before this socket, no need to continue. */
if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) {
return;
}
bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES);
realize_node->parent = node->parent;
realize_node->locx = node->locx - 100;
realize_node->locy = node->locy;
nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first);
link->fromnode = realize_node;
link->fromsock = realize_node->outputs.first;
}
/**
* If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize
* Instances" node in front of it to avoid changing behavior. Don't do this if the node will be
* replaced anyway though.
*/
static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (ELEM(node->type,
GEO_NODE_ATTRIBUTE_CAPTURE,
GEO_NODE_SEPARATE_COMPONENTS,
GEO_NODE_CONVEX_HULL,
GEO_NODE_CURVE_LENGTH,
GEO_NODE_BOOLEAN,
GEO_NODE_CURVE_FILLET,
GEO_NODE_CURVE_RESAMPLE,
GEO_NODE_CURVE_TO_MESH,
GEO_NODE_CURVE_TRIM,
GEO_NODE_MATERIAL_REPLACE,
GEO_NODE_MESH_SUBDIVIDE,
GEO_NODE_ATTRIBUTE_REMOVE,
GEO_NODE_TRIANGULATE)) {
bNodeSocket *geometry_socket = node->inputs.first;
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
/* Also realize instances for the profile input of the curve to mesh node. */
if (node->type == GEO_NODE_CURVE_TO_MESH) {
bNodeSocket *profile_socket = node->inputs.last;
add_realize_instances_before_socket(ntree, node, profile_socket);
}
}
}
void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
{
if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
@ -531,6 +598,14 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_geometry_nodes_add_realize_instance_nodes(ntree);
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
const bNode &node = params.node();
const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
node.storage;

View File

@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
remove_attribute(
geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);

View File

@ -83,10 +83,14 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
GeometrySet set_a;
if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
set_a = params.extract_input<GeometrySet>("Geometry 1");
if (set_a.has_instances()) {
params.error_message_add(
NodeWarningType::Info,
TIP_("Instances are not supported for the first geometry input, and will not be used"));
}
/* Note that it technically wouldn't be necessary to realize the instances for the first
* geometry input, but the boolean code expects the first shape for the difference operation
* to be a single mesh. */
set_a = geometry_set_realize_instances(set_a);
const Mesh *mesh_in_a = set_a.get_mesh_for_read();
if (mesh_in_a != nullptr) {
meshes.append(mesh_in_a);

View File

@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
}
/* Since only positions are read from the instances, this can be used as an internal optimization
* to avoid the cost of realizing instances before the node. But disable this for now, since
* re-enabling that optimization will be a separate step. */
# if 0
static void read_positions(const GeometryComponent &component,
Span<float4x4> transforms,
Vector<float3> *r_coords)
Span<float4x4> transforms,
Vector<float3> *r_coords)
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve,
}
}
static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
{
Vector<GeometryInstanceGroup> set_groups;
bke::geometry_set_gather_instances(geometry_set, set_groups);
Vector<float3> coords;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
Span<float4x4> transforms = set_group.transforms;
if (set.has_pointcloud()) {
read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
}
if (set.has_mesh()) {
read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
}
if (set.has_curve()) {
read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
}
}
return hull_from_bullet(nullptr, coords);
}
# endif
#endif /* WITH_BULLET */
static void geo_node_convex_hull_exec(GeoNodeExecParams params)
@ -272,33 +301,14 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
#ifdef WITH_BULLET
Mesh *mesh = nullptr;
if (geometry_set.has_instances()) {
Vector<GeometryInstanceGroup> set_groups;
bke::geometry_set_gather_instances(geometry_set, set_groups);
Vector<float3> coords;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = compute_hull(geometry_set);
geometry_set.replace_mesh(mesh);
geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
});
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
Span<float4x4> transforms = set_group.transforms;
if (set.has_pointcloud()) {
read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
}
if (set.has_mesh()) {
read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
}
if (set.has_curve()) {
read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
}
}
mesh = hull_from_bullet(nullptr, coords);
}
else {
mesh = compute_hull(geometry_set);
}
params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
params.set_output("Convex Hull", std::move(geometry_set));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without Bullet"));

View File

@ -563,19 +563,16 @@ static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
return output_curve;
}
static void geo_node_fillet_exec(GeoNodeExecParams params)
static void calculate_curve_fillet(GeometrySet &geometry_set,
const GeometryNodeCurveFilletMode mode,
const Field<float> &radius_field,
const std::optional<Field<int>> &count_field,
const bool limit_radius)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
}
NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
FilletParam fillet_param;
fillet_param.mode = mode;
@ -584,19 +581,16 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
fn::FieldEvaluator field_evaluator{field_context, domain_size};
Field<float> radius_field = params.extract_input<Field<float>>("Radius");
field_evaluator.add(std::move(radius_field));
field_evaluator.add(radius_field);
if (mode == GEO_NODE_CURVE_FILLET_POLY) {
Field<int> count_field = params.extract_input<Field<int>>("Count");
field_evaluator.add(std::move(count_field));
field_evaluator.add(*count_field);
}
field_evaluator.evaluate();
fillet_param.radii = &field_evaluator.get_evaluated<float>(0);
if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) {
params.set_output("Geometry", geometry_set);
return;
}
@ -604,13 +598,36 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
fillet_param.counts = &field_evaluator.get_evaluated<int>(1);
}
fillet_param.limit_radius = params.extract_input<bool>("Limit Radius");
fillet_param.limit_radius = limit_radius;
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param);
params.set_output("Curve", GeometrySet::create_with_curve(output_curve.release()));
geometry_set.replace_curve(output_curve.release());
}
static void geo_node_fillet_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
Field<float> radius_field = params.extract_input<Field<float>>("Radius");
const bool limit_radius = params.extract_input<bool>("Limit Radius");
std::optional<Field<int>> count_field;
if (mode == GEO_NODE_CURVE_FILLET_POLY) {
count_field.emplace(params.extract_input<Field<int>>("Count"));
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
});
params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
void register_node_type_geo_curve_fillet()

View File

@ -28,7 +28,6 @@ static void geo_node_curve_length_declare(NodeDeclarationBuilder &b)
static void geo_node_curve_length_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
curve_set = bke::geometry_set_realize_instances(curve_set);
if (!curve_set.has_curve()) {
params.set_output("Length", 0.0f);
return;

View File

@ -202,20 +202,26 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
return output_curve;
}
static void geo_node_resample_exec(GeoNodeExecParams params)
static void geometry_set_curve_resample(GeometrySet &geometry_set,
const SampleModeParam &mode_param)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Geometry", GeometrySet());
return;
}
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
geometry_set.replace_curve(output_curve.release());
}
static void geo_node_resample_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
SampleModeParam mode_param;
mode_param.mode = mode;
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
@ -232,9 +238,10 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
mode_param.length.emplace(resolution);
}
std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); });
params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes

View File

@ -32,41 +32,54 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Mesh");
}
static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const GeoNodeExecParams &params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
curve_set = bke::geometry_set_realize_instances(curve_set);
profile_set = bke::geometry_set_realize_instances(profile_set);
/* NOTE: Theoretically an "is empty" check would be more correct for errors. */
if (profile_set.has_mesh() && !profile_set.has_curve()) {
params.error_message_add(NodeWarningType::Warning,
TIP_("No curve data available in profile input"));
}
if (!curve_set.has_curve()) {
if (curve_set.has_mesh()) {
if (!geometry_set.has_curve()) {
if (!geometry_set.is_empty()) {
params.error_message_add(NodeWarningType::Warning,
TIP_("No curve data available in curve input"));
}
params.set_output("Mesh", GeometrySet());
return;
}
const CurveEval *profile_curve = profile_set.get_curve_for_read();
if (profile_curve == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(*curve_set.get_curve_for_read());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read());
geometry_set.replace_mesh(mesh);
}
else {
Mesh *mesh = bke::curve_to_mesh_sweep(*curve_set.get_curve_for_read(), *profile_curve);
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve);
geometry_set.replace_mesh(mesh);
}
}
static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
if (profile_set.has_instances()) {
params.error_message_add(NodeWarningType::Error,
TIP_("Instances are not supported in the profile input"));
params.set_output("Mesh", GeometrySet());
return;
}
if (!profile_set.has_curve() && !profile_set.is_empty()) {
params.error_message_add(NodeWarningType::Warning,
TIP_("No curve data available in the profile input"));
}
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_to_mesh(geometry_set, profile_set, params);
});
params.set_output("Mesh", std::move(curve_set));
}
} // namespace blender::nodes
void register_node_type_geo_curve_to_mesh()

View File

@ -320,28 +320,18 @@ static void trim_bezier_spline(Spline &spline,
bezier_spline.resize(size);
}
static void geo_node_curve_trim_exec(GeoNodeExecParams params)
static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
const float start,
const float end)
{
const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", std::move(geometry_set));
return;
}
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
CurveEval &curve = *curve_component.get_for_write();
CurveEval &curve = *geometry_set.get_curve_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
const float start = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
params.extract_input<float>("Start") :
params.extract_input<float>("Start_001");
const float end = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input<float>("End") :
params.extract_input<float>("End_001");
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
Spline &spline = *splines[i];
@ -382,6 +372,29 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params)
splines[i]->mark_cache_invalid();
}
});
}
static void geo_node_curve_trim_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
const float start = params.extract_input<float>("Start");
const float end = params.extract_input<float>("End");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, start, end);
});
}
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
const float start = params.extract_input<float>("Start_001");
const float end = params.extract_input<float>("End_001");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry_set_curve_trim(geometry_set, mode, start, end);
});
}
params.set_output("Curve", std::move(geometry_set));
}

View File

@ -40,11 +40,9 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
Material *new_material = params.extract_input<Material *>("New");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh *mesh = mesh_component.get_for_write();
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = geometry_set.get_mesh_for_write();
if (mesh != nullptr) {
for (const int i : IndexRange(mesh->totcol)) {
if (mesh->mat[i] == old_material) {
@ -52,7 +50,7 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
}
}
}
}
});
params.set_output("Geometry", std::move(geometry_set));
}

View File

@ -32,28 +32,9 @@ static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set = geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
return;
}
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
/* See CCGSUBSURF_LEVEL_MAX for max limit. */
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
if (subdiv_level == 0) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
@ -61,7 +42,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
mesh_settings.resolution = (1 << subdiv_level) + 1;
mesh_settings.resolution = (1 << level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
@ -79,7 +60,6 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
@ -90,6 +70,29 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
mesh_component.replace(mesh_out);
BKE_subdiv_free(subdiv);
}
static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
/* See CCGSUBSURF_LEVEL_MAX for max limit. */
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
if (subdiv_level == 0) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
params.set_output("Geometry", std::move(geometry_set));
}

View File

@ -25,20 +25,18 @@ static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Point Cloud");
b.add_output<decl::Geometry>("Curve");
b.add_output<decl::Geometry>("Volume");
b.add_output<decl::Geometry>("Instances");
}
static void geo_node_separate_components_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
/* Note that it will be possible to skip realizing instances here when instancing
* geometry directly is supported by creating corresponding geometry instances. */
geometry_set = bke::geometry_set_realize_instances(geometry_set);
GeometrySet meshes;
GeometrySet point_clouds;
GeometrySet volumes;
GeometrySet curves;
GeometrySet instances;
if (geometry_set.has<MeshComponent>()) {
meshes.add(*geometry_set.get_component_for_read<MeshComponent>());
@ -52,11 +50,15 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params)
if (geometry_set.has<VolumeComponent>()) {
volumes.add(*geometry_set.get_component_for_read<VolumeComponent>());
}
if (geometry_set.has<InstancesComponent>()) {
instances.add(*geometry_set.get_component_for_read<InstancesComponent>());
}
params.set_output("Mesh", meshes);
params.set_output("Point Cloud", point_clouds);
params.set_output("Curve", curves);
params.set_output("Volume", volumes);
params.set_output("Instances", instances);
}
} // namespace blender::nodes

View File

@ -58,14 +58,14 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params)
GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
params.node().custom2);
geometry_set = geometry_set_realize_instances(geometry_set);
/* #triangulate_mesh might modify the input mesh currently. */
Mesh *mesh_in = geometry_set.get_mesh_for_write();
if (mesh_in != nullptr) {
Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
geometry_set.replace_mesh(mesh_out);
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
/* #triangulate_mesh might modify the input mesh currently. */
Mesh *mesh_in = geometry_set.get_mesh_for_write();
if (mesh_in != nullptr) {
Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
geometry_set.replace_mesh(mesh_out);
}
});
params.set_output("Geometry", std::move(geometry_set));
}