diff options
26 files changed, 502 insertions, 28 deletions
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 02b3c88183a..5b429a61b3f 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -467,3 +467,25 @@ class VolumeComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GeometryComponentType::Volume; }; + +/** + * Used to keep track of a group of instances using the same geometry data. + */ +struct GeometryInstanceGroup { + /** + * The geometry set instanced on each of the transforms. The components are not necessarily + * owned here. For example, they may be owned by the instanced object. This cannot be a + * reference because not all instanced data will necessarily have a #geometry_set_eval. + */ + GeometrySet geometry_set; + + /** + * As an optimization to avoid copying, the same geometry set can be associated with multiple + * instances. Each instance is stored as a transform matrix here. Again, these must be owned + * because they may be transformed from the original data. TODO: Validate that last statement. + */ + blender::Vector<blender::float4x4> transforms; +}; + +blender::Vector<GeometryInstanceGroup> BKE_geometry_set_gather_instances( + const GeometrySet &geometry_set); diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 7eb41a6c623..0bc248299ca 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -14,13 +14,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_map.hh" + +#include "BKE_attribute.h" +#include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" +#include "BKE_modifier.h" #include "BKE_pointcloud.h" #include "BKE_volume.h" +#include "DNA_collection_types.h" #include "DNA_object_types.h" #include "BLI_rand.hh" @@ -29,6 +35,7 @@ using blender::float3; using blender::float4x4; +using blender::Map; using blender::MutableSpan; using blender::Span; using blender::StringRef; @@ -579,6 +586,149 @@ bool InstancesComponent::is_empty() const return transforms_.size() == 0; } +/** + * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. + */ +static GeometrySet object_get_geometry_set_for_read(const Object &object) +{ + /* Objects evaluated with a nodes modifier will have a geometry set already. */ + if (object.runtime.geometry_set_eval != nullptr) { + return *object.runtime.geometry_set_eval; + } + + /* Otherwise, construct a new geometry set with the component based on the object type. */ + GeometrySet new_geometry_set; + + if (object.type == OB_MESH) { + Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object( + &const_cast<Object &>(object), false); + + if (mesh != nullptr) { + BKE_mesh_wrapper_ensure_mdata(mesh); + + MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); + mesh_component.copy_vertex_group_names_from_object(object); + } + } + + /* TODO: Cover the case of pointclouds without modifiers-- they may not be covered by the + * #geometry_set_eval case above. */ + + /* TODO: Add volume support. */ + + /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ + return new_geometry_set; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Geometry Set Gather Recursive Instances + * \{ */ + +static void geometry_set_collect_recursive(const GeometrySet &geometry_set, + const float4x4 &transform, + Vector<GeometryInstanceGroup> &r_sets); + +static void geometry_set_collect_recursive_collection(const Collection &collection, + const float4x4 &transform, + Vector<GeometryInstanceGroup> &r_sets); + +static void geometry_set_collect_recursive_collection_instance( + const Collection &collection, const float4x4 &transform, Vector<GeometryInstanceGroup> &r_sets) +{ + float4x4 offset_matrix; + unit_m4(offset_matrix.values); + sub_v3_v3(offset_matrix.values[3], collection.instance_offset); + const float4x4 instance_transform = transform * offset_matrix; + geometry_set_collect_recursive_collection(collection, instance_transform, r_sets); +} + +static void geometry_set_collect_recursive_object(const Object &object, + const float4x4 &transform, + Vector<GeometryInstanceGroup> &r_sets) +{ + GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); + geometry_set_collect_recursive(instance_geometry_set, transform, r_sets); + + if (object.type == OB_EMPTY) { + const Collection *collection_instance = object.instance_collection; + if (collection_instance != nullptr) { + geometry_set_collect_recursive_collection_instance(*collection_instance, transform, r_sets); + } + } +} + +static void geometry_set_collect_recursive_collection(const Collection &collection, + const float4x4 &transform, + Vector<GeometryInstanceGroup> &r_sets) +{ + LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) { + BLI_assert(collection_object->ob != nullptr); + const Object &object = *collection_object->ob; + const float4x4 object_transform = transform * object.obmat; + geometry_set_collect_recursive_object(object, object_transform, r_sets); + } + LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) { + BLI_assert(collection_child->collection != nullptr); + const Collection &collection = *collection_child->collection; + geometry_set_collect_recursive_collection(collection, transform, r_sets); + } +} + +static void geometry_set_collect_recursive(const GeometrySet &geometry_set, + const float4x4 &transform, + Vector<GeometryInstanceGroup> &r_sets) +{ + r_sets.append({geometry_set, {transform}}); + + if (geometry_set.has_instances()) { + const InstancesComponent &instances_component = + *geometry_set.get_component_for_read<InstancesComponent>(); + + Span<float4x4> transforms = instances_component.transforms(); + Span<InstancedData> instances = instances_component.instanced_data(); + for (const int i : instances.index_range()) { + const InstancedData &data = instances[i]; + const float4x4 instance_transform = transform * transforms[i]; + + if (data.type == INSTANCE_DATA_TYPE_OBJECT) { + BLI_assert(data.data.object != nullptr); + const Object &object = *data.data.object; + geometry_set_collect_recursive_object(object, instance_transform, r_sets); + } + else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { + BLI_assert(data.data.collection != nullptr); + const Collection &collection = *data.data.collection; + geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets); + } + } + } +} + +/** + * Return flattened vector of the geometry component's recursive instances. I.e. all collection + * instances and object instances will be expanded into the instances of their geometry components. + * Even the instances in those geometry components' will be included. + * + * \note For convenience (to avoid duplication in the caller), the returned vector also contains + * the argument geometry set. + * + * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. + */ +Vector<GeometryInstanceGroup> BKE_geometry_set_gather_instances(const GeometrySet &geometry_set) +{ + Vector<GeometryInstanceGroup> result_vector; + + float4x4 unit_transform; + unit_m4(unit_transform.values); + + geometry_set_collect_recursive(geometry_set, unit_transform, result_vector); + + return result_vector; +} + static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids) { using namespace blender; diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index daaccf4450b..95c25795356 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -17,8 +17,257 @@ #include "node_geometry_util.hh" #include "node_util.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_pointcloud.h" + namespace blender::nodes { +void gather_attribute_info(Map<std::string, AttributeInfo> &attributes, + const GeometryComponentType component_type, + Span<GeometryInstanceGroup> set_groups, + const Set<std::string> &ignored_attributes) +{ + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (!set.has(component_type)) { + continue; + } + const GeometryComponent &component = *set.get_component_for_read(component_type); + + for (const std::string name : component.attribute_names()) { + if (ignored_attributes.contains(name)) { + continue; + } + const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name); + if (!read_attribute) { + continue; + } + const AttributeDomain domain = read_attribute->domain(); + const CustomDataType data_type = read_attribute->custom_data_type(); + + auto add_info = [&, data_type, domain](AttributeInfo *info) { + info->domain = domain; + info->data_type = data_type; + }; + auto modify_info = [&, data_type, domain](AttributeInfo *info) { + info->domain = domain; /* TODO: Use highest priority domain. */ + info->data_type = attribute_data_type_highest_complexity({info->data_type, data_type}); + }; + + attributes.add_or_modify(name, add_info, modify_info); + } + } +} + +static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups) +{ + int totverts = 0; + int totloops = 0; + int totedges = 0; + int totpolys = 0; + int64_t cd_dirty_vert = 0; + int64_t cd_dirty_poly = 0; + int64_t cd_dirty_edge = 0; + int64_t cd_dirty_loop = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + totverts += mesh.totvert * set_group.transforms.size(); + totloops += mesh.totloop * set_group.transforms.size(); + totedges += mesh.totedge * set_group.transforms.size(); + totpolys += mesh.totpoly * set_group.transforms.size(); + cd_dirty_vert |= mesh.runtime.cd_dirty_vert; + cd_dirty_poly |= mesh.runtime.cd_dirty_poly; + cd_dirty_edge |= mesh.runtime.cd_dirty_edge; + cd_dirty_loop |= mesh.runtime.cd_dirty_loop; + } + } + + Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); + /* Copy settings from the first input geometry set with a mesh. */ + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + BKE_mesh_copy_settings(new_mesh, &mesh); + break; + } + } + new_mesh->runtime.cd_dirty_vert = cd_dirty_vert; + new_mesh->runtime.cd_dirty_poly = cd_dirty_poly; + new_mesh->runtime.cd_dirty_edge = cd_dirty_edge; + new_mesh->runtime.cd_dirty_loop = cd_dirty_loop; + + int vert_offset = 0; + int loop_offset = 0; + int edge_offset = 0; + int poly_offset = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + for (const float4x4 &transform : set_group.transforms) { + for (const int i : IndexRange(mesh.totvert)) { + const MVert &old_vert = mesh.mvert[i]; + MVert &new_vert = new_mesh->mvert[vert_offset + i]; + + new_vert = old_vert; + + const float3 new_position = transform * float3(old_vert.co); + copy_v3_v3(new_vert.co, new_position); + } + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &old_edge = mesh.medge[i]; + MEdge &new_edge = new_mesh->medge[edge_offset + i]; + new_edge = old_edge; + new_edge.v1 += vert_offset; + new_edge.v2 += vert_offset; + } + for (const int i : IndexRange(mesh.totloop)) { + const MLoop &old_loop = mesh.mloop[i]; + MLoop &new_loop = new_mesh->mloop[loop_offset + i]; + new_loop = old_loop; + new_loop.v += vert_offset; + new_loop.e += edge_offset; + } + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &old_poly = mesh.mpoly[i]; + MPoly &new_poly = new_mesh->mpoly[poly_offset + i]; + new_poly = old_poly; + new_poly.loopstart += loop_offset; + } + + vert_offset += mesh.totvert; + loop_offset += mesh.totloop; + edge_offset += mesh.totedge; + poly_offset += mesh.totpoly; + } + } + } + + return new_mesh; +} + +static void join_attributes(Span<GeometryInstanceGroup> set_groups, + const GeometryComponentType component_type, + const Map<std::string, AttributeInfo> &attribute_info, + GeometryComponent &result) +{ + for (Map<std::string, AttributeInfo>::Item entry : attribute_info.items()) { + StringRef name = entry.key; + const AttributeDomain domain_output = entry.value.domain; + const CustomDataType data_type_output = entry.value.data_type; + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); + BLI_assert(cpp_type != nullptr); + + result.attribute_try_create(entry.key, domain_output, data_type_output); + WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); + if (!write_attribute || &write_attribute->cpp_type() != cpp_type || + write_attribute->domain() != domain_output) { + continue; + } + fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + + int offset = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has(component_type)) { + const GeometryComponent &component = *set.get_component_for_read(component_type); + const int domain_size = component.attribute_domain_size(domain_output); + if (domain_size == 0) { + continue; /* Domain size is 0, so no need to increment the offset. */ + } + ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + name, domain_output, data_type_output); + + if (source_attribute) { + fn::GSpan src_span = source_attribute->get_span(); + const void *src_buffer = src_span.data(); + for (const int UNUSED(i) : set_group.transforms.index_range()) { + void *dst_buffer = dst_span[offset]; + cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); + offset += domain_size; + } + } + else { + offset += domain_size * set_group.transforms.size(); + } + } + } + + write_attribute->apply_span(); + } +} + +static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) +{ + Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); + + MeshComponent &dst_component = result.get_component_for_write<MeshComponent>(); + dst_component.replace(new_mesh); + + /* The position attribute is handled above already. */ + Map<std::string, AttributeInfo> attributes; + gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {"position"}); + join_attributes(set_groups, + GeometryComponentType::Mesh, + attributes, + static_cast<GeometryComponent &>(dst_component)); +} + +static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups, + GeometrySet &result) +{ + int totpoint = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has<PointCloudComponent>()) { + const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>(); + totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); + } + } + + PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); + dst_component.replace(pointcloud); + Map<std::string, AttributeInfo> attributes; + gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {}); + join_attributes(set_groups, + GeometryComponentType::PointCloud, + attributes, + static_cast<GeometryComponent &>(dst_component)); +} + +static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, + GeometrySet &result) +{ + /* Not yet supported. Joining volume grids with the same name requires resampling of at least + * one of the grids. The cell size of the resulting volume has to be determined somehow. */ + VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); + UNUSED_VARS(set_groups, dst_component); +} + +GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) +{ + if (!geometry_set.has_instances()) { + return geometry_set; + } + + GeometrySet new_geometry_set; + + Vector<GeometryInstanceGroup> set_groups = BKE_geometry_set_gather_instances(geometry_set); + join_instance_groups_mesh(set_groups, new_geometry_set); + join_instance_groups_pointcloud(set_groups, new_geometry_set); + join_instance_groups_volume(set_groups, new_geometry_set); + + return new_geometry_set; +} + /** * Update the availability of a group of input sockets with the same name, * used for switching between attribute inputs or single values. diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index b7b2afeefcb..7ca44d82a38 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -49,4 +49,21 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType>); Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); +GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); + +struct AttributeInfo { + CustomDataType data_type; + AttributeDomain domain; +}; + +/** + * Add information about all the attributes on every component of the type. The resulting info + * will contain the highest complexity data type and the highest priority domain among every + * attribute with the given name on all of the input components. + */ +void gather_attribute_info(Map<std::string, AttributeInfo> &attributes, + const GeometryComponentType component_type, + Span<GeometryInstanceGroup> set_groups, + const Set<std::string> &ignored_attributes); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index e592bd0bda8..02a6ac49c29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -163,6 +163,8 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index 179e418214c..fc5fb4c5488 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -81,6 +81,8 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index 5e434a7f96d..ee97102de7b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -318,6 +318,8 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 92b47769d1f..a5ac1926963 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -119,6 +119,8 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index 076f7a9ee28..1780ec69df2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -267,6 +267,8 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 976970d06b1..2dbfcd2819c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -174,6 +174,8 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index 45f20ca553a..e46fdaa127a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -202,6 +202,12 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target"); + geometry_set = geometry_set_realize_instances(geometry_set); + + /* This isn't required. This node should be rewritten to handle instances + * for the target geometry set. However, the generic BVH API complicates this. */ + geometry_set_target = geometry_set_realize_instances(geometry_set_target); + if (geometry_set.has<MeshComponent>()) { attribute_calc_proximity( geometry_set.get_component_for_write<MeshComponent>(), geometry_set_target, params); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index f5765af83b3..f4399b45b2e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -202,6 +202,8 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); const int seed = params.get_input<int>("Seed"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { randomize_attribute(geometry_set.get_component_for_write<MeshComponent>(), params, seed); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index b6a960e5617..fc3cd099d41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -88,6 +88,8 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 62a291e8506..adcdab58e30 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -419,6 +419,8 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 403b74b2fef..38215b54640 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -124,6 +124,12 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) return; } + /* TODO: Boolean does support an input of multiple meshes. Currently they must all be + * converted to BMesh before running the operation though. D9957 will make it possible + * to use the mesh structure directly. */ + geometry_set_in_a = geometry_set_realize_instances(geometry_set_in_a); + geometry_set_in_b = geometry_set_realize_instances(geometry_set_in_b); + const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read(); const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 48ff8f2f683..1c794d1f43e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -51,6 +51,8 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params) { 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", std::move(geometry_set)); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 4e26977b85a..7249a34134f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -77,35 +77,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) quat_to_eul(rotation, quaternion); if (object != self_object) { - if (object->type == OB_MESH) { - Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object, false); - if (mesh != nullptr) { - BKE_mesh_wrapper_ensure_mdata(mesh); - - /* Make a copy because the life time of the other mesh might be shorter. */ - Mesh *copied_mesh = BKE_mesh_copy_for_eval(mesh, false); - - if (transform_space_relative) { - /* Transform into the local space of the object that is being modified. */ - BKE_mesh_transform(copied_mesh, transform, true); - } - - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace(copied_mesh); - mesh_component.copy_vertex_group_names_from_object(*object); - } + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + + if (transform_space_relative) { + instances.add_instance(object, transform); } - if (object->type == OB_VOLUME) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - - if (transform_space_relative) { - instances.add_instance(object, transform); - } - else { - float unit_transform[4][4]; - unit_m4(unit_transform); - instances.add_instance(object, unit_transform); - } + else { + float unit_transform[4][4]; + unit_m4(unit_transform); + instances.add_instance(object, unit_transform); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index 93abed7926e..581c356742b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -419,6 +419,9 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; + /* TODO: This node only needs read-only access to input instances. */ + geometry_set = geometry_set_realize_instances(geometry_set); + GeometryNodePointDistributeMethod distribute_method = static_cast<GeometryNodePointDistributeMethod>(params.node().custom1); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 1e7cf21f921..5cb98901d8d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -194,6 +194,10 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; + /* TODO: This node should be able to instance on the input instances component + * rather than making the entire input geometry set real. */ + geometry_set = geometry_set_realize_instances(geometry_set); + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); if (geometry_set.has<MeshComponent>()) { add_instances_from_geometry_component( diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc index 3ca898bfd83..013dbf5045f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -165,6 +165,8 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc index 78e23b783db..b73c8251e72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -64,6 +64,8 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 5f9d208a440..8bb8c5e980a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -178,6 +178,10 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params) GeometrySet out_set_a(geometry_set); GeometrySet out_set_b; + /* TODO: This is not necessary-- the input goemetry set can be read only, + * but it must be rewritten to handle instance groups. */ + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<PointCloudComponent>()) { separate_point_cloud(*geometry_set.get_component_for_read<PointCloudComponent>(), params, diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index f7f369f5d66..0b9d561bccb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -63,6 +63,8 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 428f129fb36..b79ca0a6197 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -215,6 +215,9 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params) GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; + /* TODO: Read-only access to instances should be supported here, for now they are made real. */ + geometry_set_in = geometry_set_realize_instances(geometry_set_in); + #ifdef WITH_OPENVDB initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); #endif diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 99f7339a1cc..4cdb669f964 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -55,6 +55,8 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { 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; diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 32fa32a9f13..0c754ad3b37 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -65,6 +65,8 @@ 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) { |