Fix #141469: Geometry Nodes: use safer approach to modifying each instance geometry

Many nodes operate on all the instances that are passed into them. For example,
the Subdivision Surface node subdivides the mesh at the root but also instanced
meshes. This works well for most nodes, but there are a few nodes were the old
`modify_geometry_sets` function was not very well defined and it was tricky to
use correctly.

The fundamental problem was that the behavior is not obvious when a node creates
or modifies instances and how those are integrated with the already existing
instances.

This patch solves this with the following changes:
* Remove the old `GeometrySet::modify_geometry_sets` and related
  `*_during_modify` methods.
* Add a new `blender::geometry::foreach_real_geometry` function that is similar
  to the old `modify_geometry_sets` but has a more well-defined interface:
  * It never passes instances into the callback. So existing instances can't be
    modified with it.
  * The callback is allowed to create new instances. This will automatically be
    merged back with potentially already existing instances. The callback does
    not have to worry about accidentally invalidating existing instances like
    before.
* A few existing usages used `modify_geometry_sets` to actually modify existing
  instances (usually just removing attributes). Those can't use the new
  `foreach_real_geometry`, so they just get a custom simple recursive
  implementation instead of using a generic function.

Pull Request: https://projects.blender.org/blender/blender/pulls/143898
This commit is contained in:
Jacques Lucke
2025-08-05 06:25:20 +02:00
parent b9b71dbb71
commit 2c435ce8df
72 changed files with 577 additions and 353 deletions

View File

@@ -210,12 +210,6 @@ struct GeometrySet {
* Remove all geometry components with types that are not in the provided list.
*/
void keep_only(Span<GeometryComponent::Type> component_types);
/**
* Keeps the provided geometry types, but also instances and edit data.
* Instances must not be removed while using #modify_geometry_sets.
*/
void keep_only_during_modify(Span<GeometryComponent::Type> component_types);
void remove_geometry_during_modify();
void add(const GeometryComponent &component);
@@ -272,14 +266,6 @@ struct GeometrySet {
Vector<GeometryComponent::Type> gather_component_types(bool include_instances,
bool ignore_empty) const;
using ForeachSubGeometryCallback = FunctionRef<void(GeometrySet &geometry_set)>;
/**
* Modify every (recursive) instance separately. This is often more efficient than realizing all
* instances just to change the same thing on all of them.
*/
void modify_geometry_sets(ForeachSubGeometryCallback callback);
/* Utility methods for creation. */
/**
* Create a new geometry set that only contains the given mesh.

View File

@@ -168,6 +168,8 @@ class Instances {
void add_instance(int instance_handle, const float4x4 &transform);
Span<InstanceReference> references() const;
MutableSpan<InstanceReference> references_for_write();
void remove_unused_references();
/**

View File

@@ -53,52 +53,60 @@ static std::unique_ptr<BakeMaterialsList> materials_to_weak_references(
return materials_list;
}
static void prepare_geometry_for_bake_recursive(GeometrySet &geometry,
BakeDataBlockMap *data_block_map)
{
if (Mesh *mesh = geometry.get_mesh_for_write()) {
mesh->attributes_for_write().remove_anonymous();
mesh->runtime->bake_materials = materials_to_weak_references(
&mesh->mat, &mesh->totcol, data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
curves->geometry.wrap().attributes_for_write().remove_anonymous();
curves->geometry.runtime->bake_materials = materials_to_weak_references(
&curves->mat, &curves->totcol, data_block_map);
}
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
drawing.strokes_for_write().attributes_for_write().remove_anonymous();
}
grease_pencil->attributes_for_write().remove_anonymous();
grease_pencil->runtime->bake_materials = materials_to_weak_references(
&grease_pencil->material_array, &grease_pencil->material_array_num, data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
pointcloud->attributes_for_write().remove_anonymous();
pointcloud->runtime->bake_materials = materials_to_weak_references(
&pointcloud->mat, &pointcloud->totcol, data_block_map);
}
if (Volume *volume = geometry.get_volume_for_write()) {
volume->runtime->bake_materials = materials_to_weak_references(
&volume->mat, &volume->totcol, data_block_map);
}
if (bke::Instances *instances = geometry.get_instances_for_write()) {
instances->attributes_for_write().remove_anonymous();
instances->ensure_geometry_instances();
for (bke::InstanceReference &reference : instances->references_for_write()) {
if (reference.type() == bke::InstanceReference::Type::GeometrySet) {
prepare_geometry_for_bake_recursive(reference.geometry_set(), data_block_map);
}
else {
/* Can only bake geometry instances currently. */
reference = bke::InstanceReference();
}
}
}
}
void GeometryBakeItem::prepare_geometry_for_bake(GeometrySet &main_geometry,
BakeDataBlockMap *data_block_map)
{
main_geometry.ensure_owns_all_data();
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
mesh->attributes_for_write().remove_anonymous();
mesh->runtime->bake_materials = materials_to_weak_references(
&mesh->mat, &mesh->totcol, data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
curves->geometry.wrap().attributes_for_write().remove_anonymous();
curves->geometry.runtime->bake_materials = materials_to_weak_references(
&curves->mat, &curves->totcol, data_block_map);
}
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
greasepencil::Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
drawing.strokes_for_write().attributes_for_write().remove_anonymous();
}
grease_pencil->attributes_for_write().remove_anonymous();
grease_pencil->runtime->bake_materials = materials_to_weak_references(
&grease_pencil->material_array, &grease_pencil->material_array_num, data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
pointcloud->attributes_for_write().remove_anonymous();
pointcloud->runtime->bake_materials = materials_to_weak_references(
&pointcloud->mat, &pointcloud->totcol, data_block_map);
}
if (Volume *volume = geometry.get_volume_for_write()) {
volume->runtime->bake_materials = materials_to_weak_references(
&volume->mat, &volume->totcol, data_block_map);
}
if (bke::Instances *instances = geometry.get_instances_for_write()) {
instances->attributes_for_write().remove_anonymous();
}
geometry.keep_only_during_modify({GeometryComponent::Type::Mesh,
GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Volume,
GeometryComponent::Type::Instance});
});
prepare_geometry_for_bake_recursive(main_geometry, data_block_map);
}
static void restore_materials(Material ***materials,
@@ -125,39 +133,47 @@ static void restore_materials(Material ***materials,
}
}
static void restore_data_blocks_recursive(GeometrySet &geometry, BakeDataBlockMap *data_block_map)
{
if (Mesh *mesh = geometry.get_mesh_for_write()) {
restore_materials(
&mesh->mat, &mesh->totcol, std::move(mesh->runtime->bake_materials), data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
restore_materials(&curves->mat,
&curves->totcol,
std::move(curves->geometry.runtime->bake_materials),
data_block_map);
}
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
restore_materials(&grease_pencil->material_array,
&grease_pencil->material_array_num,
std::move(grease_pencil->runtime->bake_materials),
data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
restore_materials(&pointcloud->mat,
&pointcloud->totcol,
std::move(pointcloud->runtime->bake_materials),
data_block_map);
}
if (Volume *volume = geometry.get_volume_for_write()) {
restore_materials(
&volume->mat, &volume->totcol, std::move(volume->runtime->bake_materials), data_block_map);
}
if (bke::Instances *instances = geometry.get_instances_for_write()) {
for (bke::InstanceReference &reference : instances->references_for_write()) {
if (reference.type() == bke::InstanceReference::Type::GeometrySet) {
restore_data_blocks_recursive(reference.geometry_set(), data_block_map);
}
}
}
}
void GeometryBakeItem::try_restore_data_blocks(GeometrySet &main_geometry,
BakeDataBlockMap *data_block_map)
{
main_geometry.modify_geometry_sets([&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
restore_materials(
&mesh->mat, &mesh->totcol, std::move(mesh->runtime->bake_materials), data_block_map);
}
if (Curves *curves = geometry.get_curves_for_write()) {
restore_materials(&curves->mat,
&curves->totcol,
std::move(curves->geometry.runtime->bake_materials),
data_block_map);
}
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
restore_materials(&grease_pencil->material_array,
&grease_pencil->material_array_num,
std::move(grease_pencil->runtime->bake_materials),
data_block_map);
}
if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
restore_materials(&pointcloud->mat,
&pointcloud->totcol,
std::move(pointcloud->runtime->bake_materials),
data_block_map);
}
if (Volume *volume = geometry.get_volume_for_write()) {
restore_materials(&volume->mat,
&volume->totcol,
std::move(volume->runtime->bake_materials),
data_block_map);
}
});
restore_data_blocks_recursive(main_geometry, data_block_map);
}
#ifdef WITH_OPENVDB

View File

@@ -164,19 +164,6 @@ void GeometrySet::keep_only(const Span<GeometryComponent::Type> component_types)
}
}
void GeometrySet::keep_only_during_modify(const Span<GeometryComponent::Type> component_types)
{
Vector<GeometryComponent::Type> extended_types = component_types;
extended_types.append_non_duplicates(GeometryComponent::Type::Instance);
extended_types.append_non_duplicates(GeometryComponent::Type::Edit);
this->keep_only(extended_types);
}
void GeometrySet::remove_geometry_during_modify()
{
this->keep_only_during_modify({});
}
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[size_t(component.type())]);
@@ -796,39 +783,6 @@ Vector<GeometryComponent::Type> GeometrySet::gather_component_types(const bool i
return types;
}
static void gather_mutable_geometry_sets(GeometrySet &geometry_set,
Vector<GeometrySet *> &r_geometry_sets)
{
r_geometry_sets.append(&geometry_set);
if (!geometry_set.has_instances()) {
return;
}
/* In the future this can be improved by deduplicating instance references across different
* instances. */
Instances &instances = *geometry_set.get_instances_for_write();
instances.ensure_geometry_instances();
for (const int handle : instances.references().index_range()) {
if (instances.references()[handle].type() == InstanceReference::Type::GeometrySet) {
GeometrySet &instance_geometry = instances.geometry_set_from_reference(handle);
gather_mutable_geometry_sets(instance_geometry, r_geometry_sets);
}
}
}
void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback)
{
Vector<GeometrySet *> geometry_sets;
gather_mutable_geometry_sets(*this, geometry_sets);
if (geometry_sets.size() == 1) {
/* Avoid possible overhead and a large call stack when multithreading is pointless. */
callback(*geometry_sets.first());
}
else {
threading::parallel_for_each(geometry_sets,
[&](GeometrySet *geometry_set) { callback(*geometry_set); });
}
}
bool object_has_geometry_set_instances(const Object &object)
{
const GeometrySet *geometry_set = object.runtime->geometry_set_eval;

View File

@@ -275,6 +275,11 @@ Span<InstanceReference> Instances::references() const
return references_;
}
MutableSpan<InstanceReference> Instances::references_for_write()
{
return references_;
}
void Instances::remove(const IndexMask &mask, const AttributeFilter &attribute_filter)
{
const std::optional<IndexRange> masked_range = mask.to_range();

View File

@@ -20,6 +20,7 @@ set(SRC
intern/fillet_curves.cc
intern/fit_curves.cc
intern/interpolate_curves.cc
intern/foreach_geometry.cc
intern/join_geometries.cc
intern/merge_curves.cc
intern/merge_layers.cc
@@ -63,6 +64,7 @@ set(SRC
GEO_extract_elements.hh
GEO_fillet_curves.hh
GEO_fit_curves.hh
GEO_foreach_geometry.hh
GEO_interpolate_curves.hh
GEO_join_geometries.hh
GEO_merge_curves.hh

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_geometry_set.hh"
namespace blender::geometry {
/**
* Modify every real geometry separately, including those from instances. The input to the
* callback never contains instances. Newly generated instances will be merged with the
* previously existing ones.
*/
void foreach_real_geometry(bke::GeometrySet &geometry,
FunctionRef<void(bke::GeometrySet &geometry_set)> fn);
} // namespace blender::geometry

View File

@@ -8,10 +8,18 @@
namespace blender::geometry {
/**
* \param allow_merging_instance_references: If true, instance references from multiple instances
* components may be merged when they are the same. This is typically good because it reduces the
* amount of processing for later nodes. However, this may be undesirable in some cases if the
* instance references are modified afterwards and the calling code assumes that the instances
* references are just concatenated.
*/
bke::GeometrySet join_geometries(Span<bke::GeometrySet> geometries,
const bke::AttributeFilter &attribute_filter,
const std::optional<Span<bke::GeometryComponent::Type>>
&component_types_to_join = std::nullopt);
&component_types_to_join = std::nullopt,
bool allow_merging_instance_references = true);
void join_attributes(const Span<const bke::GeometryComponent *> src_components,
bke::GeometryComponent &r_result,

View File

@@ -0,0 +1,105 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_geometry_set.hh"
#include "BKE_instances.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_join_geometries.hh"
namespace blender::geometry {
static void extract_real_geometries_recursive(
bke::GeometrySet &geometry,
Vector<int> &path,
Map<bke::GeometrySet, Vector<Vector<int>>> &r_real_geometries)
{
bke::GeometrySet real_geometry = geometry;
real_geometry.remove(bke::GeometryComponent::Type::Instance);
geometry.keep_only({bke::GeometryComponent::Type::Instance});
r_real_geometries.lookup_or_add_default(std::move(real_geometry)).append(path);
bke::Instances *instances = geometry.get_instances_for_write();
if (!instances) {
return;
}
instances->ensure_geometry_instances();
MutableSpan<bke::InstanceReference> references = instances->references_for_write();
for (const int i : references.index_range()) {
bke::InstanceReference &reference = references[i];
if (reference.type() == bke::InstanceReference::Type::GeometrySet) {
bke::GeometrySet &sub_geometry = reference.geometry_set();
path.append(i);
extract_real_geometries_recursive(sub_geometry, path, r_real_geometries);
path.pop_last();
}
}
}
static void reinsert_modified_geometry_recursive(bke::GeometrySet &geometry,
const bke::GeometrySet &geometry_to_insert,
const Span<int> path)
{
if (path.is_empty()) {
/* Instance references must not be merged here as that could invalidate the paths. */
const bool allow_merging_instance_references = false;
/* Important to pass the old geometry first, so that the instance reference paths stay
* valid. */
geometry = join_geometries(
{geometry, geometry_to_insert}, {}, {}, allow_merging_instance_references);
return;
}
bke::Instances *instances = geometry.get_instances_for_write();
BLI_assert(instances);
const int reference_i = path.first();
const MutableSpan<bke::InstanceReference> references = instances->references_for_write();
BLI_assert(reference_i < references.size());
bke::InstanceReference &reference = references[reference_i];
BLI_assert(reference.type() == bke::InstanceReference::Type::GeometrySet);
bke::GeometrySet &sub_geometry = reference.geometry_set();
reinsert_modified_geometry_recursive(sub_geometry, geometry_to_insert, path.drop_front(1));
}
struct GeometryWithPaths {
bke::GeometrySet geometry;
Vector<Vector<int>> paths;
};
void foreach_real_geometry(bke::GeometrySet &geometry,
FunctionRef<void(bke::GeometrySet &geometry_set)> fn)
{
/* Afterwards the geometry does not have realized geometry anymore. It has been extracted and
* will be reinserted afterwards. */
Map<bke::GeometrySet, Vector<Vector<int>>> real_geometries;
{
Vector<int> path;
extract_real_geometries_recursive(geometry, path, real_geometries);
}
/* Take the geometries out of the map so that they can be edited in-place. As keys in the #Map
* the geometries are const and thus can't be modified. */
Vector<GeometryWithPaths> geometries_with_paths;
for (auto &&item : real_geometries.items()) {
geometries_with_paths.append({item.key, std::move(item.value)});
}
/* Clear to avoid extra references to the geometries which prohibit editing them in-place. */
real_geometries.clear();
/* Actually modify the geometries in parallel. */
threading::parallel_for(geometries_with_paths.index_range(), 1, [&](const IndexRange range) {
for (const int i : range) {
bke::GeometrySet &geometry_to_modify = geometries_with_paths[i].geometry;
fn(geometry_to_modify);
}
});
/* Reinsert modified geometries. */
for (GeometryWithPaths &geometry_with_paths : geometries_with_paths) {
for (const Span<int> path : geometry_with_paths.paths) {
reinsert_modified_geometry_recursive(geometry, geometry_with_paths.geometry, path);
}
}
}
} // namespace blender::geometry

View File

@@ -95,6 +95,7 @@ void join_attributes(const Span<const GeometryComponent *> src_components,
}
static void join_instances(const Span<const GeometryComponent *> src_components,
const bool allow_merging_instance_references,
GeometrySet &result)
{
Array<int> offsets_data(src_components.size() + 1);
@@ -109,7 +110,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
MutableSpan<int> all_handles = dst_instances->reference_handles_for_write();
Map<std::reference_wrapper<const bke::InstanceReference>, int> new_handle_by_src_reference;
Map<std::reference_wrapper<const bke::InstanceReference>, int> new_handle_by_src_reference_cache;
for (const int i : src_components.index_range()) {
const auto &src_component = static_cast<const bke::InstancesComponent &>(*src_components[i]);
@@ -119,8 +120,13 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
Array<int> handle_map(src_references.size());
for (const int src_handle : src_references.index_range()) {
const bke::InstanceReference &src_reference = src_references[src_handle];
handle_map[src_handle] = new_handle_by_src_reference.lookup_or_add_cb(
src_reference, [&]() { return dst_instances->add_new_reference(src_reference); });
if (allow_merging_instance_references) {
handle_map[src_handle] = new_handle_by_src_reference_cache.lookup_or_add_cb(
src_reference, [&]() { return dst_instances->add_new_reference(src_reference); });
}
else {
handle_map[src_handle] = dst_instances->add_new_reference(src_reference);
}
}
const IndexRange dst_range = offsets[i];
@@ -144,6 +150,7 @@ static void join_volumes(const Span<const GeometryComponent *> /*src_components*
static void join_component_type(const bke::GeometryComponent::Type component_type,
const Span<GeometrySet> src_geometry_sets,
const bke::AttributeFilter &attribute_filter,
const bool allow_merging_instance_references,
GeometrySet &result)
{
Vector<const GeometryComponent *> components;
@@ -164,7 +171,7 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ
switch (component_type) {
case bke::GeometryComponent::Type::Instance:
join_instances(components, result);
join_instances(components, allow_merging_instance_references, result);
return;
case bke::GeometryComponent::Type::Volume:
join_volumes(components, result);
@@ -199,7 +206,8 @@ static void join_component_type(const bke::GeometryComponent::Type component_typ
GeometrySet join_geometries(
const Span<GeometrySet> geometries,
const bke::AttributeFilter &attribute_filter,
const std::optional<Span<GeometryComponent::Type>> &component_types_to_join)
const std::optional<Span<GeometryComponent::Type>> &component_types_to_join,
const bool allow_merging_instance_references)
{
GeometrySet result;
result.name = geometries.is_empty() ? "" : geometries[0].name;
@@ -218,7 +226,8 @@ GeometrySet join_geometries(
supported_types);
for (const GeometryComponent::Type type : types_to_join) {
join_component_type(type, geometries, attribute_filter, result);
join_component_type(
type, geometries, attribute_filter, allow_merging_instance_references, result);
}
return result;

View File

@@ -2426,11 +2426,17 @@ static void execute_realize_edit_data_tasks(const Span<RealizeEditDataTask> task
static void remove_id_attribute_from_instances(bke::GeometrySet &geometry_set)
{
geometry_set.modify_geometry_sets([&](bke::GeometrySet &sub_geometry) {
if (Instances *instances = sub_geometry.get_instances_for_write()) {
instances->attributes_for_write().remove("id");
Instances *instances = geometry_set.get_instances_for_write();
if (!instances) {
return;
}
instances->attributes_for_write().remove("id");
instances->ensure_geometry_instances();
for (bke::InstanceReference &reference : instances->references_for_write()) {
if (reference.type() == bke::InstanceReference::Type::GeometrySet) {
remove_id_attribute_from_instances(reference.geometry_set());
}
});
}
}
/** Propagate instances from the old geometry set to the new geometry set if they are not

View File

@@ -18,6 +18,8 @@
#include "BKE_library.hh"
#include "BKE_screen.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_attribute_capture_cc {
@@ -196,7 +198,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil};
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
for (const GeometryComponent::Type type : types) {
if (geometry_set.has(type)) {
capture_on(geometry_set.get_component_for_write(type));

View File

@@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_primitive_cuboid.hh"
#include "GEO_transform.hh"
@@ -49,7 +50,7 @@ static void node_geo_exec(GeoNodeExecParams params)
* every instance). Because geometry components are reference counted anyway, we can just
* repurpose the original geometry sets for the output. */
if (params.output_is_required("Bounding Box")) {
geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &sub_geometry) {
std::optional<Bounds<float3>> sub_bounds;
/* Reuse the min and max calculation if this is the main "real" geometry set. */
@@ -61,7 +62,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (!sub_bounds) {
sub_geometry.remove_geometry_during_modify();
sub_geometry.clear();
}
else {
const float3 scale = sub_bounds->max - sub_bounds->min;
@@ -69,7 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
geometry::transform_mesh(*mesh, center, math::Quaternion::identity(), float3(1));
sub_geometry.replace_mesh(mesh);
sub_geometry.keep_only_during_modify({GeometryComponent::Type::Mesh});
sub_geometry.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
}
});

View File

@@ -10,6 +10,7 @@
#include "BKE_material.hh"
#include "BKE_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -257,7 +258,7 @@ static void node_geo_exec(GeoNodeExecParams params)
#ifdef WITH_BULLET
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
Mesh *mesh = compute_hull(geometry_set);
if (mesh) {
geometry::debug_randomize_mesh_order(mesh);
@@ -266,7 +267,9 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_grease_pencil()) {
convex_hull_grease_pencil(geometry_set);
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
geometry_set.keep_only({GeometryComponent::Type::Mesh,
GeometryComponent::Type::Instance,
GeometryComponent::Type::Edit});
});
params.set_output("Convex Hull", std::move(geometry_set));

View File

@@ -14,6 +14,8 @@
#include "BLI_task.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_fill_cc {
@@ -310,8 +312,9 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<int> group_index = params.extract_input<Field<int>>("Group ID");
const GeometryNodeCurveFillMode mode = params.extract_input<GeometryNodeCurveFillMode>("Mode");
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode, group_index); });
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
curve_fill_calculate(geometry, mode, group_index);
});
params.set_output("Mesh", std::move(geometry_set));
}

View File

@@ -7,6 +7,7 @@
#include "BKE_grease_pencil.hh"
#include "GEO_fillet_curves.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
@@ -142,7 +143,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Curve");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (geometry_set.has_curves()) {
const Curves &curves_id = *geometry_set.get_curves();
const bke::CurvesGeometry &src_curves = curves_id.geometry.wrap();

View File

@@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_foreach_geometry.hh"
#include "GEO_resample_curves.hh"
#include "BKE_curves.hh"
@@ -91,7 +92,7 @@ static void node_geo_exec(GeoNodeExecParams params)
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
@@ -123,7 +124,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
@@ -153,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_reverse_cc {
@@ -57,7 +59,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context{*curves_id, AttrDomain::Curve};

View File

@@ -9,6 +9,8 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_set_handle_type_cc {
@@ -101,7 +103,7 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_curves = false;
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
has_curves = true;

View File

@@ -9,6 +9,7 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_set_curve_type.hh"
#include "RNA_enum_types.hh"
@@ -52,7 +53,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (!geometry_set.has_curves()) {
return;
}

View File

@@ -5,6 +5,7 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_subdivide_curves.hh"
#include "node_geometry_util.hh"
@@ -88,7 +89,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Curve");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (geometry_set.has_curves()) {
const Curves &src_curves_id = *geometry_set.get_curves();
Curves *dst_curves_id = subdivide_curves(src_curves_id, cuts_field, attribute_filter);

View File

@@ -8,6 +8,7 @@
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_join_geometries.hh"
#include "GEO_randomize.hh"
@@ -126,7 +127,7 @@ static void node_geo_exec(GeoNodeExecParams params)
bke::GeometryComponentEditData::remember_deformed_positions_if_necessary(curve_set);
const AttributeFilter &attribute_filter = params.get_attribute_filter("Mesh");
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(curve_set, [&](GeometrySet &geometry_set) {
if (geometry_set.has_curves()) {
const Curves &curves = *geometry_set.get_curves();
@@ -142,7 +143,9 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_grease_pencil()) {
grease_pencil_to_mesh(geometry_set, profile_set, scale_field, fill_caps, attribute_filter);
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
geometry_set.keep_only({GeometryComponent::Type::Mesh,
GeometryComponent::Type::Instance,
GeometryComponent::Type::Edit});
});
params.set_output("Mesh", std::move(curve_set));

View File

@@ -11,6 +11,7 @@
#include "BKE_instances.hh"
#include "BKE_pointcloud.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_join_geometries.hh"
#include "GEO_resample_curves.hh"
@@ -215,7 +216,7 @@ static void node_geo_exec(GeoNodeExecParams params)
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
const Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
bke::CurvesGeometry dst_curves = geometry::resample_to_count(
src_curves_id->geometry.wrap(),
@@ -246,13 +247,15 @@ static void node_geo_exec(GeoNodeExecParams params)
}
layer_pointclouds_to_instances(pointcloud_by_layer, attribute_filter, geometry);
}
geometry.keep_only_during_modify({bke::GeometryComponent::Type::PointCloud});
geometry.keep_only({bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Edit});
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
const Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
bke::CurvesGeometry dst_curves = geometry::resample_to_length(
src_curves_id->geometry.wrap(),
@@ -283,12 +286,14 @@ static void node_geo_exec(GeoNodeExecParams params)
}
layer_pointclouds_to_instances(pointcloud_by_layer, attribute_filter, geometry);
}
geometry.keep_only_during_modify({bke::GeometryComponent::Type::PointCloud});
geometry.keep_only({bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Edit});
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED: {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (const Curves *src_curves_id = geometry.get_curves()) {
bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
src_curves_id->geometry.wrap(),
@@ -318,7 +323,9 @@ static void node_geo_exec(GeoNodeExecParams params)
}
layer_pointclouds_to_instances(pointcloud_by_layer, attribute_filter, geometry);
}
geometry.keep_only_during_modify({bke::GeometryComponent::Type::PointCloud});
geometry.keep_only({bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Instance,
bke::GeometryComponent::Type::Edit});
});
break;
}

View File

@@ -10,6 +10,7 @@
#include "NOD_socket_search_link.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_trim_curves.hh"
#include "NOD_rna_define.hh"
@@ -219,7 +220,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
Field<float> end_field = params.extract_input<Field<float>>("End");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
geometry_set_curve_trim(
geometry_set, mode, selection_field, start_field, end_field, attribute_filter);
});
@@ -227,7 +228,7 @@ static void node_geo_exec(GeoNodeExecParams params)
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
Field<float> start_field = params.extract_input<Field<float>>("Start_001");
Field<float> end_field = params.extract_input<Field<float>>("End_001");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
geometry_set_curve_trim(
geometry_set, mode, selection_field, start_field, end_field, attribute_filter);
});

View File

@@ -7,6 +7,7 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_separate_geometry.hh"
#include "RNA_enum_types.hh"
@@ -74,7 +75,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry::separate_geometry(geometry_set, domain, mode, selection, attribute_filter, is_error);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
bool is_error;
/* Invert here because we want to keep the things not in the selection. */
geometry::separate_geometry(

View File

@@ -17,6 +17,7 @@
#include "BKE_volume.hh"
#include "BKE_volume_grid.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -195,9 +196,9 @@ static void node_geo_exec(GeoNodeExecParams params)
threshold = params.extract_input<float>("Threshold");
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (!geometry_set.has_volume()) {
geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud});
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
const VolumeComponent *component = geometry_set.get_component<VolumeComponent>();
@@ -241,7 +242,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry::debug_randomize_point_order(pointcloud);
geometry_set.replace_pointcloud(pointcloud);
geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud});
geometry_set.keep_only({GeometryComponent::Type::PointCloud, GeometryComponent::Type::Edit});
});
params.set_output("Points", std::move(geometry_set));

View File

@@ -20,6 +20,7 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -594,12 +595,12 @@ static void node_geo_exec(GeoNodeExecParams params)
lazy_threading::send_hint();
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
point_distribution_calculate(
geometry_set, selection_field, method, seed, attribute_outputs, params);
/* Keep instances because the original geometry set may contain instances that are processed as
* well. */
geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud});
geometry_set.keep_only({GeometryComponent::Type::PointCloud, GeometryComponent::Type::Edit});
});
params.set_output("Points", std::move(geometry_set));

View File

@@ -7,6 +7,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -920,7 +921,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
const bool keep_boundaries = params.extract_input<bool>("Keep Boundaries");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (const Mesh *mesh = geometry_set.get_mesh()) {
Mesh *new_mesh = calc_dual_mesh(
*mesh, keep_boundaries, params.get_attribute_filter("Dual Mesh"));

View File

@@ -21,6 +21,8 @@
#include "NOD_rna_define.hh"
#include "GEO_foreach_geometry.hh"
#include "FN_multi_function_builder.hh"
#include "UI_interface_layout.hh"
@@ -317,8 +319,9 @@ static void duplicate_curves(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs,
const AttributeFilter &attribute_filter)
{
geometry_set.keep_only_during_modify(
{GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
geometry_set.keep_only({GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil,
GeometryComponent::Type::Edit});
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
if (const Curves *curves_id = geometry_set.get_curves()) {
const bke::CurvesFieldContext field_context{*curves_id, AttrDomain::Curve};
@@ -463,10 +466,10 @@ static void duplicate_faces(GeometrySet &geometry_set,
const AttributeFilter &attribute_filter)
{
if (!geometry_set.has_mesh()) {
geometry_set.remove_geometry_during_modify();
geometry_set.clear();
return;
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
geometry_set.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
const Mesh &mesh = *geometry_set.get_mesh();
const OffsetIndices faces = mesh.faces();
@@ -649,7 +652,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
const AttributeFilter &attribute_filter)
{
if (!geometry_set.has_mesh()) {
geometry_set.remove_geometry_during_modify();
geometry_set.clear();
return;
};
const Mesh &mesh = *geometry_set.get_mesh();
@@ -994,8 +997,8 @@ static void duplicate_points(GeometrySet &geometry_set,
break;
}
}
component_types.append(GeometryComponent::Type::Instance);
geometry_set.keep_only_during_modify(component_types);
component_types.append(GeometryComponent::Type::Edit);
geometry_set.keep_only(component_types);
}
/** \} */
@@ -1015,7 +1018,7 @@ static void duplicate_layers(GeometrySet &geometry_set,
geometry_set.clear();
return;
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::GreasePencil});
geometry_set.keep_only({GeometryComponent::Type::GreasePencil, GeometryComponent::Type::Edit});
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
const GreasePencil &src_grease_pencil = *geometry_set.get_grease_pencil();
@@ -1179,7 +1182,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set, count_field, selection_field, attribute_outputs, attribute_filter);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
switch (duplicate_domain) {
case AttrDomain::Curve:
duplicate_curves(

View File

@@ -6,6 +6,7 @@
#include "DNA_mesh_types.h"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_to_curve.hh"
#include "node_geometry_util.hh"
@@ -72,10 +73,10 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
const Mesh *mesh = geometry_set.get_mesh();
if (mesh == nullptr) {
geometry_set.keep_only({GeometryComponent::Type::Instance});
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
@@ -88,13 +89,13 @@ static void node_geo_exec(GeoNodeExecParams params)
IndexMask start_verts = evaluator.get_evaluated_as_mask(1);
if (start_verts.is_empty()) {
geometry_set.keep_only({GeometryComponent::Type::Instance});
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
geometry_set.replace_curves(edge_paths_to_curves_convert(
*mesh, start_verts, next_vert, params.get_attribute_filter("Curves")));
geometry_set.keep_only({GeometryComponent::Type::Curve, GeometryComponent::Type::Instance});
geometry_set.keep_only({GeometryComponent::Type::Curve, GeometryComponent::Type::Edit});
});
params.set_output("Curves", std::move(geometry_set));

View File

@@ -4,6 +4,7 @@
#include "DNA_mesh_types.h"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_split_edges.hh"
#include "node_geometry_util.hh"
@@ -27,7 +28,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (const Mesh *mesh = geometry_set.get_mesh()) {
const bke::MeshFieldContext field_context{*mesh, AttrDomain::Edge};
fn::FieldEvaluator selection_evaluator{field_context, mesh->edges_num};

View File

@@ -17,6 +17,7 @@
#include "BKE_mesh_mapping.hh"
#include "BKE_mesh_runtime.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_selection.hh"
#include "GEO_randomize.hh"
@@ -1476,7 +1477,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Mesh");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
switch (mode) {

View File

@@ -4,6 +4,8 @@
#include "BKE_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_flip_faces_cc {
@@ -25,7 +27,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
const bke::MeshFieldContext field_context(*mesh, AttrDomain::Face);
fn::FieldEvaluator evaluator(field_context, mesh->faces_num);

View File

@@ -11,6 +11,7 @@
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_join_geometries.hh"
#include "node_geometry_util.hh"
@@ -187,16 +188,8 @@ static void node_geo_exec(GeoNodeExecParams params)
instance.ensure_owns_direct_data();
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Instances");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
/* It's important not to invalidate the existing #InstancesComponent because it owns references
* to other geometry sets that are processed by this node. */
InstancesComponent &instances_component =
geometry_set.get_component_for_write<InstancesComponent>();
bke::Instances *dst_instances = instances_component.get_for_write();
if (dst_instances == nullptr) {
dst_instances = new bke::Instances();
instances_component.replace(dst_instances);
}
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
bke::Instances *dst_instances = new bke::Instances();
const Array<GeometryComponent::Type> types{GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
@@ -226,7 +219,7 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_grease_pencil()) {
using namespace bke::greasepencil;
const GreasePencil &grease_pencil = *geometry_set.get_grease_pencil();
bke::Instances *instances = new bke::Instances();
bke::Instances *instances_per_layer = new bke::Instances();
for (const int layer_index : grease_pencil.layers().index_range()) {
const Layer &layer = grease_pencil.layer(layer_index);
const Drawing *drawing = grease_pencil.get_eval_drawing(layer);
@@ -239,8 +232,8 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Add an empty reference so the number of layers and instances match.
* This makes it easy to reconstruct the layers afterwards and keep their attributes.
* Although in this particular case we don't propagate the attributes. */
const int handle = instances->add_reference(bke::InstanceReference());
instances->add_instance(handle, layer_transform);
const int handle = instances_per_layer->add_reference(bke::InstanceReference());
instances_per_layer->add_instance(handle, layer_transform);
continue;
}
/* TODO: Attributes are not propagating from the curves or the points. */
@@ -254,25 +247,23 @@ static void node_geo_exec(GeoNodeExecParams params)
params,
attributes_to_propagate);
GeometrySet temp_set = GeometrySet::from_instances(layer_instances);
const int handle = instances->add_reference(bke::InstanceReference{temp_set});
instances->add_instance(handle, layer_transform);
const int handle = instances_per_layer->add_reference(bke::InstanceReference{temp_set});
instances_per_layer->add_instance(handle, layer_transform);
}
bke::copy_attributes(geometry_set.get_grease_pencil()->attributes(),
bke::AttrDomain::Layer,
bke::AttrDomain::Instance,
attribute_filter,
instances->attributes_for_write());
instances_per_layer->attributes_for_write());
GeometrySet new_instances = geometry::join_geometries(
{GeometrySet::from_instances(dst_instances, bke::GeometryOwnershipType::Editable),
GeometrySet::from_instances(instances)},
{GeometrySet::from_instances(dst_instances),
GeometrySet::from_instances(instances_per_layer)},
attribute_filter);
instances_component.replace(
new_instances.get_component_for_write<InstancesComponent>().release());
geometry_set.replace_grease_pencil(nullptr);
dst_instances = new_instances.get_component_for_write<InstancesComponent>().release();
}
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
geometry_set.replace_instances(dst_instances);
});
/* Unused references may have been added above. Remove those now so that other nodes don't

View File

@@ -6,6 +6,8 @@
#include "DNA_mesh_types.h"
#include "GEO_foreach_geometry.hh"
#include "BKE_grease_pencil.hh"
namespace blender::nodes::node_geo_material_replace_cc {
@@ -40,7 +42,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
replace_materials({mesh->mat, mesh->totcol}, old_material, new_material);
}

View File

@@ -5,6 +5,7 @@
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_merge_by_distance.hh"
#include "GEO_point_merge_by_distance.hh"
@@ -103,7 +104,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const float merge_distance = params.extract_input<float>("Distance");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (const PointCloud *pointcloud = geometry_set.get_pointcloud()) {
PointCloud *result = pointcloud_merge_by_distance(
*pointcloud, merge_distance, selection, params.get_attribute_filter("Geometry"));

View File

@@ -4,6 +4,7 @@
#include "node_geometry_util.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_merge_layers.hh"
#include "BKE_grease_pencil.hh"
@@ -170,8 +171,9 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeAttributeFilter attribute_filter = params.get_attribute_filter("Grease Pencil");
main_geometry.modify_geometry_sets(
[&](GeometrySet &geometry) { merge_layers(geometry, storage, params, attribute_filter); });
geometry::foreach_real_geometry(main_geometry, [&](GeometrySet &geometry) {
merge_layers(geometry, storage, params, attribute_filter);
});
params.set_output("Grease Pencil", std::move(main_geometry));
}

View File

@@ -5,6 +5,7 @@
#include "BKE_subdiv.hh"
#include "BKE_subdiv_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -72,7 +73,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (const Mesh *mesh = geometry_set.get_mesh()) {
geometry_set.replace_mesh(simple_subdivide_mesh(*mesh, level));
}

View File

@@ -6,6 +6,7 @@
#include "NOD_rna_define.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_to_curve.hh"
#include "UI_interface_c.hh"
@@ -39,10 +40,10 @@ static void node_geo_exec(GeoNodeExecParams params)
const Mode mode = Mode(params.node().custom1);
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
const Mesh *mesh = geometry_set.get_mesh();
if (mesh == nullptr) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
@@ -54,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
if (selection.is_empty()) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
@@ -70,7 +71,7 @@ static void node_geo_exec(GeoNodeExecParams params)
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
if (selection.is_empty()) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
@@ -80,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::Curve});
geometry_set.keep_only({GeometryComponent::Type::Curve, GeometryComponent::Type::Edit});
});
params.set_output("Curve", std::move(geometry_set));

View File

@@ -16,6 +16,8 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "FN_multi_function_builder.hh"
#include "node_geometry_util.hh"
@@ -60,12 +62,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
{
const Mesh *mesh = geometry_set.get_mesh();
if (mesh == nullptr) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
const int domain_size = mesh->attributes().domain_size(domain);
if (domain_size == 0) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
const AttributeAccessor src_attributes = mesh->attributes();
@@ -138,7 +140,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
}
geometry_set.replace_pointcloud(pointcloud);
geometry_set.keep_only_during_modify({GeometryComponent::Type::PointCloud});
geometry_set.keep_only({GeometryComponent::Type::PointCloud, GeometryComponent::Type::Edit});
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -161,7 +163,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Points");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
switch (mode) {
case GEO_NODE_MESH_TO_POINTS_VERTICES:
geometry_set_mesh_to_points(geometry_set,

View File

@@ -9,6 +9,7 @@
#include "BKE_lib_id.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_to_volume.hh"
#include "NOD_rna_define.hh"
@@ -127,11 +128,11 @@ static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh(), params);
geometry_set.replace_volume(volume);
geometry_set.keep_only_during_modify({GeometryComponent::Type::Volume});
geometry_set.keep_only({GeometryComponent::Type::Volume, GeometryComponent::Type::Edit});
}
});
params.set_output("Volume", std::move(geometry_set));

View File

@@ -14,6 +14,7 @@
#include "BLI_sort.hh"
#include "BLI_task.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "BKE_geometry_set.hh"
@@ -174,14 +175,14 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float> weight_field = params.extract_input<Field<float>>("Weight");
const NodeAttributeFilter attribute_filter = params.get_attribute_filter("Curves");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
geometry_set.replace_curves(nullptr);
if (const PointCloud *points = geometry_set.get_pointcloud()) {
Curves *curves_id = curves_from_points(
*points, group_id_field, weight_field, attribute_filter);
geometry_set.replace_curves(curves_id);
}
geometry_set.keep_only_during_modify({GeometryComponent::Type::Curve});
geometry_set.keep_only({GeometryComponent::Type::Curve, GeometryComponent::Type::Edit});
});
params.set_output("Curves", std::move(geometry_set));

View File

@@ -9,6 +9,8 @@
#include "BKE_customdata.hh"
#include "BKE_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_points_to_vertices_cc {
@@ -29,11 +31,11 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
{
const PointCloud *points = geometry_set.get_pointcloud();
if (points == nullptr) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
if (points->totpoint == 0) {
geometry_set.remove_geometry_during_modify();
geometry_set.keep_only({GeometryComponent::Type::Edit});
return;
}
@@ -85,7 +87,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
mesh->tag_overlapping_none();
geometry_set.replace_mesh(mesh);
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
geometry_set.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -93,7 +95,7 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
geometry_set_points_to_vertices(
geometry_set, selection_field, params.get_attribute_filter("Mesh"));
});

View File

@@ -12,6 +12,7 @@
#include "node_geometry_util.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_points_to_volume.hh"
#include "BKE_lib_id.hh"
@@ -115,7 +116,7 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
blender::geometry::fog_volume_grid_add_from_points(
volume, "density", positions, radii, voxel_size, density);
r_geometry_set.keep_only_during_modify({GeometryComponent::Type::Volume});
r_geometry_set.keep_only({GeometryComponent::Type::Volume, GeometryComponent::Type::Edit});
r_geometry_set.replace_volume(volume);
}
@@ -172,7 +173,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
initialize_volume_component_from_points(params, geometry_set);
});
params.set_output("Volume", std::move(geometry_set));

View File

@@ -2,6 +2,7 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_instances.hh"
#include "node_geometry_util.hh"
#include <fmt/format.h>
@@ -36,6 +37,82 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::String>("Name").is_attribute_name().hide_label();
}
struct RemoveAttributeParams {
PatternMode pattern_mode;
std::string pattern;
std::string wildcard_prefix;
std::string wildcard_suffix;
Set<std::string> removed_attributes;
Set<std::string> failed_attributes;
};
static void remove_attributes_recursive(GeometrySet &geometry_set, RemoveAttributeParams &params)
{
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve,
GeometryComponent::Type::Instance,
GeometryComponent::Type::GreasePencil})
{
if (!geometry_set.has(type)) {
continue;
}
/* First check if the attribute exists before getting write access,
* to avoid potentially expensive unnecessary copies. */
const GeometryComponent &read_only_component = *geometry_set.get_component(type);
Vector<std::string> attributes_to_remove;
switch (params.pattern_mode) {
case PatternMode::Exact: {
if (read_only_component.attributes()->contains(params.pattern)) {
attributes_to_remove.append(params.pattern);
}
break;
}
case PatternMode::Wildcard: {
read_only_component.attributes()->foreach_attribute([&](const bke::AttributeIter &iter) {
const StringRef attribute_name = iter.name;
if (bke::attribute_name_is_anonymous(attribute_name)) {
return;
}
if (attribute_name.startswith(params.wildcard_prefix) &&
attribute_name.endswith(params.wildcard_suffix))
{
attributes_to_remove.append(attribute_name);
}
});
break;
}
}
if (attributes_to_remove.is_empty()) {
break;
}
GeometryComponent &component = geometry_set.get_component_for_write(type);
for (const StringRef attribute_name : attributes_to_remove) {
if (!bke::allow_procedural_attribute_access(attribute_name)) {
continue;
}
if (component.attributes_for_write()->remove(attribute_name)) {
params.removed_attributes.add(attribute_name);
}
else {
params.failed_attributes.add(attribute_name);
}
}
}
if (bke::Instances *instances = geometry_set.get_instances_for_write()) {
instances->ensure_geometry_instances();
for (bke::InstanceReference &reference : instances->references_for_write()) {
if (reference.type() == bke::InstanceReference::Type::GeometrySet) {
remove_attributes_recursive(reference.geometry_set(), params);
}
}
}
}
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -44,6 +121,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
return;
}
PatternMode pattern_mode = params.get_input<PatternMode>("Pattern Mode");
if (pattern_mode == PatternMode::Wildcard) {
const int wildcard_count = Span(pattern.c_str(), pattern.size()).count('*');
@@ -58,83 +136,24 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
StringRef wildcard_prefix;
StringRef wildcard_suffix;
RemoveAttributeParams removal_params;
removal_params.pattern_mode = pattern_mode;
removal_params.pattern = pattern;
if (pattern_mode == PatternMode::Wildcard) {
const int wildcard_index = pattern.find('*');
wildcard_prefix = StringRef(pattern).substr(0, wildcard_index);
wildcard_suffix = StringRef(pattern).substr(wildcard_index + 1);
removal_params.wildcard_prefix = StringRef(pattern).substr(0, wildcard_index);
removal_params.wildcard_suffix = StringRef(pattern).substr(wildcard_index + 1);
}
Mutex attribute_log_mutex;
Set<std::string> removed_attributes;
Set<std::string> failed_attributes;
remove_attributes_recursive(geometry_set, removal_params);
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve,
GeometryComponent::Type::Instance,
GeometryComponent::Type::GreasePencil})
{
if (!geometry_set.has(type)) {
continue;
}
/* First check if the attribute exists before getting write access,
* to avoid potentially expensive unnecessary copies. */
const GeometryComponent &read_only_component = *geometry_set.get_component(type);
Vector<std::string> attributes_to_remove;
switch (pattern_mode) {
case PatternMode::Exact: {
if (read_only_component.attributes()->contains(pattern)) {
attributes_to_remove.append(pattern);
}
break;
}
case PatternMode::Wildcard: {
read_only_component.attributes()->foreach_attribute([&](const bke::AttributeIter &iter) {
const StringRef attribute_name = iter.name;
if (bke::attribute_name_is_anonymous(attribute_name)) {
return;
}
if (attribute_name.startswith(wildcard_prefix) &&
attribute_name.endswith(wildcard_suffix))
{
attributes_to_remove.append(attribute_name);
}
});
break;
}
}
if (attributes_to_remove.is_empty()) {
break;
}
GeometryComponent &component = geometry_set.get_component_for_write(type);
for (const StringRef attribute_name : attributes_to_remove) {
if (!bke::allow_procedural_attribute_access(attribute_name)) {
continue;
}
if (component.attributes_for_write()->remove(attribute_name)) {
std::lock_guard lock{attribute_log_mutex};
removed_attributes.add(attribute_name);
}
else {
std::lock_guard lock{attribute_log_mutex};
failed_attributes.add(attribute_name);
}
}
}
});
for (const StringRef attribute_name : removed_attributes) {
for (const StringRef attribute_name : removal_params.removed_attributes) {
params.used_named_attribute(attribute_name, NamedAttributeUsage::Remove);
}
if (!failed_attributes.is_empty()) {
if (!removal_params.failed_attributes.is_empty()) {
Vector<std::string> quoted_attribute_names;
for (const StringRef attribute_name : failed_attributes) {
for (const StringRef attribute_name : removal_params.failed_attributes) {
quoted_attribute_names.append(fmt::format("\"{}\"", attribute_name));
}
const std::string message = fmt::format(
@@ -142,7 +161,7 @@ static void node_geo_exec(GeoNodeExecParams params)
fmt::join(quoted_attribute_names, ", "));
params.error_message_add(NodeWarningType::Warning, message);
}
else if (removed_attributes.is_empty() && pattern_mode == PatternMode::Exact) {
else if (removal_params.removed_attributes.is_empty() && pattern_mode == PatternMode::Exact) {
const std::string message = fmt::format(fmt::runtime(TIP_("Attribute does not exist: \"{}\"")),
pattern);
params.error_message_add(NodeWarningType::Warning, message);

View File

@@ -17,6 +17,7 @@
#include "UI_interface_layout.hh"
#include "UI_resources.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_selection.hh"
#include "NOD_rna_define.hh"
@@ -474,7 +475,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float> scale_field = params.extract_input<Field<float>>("Scale");
const Field<float3> center_field = params.extract_input<Field<float3>>("Center");
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
const bke::MeshFieldContext context{*mesh, domain};
FieldEvaluator evaluator{context, mesh->attributes().domain_size(domain)};

View File

@@ -9,6 +9,7 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_separate_geometry.hh"
#include "node_geometry_util.hh"
@@ -68,7 +69,7 @@ static void node_geo_exec(GeoNodeExecParams params)
is_error);
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
geometry::separate_geometry(geometry_set,
domain,
GEO_NODE_DELETE_GEOMETRY_MODE_ALL,

View File

@@ -15,6 +15,8 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_handles_cc {
@@ -176,7 +178,7 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_curves = false;
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
has_curves = true;

View File

@@ -7,6 +7,8 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_normal_cc {
@@ -90,7 +92,7 @@ static void node_geo_exec(GeoNodeExecParams params)
custom_normal = params.extract_input<Field<float3>>("Normal");
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
set_curve_normal(curves,

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_radius_cc {
@@ -44,7 +46,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<float> radius = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context(*curves_id, AttrDomain::Point);

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_tilt_cc {
@@ -58,7 +60,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<float> tilt = params.extract_input<Field<float>>("Tilt");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context(*curves_id, AttrDomain::Point);

View File

@@ -10,6 +10,8 @@
#include "NOD_rna_define.hh"
#include "GEO_foreach_geometry.hh"
#include "RNA_enum_types.hh"
#include "node_geometry_util.hh"
@@ -62,7 +64,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const StringRef color_attr_name = domain == AttrDomain::Point ? "vertex_color" : "fill_color";
const StringRef opacity_attr_name = domain == AttrDomain::Point ? "opacity" : "fill_opacity";
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
using namespace bke::greasepencil;
for (const int layer_index : grease_pencil->layers().index_range()) {

View File

@@ -11,6 +11,8 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_grease_pencil_set_depth_mode {
@@ -40,7 +42,7 @@ static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Grease Pencil");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
SET_FLAG_FROM_TEST(grease_pencil->flag,
params.node().custom1 == GREASE_PENCIL_STROKE_ORDER_3D,

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_grease_pencil_softness_cc {
@@ -28,7 +30,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<float> softness = params.extract_input<Field<float>>("Softness");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry) {
if (GreasePencil *grease_pencil = geometry.get_grease_pencil_for_write()) {
using namespace bke::greasepencil;
for (const int layer_index : grease_pencil->layers().index_range()) {

View File

@@ -13,6 +13,8 @@
#include "BKE_grease_pencil.hh"
#include "BKE_material.hh"
#include "GEO_foreach_geometry.hh"
namespace blender::nodes::node_geo_set_material_cc {
static void node_declare(NodeDeclarationBuilder &b)
@@ -83,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params)
bool volume_selection_warning = false;
bool curves_selection_warning = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
if (mesh->faces_num == 0) {
if (mesh->verts_num > 0) {

View File

@@ -10,6 +10,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
namespace blender::nodes::node_geo_set_material_index_cc {
static void node_declare(NodeDeclarationBuilder &b)
@@ -50,7 +52,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<int> material_index = params.extract_input<Field<int>>("Material Index");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
bke::try_capture_field_on_geometry(mesh->attributes_for_write(),
bke::MeshFieldContext(*mesh, AttrDomain::Face),

View File

@@ -9,6 +9,8 @@
#include "NOD_rna_define.hh"
#include "GEO_foreach_geometry.hh"
#include "RNA_enum_types.hh"
#include "node_geometry_util.hh"
@@ -76,7 +78,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool remove_custom = params.extract_input<bool>("Remove Custom");
const fn::Field sharp_edge = params.extract_input<fn::Field<bool>>("Edge Sharpness");
const fn::Field sharp_face = params.extract_input<fn::Field<bool>>("Face Sharpness");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
/* Evaluate both fields before storing the result to avoid one attribute change
* potentially affecting the other field evaluation. */
@@ -129,7 +131,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
case Mode::Free: {
const fn::Field custom_normal = params.extract_input<fn::Field<float3>>("Custom Normal");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
const bke::AttrDomain domain = bke::AttrDomain(node.custom2);
bke::try_capture_field_on_geometry(mesh->attributes_for_write(),
@@ -144,7 +146,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
case Mode::CornerFanSpace: {
const fn::Field custom_normal = params.extract_input<fn::Field<float3>>("Custom Normal");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
const bke::MeshFieldContext context(*mesh, bke::AttrDomain::Corner);
fn::FieldEvaluator evaluator(context, mesh->corners_num);

View File

@@ -4,6 +4,8 @@
#include "DNA_pointcloud_types.h"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_point_radius_cc {
@@ -30,7 +32,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<float> radius = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
bke::try_capture_field_on_geometry(pointcloud->attributes_for_write(),
bke::PointCloudFieldContext(*pointcloud),

View File

@@ -11,6 +11,8 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_shade_smooth_cc {
@@ -89,7 +91,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<bool> smooth_field = params.extract_input<Field<bool>>("Shade Smooth");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
set_sharp(*mesh,
domain,

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_cyclic_cc {
@@ -58,7 +60,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<bool> cyclic = params.extract_input<Field<bool>>("Cyclic");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context{*curves_id, AttrDomain::Curve};

View File

@@ -5,6 +5,8 @@
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_resolution_cc {
@@ -59,7 +61,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
const Field<int> resolution = params.extract_input<Field<int>>("Resolution");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (Curves *curves_id = geometry_set.get_curves_for_write()) {
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const bke::CurvesFieldContext field_context(*curves_id, AttrDomain::Curve);

View File

@@ -12,6 +12,7 @@
#include "BLI_sort.hh"
#include "BLI_task.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_reorder.hh"
#include "NOD_rna_define.hh"
@@ -224,7 +225,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
for (const auto [type, domains] : geometry::components_supported_reordering().items()) {
const bke::GeometryComponent *src_component = geometry_set.get_component(type);
if (src_component == nullptr || src_component->is_empty()) {

View File

@@ -18,6 +18,8 @@
#include "NOD_rna_define.hh"
#include "NOD_socket_search_link.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
#include <fmt/format.h>
@@ -145,7 +147,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
else {
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
for (const GeometryComponent::Type type : {GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Curve,

View File

@@ -12,6 +12,7 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
#include "FN_multi_function_builder.hh"
@@ -194,7 +195,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
if (const Mesh *mesh = geometry_set.get_mesh()) {
geometry_set.replace_mesh(mesh_subsurf_calc(
mesh, level, vert_crease, edge_crease, boundary_smooth, uv_smooth, use_limit_surface));

View File

@@ -4,6 +4,8 @@
#include "DNA_mesh_types.h"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_tool_set_face_set_cc {
@@ -37,7 +39,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool is_zero = is_constant_zero(face_set);
GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
if (is_zero) {
mesh->attributes_for_write().remove(".sculpt_face_set");

View File

@@ -12,6 +12,8 @@
#include "RNA_enum_types.hh"
#include "GEO_foreach_geometry.hh"
#include "FN_multi_function_builder.hh"
#include "node_geometry_util.hh"
@@ -95,7 +97,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GField selection = params.extract_input<GField>("Selection");
const AttrDomain domain = AttrDomain(params.node().custom1);
const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
if (Mesh *mesh = geometry.get_mesh_for_write()) {
switch (mode) {
case OB_MODE_EDIT: {

View File

@@ -4,8 +4,8 @@
#include "DNA_mesh_types.h"
#include "GEO_foreach_geometry.hh"
#include "GEO_mesh_triangulate.hh"
#include "GEO_randomize.hh"
#include "node_geometry_util.hh"
@@ -85,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const auto ngon_method = params.extract_input<geometry::TriangulateNGonMode>("N-gon Method");
const auto quad_method = params.extract_input<geometry::TriangulateQuadMode>("Quad Method");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
const Mesh *src_mesh = geometry_set.get_mesh();
if (!src_mesh) {
return;

View File

@@ -15,6 +15,7 @@
#include "BKE_volume_grid.hh"
#include "BKE_volume_to_mesh.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_randomize.hh"
namespace blender::nodes::node_geo_volume_to_mesh_cc {
@@ -191,10 +192,10 @@ static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
geometry::foreach_real_geometry(geometry_set, [&](GeometrySet &geometry_set) {
Mesh *mesh = create_mesh_from_volume(geometry_set, params);
geometry_set.replace_mesh(mesh);
geometry_set.keep_only_during_modify({GeometryComponent::Type::Mesh});
geometry_set.keep_only({GeometryComponent::Type::Mesh, GeometryComponent::Type::Edit});
});
params.set_output("Mesh", std::move(geometry_set));
#else

View File

@@ -16,6 +16,8 @@
#include "NOD_node_declaration.hh"
#include "NOD_socket.hh"
#include "GEO_foreach_geometry.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_nodes_reference_set.hh"
#include "BKE_geometry_set.hh"
@@ -738,20 +740,13 @@ static MultiValueMap<bke::AttrDomain, OutputAttributeInfo> find_output_attribute
static Vector<OutputAttributeToStore> compute_attributes_to_store(
const bke::GeometrySet &geometry,
const MultiValueMap<bke::AttrDomain, OutputAttributeInfo> &outputs_by_domain,
const bool do_instances)
const Span<const bke::GeometryComponent::Type> component_types)
{
Vector<OutputAttributeToStore> attributes_to_store;
for (const auto component_type : {bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Curve,
bke::GeometryComponent::Type::Instance})
{
for (const auto component_type : component_types) {
if (!geometry.has(component_type)) {
continue;
}
if (!do_instances && component_type == bke::GeometryComponent::Type::Instance) {
continue;
}
const bke::GeometryComponent &component = *geometry.get_component(component_type);
const bke::AttributeAccessor attributes = *component.attributes();
for (const auto item : outputs_by_domain.items()) {
@@ -838,24 +833,32 @@ static void store_output_attributes(bke::GeometrySet &geometry,
if (outputs_by_domain.size() == 0) {
return;
}
{
/* Handle top level instances separately first. */
Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store(
geometry, outputs_by_domain, {bke::GeometryComponent::Type::Instance});
store_computed_output_attributes(geometry, attributes_to_store);
}
const bool only_instance_attributes = outputs_by_domain.size() == 1 &&
*outputs_by_domain.keys().begin() ==
bke::AttrDomain::Instance;
if (only_instance_attributes) {
/* No need to call #modify_geometry_sets when only adding attributes to top-level instances.
/* No need to call #foreach_real_geometry when only adding attributes to top-level instances.
* This avoids some unnecessary data copies currently if some sub-geometries are not yet owned
* by the geometry set, i.e. they use #GeometryOwnershipType::Editable/ReadOnly. */
Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store(
geometry, outputs_by_domain, true);
store_computed_output_attributes(geometry, attributes_to_store);
return;
}
geometry.modify_geometry_sets([&](bke::GeometrySet &instance_geometry) {
geometry::foreach_real_geometry(geometry, [&](bke::GeometrySet &instance_geometry) {
/* Instance attributes should only be created for the top-level geometry. */
const bool do_instances = &geometry == &instance_geometry;
Vector<OutputAttributeToStore> attributes_to_store = compute_attributes_to_store(
instance_geometry, outputs_by_domain, do_instances);
instance_geometry,
outputs_by_domain,
{bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Curve});
store_computed_output_attributes(instance_geometry, attributes_to_store);
});
}

View File

@@ -13,6 +13,7 @@
#include "BKE_node_socket_value.hh"
#include "GEO_extract_elements.hh"
#include "GEO_foreach_geometry.hh"
#include "GEO_join_geometries.hh"
#include "FN_lazy_function_graph_executor.hh"
@@ -1137,7 +1138,7 @@ void LazyFunctionForReduceForeachGeometryElement::handle_generation_items_group(
}
}
else {
geometry.modify_geometry_sets([&](GeometrySet &sub_geometry) {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &sub_geometry) {
for (const GeometryComponent::Type component_type :
{GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,

View File

@@ -54,6 +54,8 @@
#include "DEG_depsgraph_query.hh"
#include "GEO_foreach_geometry.hh"
#include "list_function_eval.hh"
#include "volume_grid_function_eval.hh"
@@ -909,7 +911,7 @@ class LazyFunctionForViewerNode : public LazyFunction {
}
}
else {
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
for (const bke::GeometryComponent::Type type :
{bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,

Binary file not shown.