diff options
author | Hans Goudey <h.goudey@me.com> | 2021-05-08 22:53:32 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-05-08 22:53:32 +0300 |
commit | 9569a522f28a30d1b95cbee7b05cb3b2a397a268 (patch) | |
tree | 633754ba177e2a2dc0c2e06656e2dac5ad061947 | |
parent | 34439f05ab68e0f6287c96d7391f38dca8e64205 (diff) |
Geometry Nodes: Refactor point instance node
This patch refactors the instance component to make use of the earlier
refactoring in rB4599cea15dcf. Now we don't have to build an array of
instance references the size of the point domain, and we can gather the
possible instances only once and use the same vector for all component
types. Generally the node should be a bit faster and use less memory.
The logic is moved around a bit, especially the hashing of the ID
attribute to pick from the instance list, but the result is unchanged.
Differential Revision: https://developer.blender.org/D11203
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_point_instance.cc | 145 |
1 files changed, 85 insertions, 60 deletions
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 d42b61df5a8..98f36deb050 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -63,94 +63,93 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } -static void get_instance_references__object(const GeoNodeExecParams ¶ms, - MutableSpan<InstanceReference> r_references) +static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams ¶ms) { - Object *object = params.get_input<Object *>("Object"); + Object *object = params.extract_input<Object *>("Object"); if (object == params.self_object()) { - object = nullptr; + return {}; } if (object != nullptr) { - r_references.fill(*object); + return {*object}; } + return {}; } -static void get_instance_references__collection(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - MutableSpan<InstanceReference> r_references) +static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; Collection *collection = params.get_input<Collection *>("Collection"); if (collection == nullptr) { - return; + return {}; } if (BLI_listbase_is_empty(&collection->children) && BLI_listbase_is_empty(&collection->gobject)) { params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty")); - return; + return {}; } - const bool use_whole_collection = (node_storage->flag & - GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; - if (use_whole_collection) { - r_references.fill(*collection); + if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) { + return {*collection}; } - else { - Vector<InstanceReference> possible_references; - /* Direct child objects are instanced as objects. */ - LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - possible_references.append(*cob->ob); - } - /* Direct child collections are instanced as collections. */ - LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - possible_references.append(*child->collection); - } - if (!possible_references.is_empty()) { - const int seed = params.get_input<int>("Seed"); - Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT); - for (const int i : r_references.index_range()) { - const int index = BLI_hash_int_2d(ids[i], seed) % possible_references.size(); - r_references[i] = possible_references[index]; - } - } + Vector<InstanceReference> references; + /* Direct child objects are instanced as objects. */ + LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { + references.append(*cob->ob); + } + /* Direct child collections are instanced as collections. */ + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { + references.append(*child->collection); } + + return references; } -static Array<InstanceReference> get_instance_references(const GeoNodeExecParams ¶ms, - const GeometryComponent &component, - const int amount) +static Vector<InstanceReference> get_instance_references(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage; const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType) node_storage->instance_type; - Array<InstanceReference> references(amount); switch (type) { case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: { - get_instance_references__object(params, references); - break; + return get_instance_references__object(params); } case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { - get_instance_references__collection(params, component, references); - break; + return get_instance_references__collection(params); } } - return references; + return {}; } -static void add_instances_from_geometry_component(InstancesComponent &instances, - const GeometryComponent &src_geometry, - const GeoNodeExecParams ¶ms) +/** + * Add the instance references to the component as a separate step from actually creating the + * instances in order to avoid a map lookup for every transform. While this might add some + * unecessary references if they are not chosen while adding transforms, in the common cases + * there are many more transforms than there are references, so that isn't likely. + */ +static Array<int> add_instance_references(InstancesComponent &instance_component, + Span<InstanceReference> possible_references) +{ + Array<int> possible_handles(possible_references.size()); + for (const int i : possible_references.index_range()) { + possible_handles[i] = instance_component.add_reference(possible_references[i]); + } + return possible_handles; +} + +static void add_instances_from_component(InstancesComponent &instances, + const GeometryComponent &src_geometry, + Span<int> possible_handles, + const GeoNodeExecParams ¶ms) { const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_geometry.attribute_domain_size(domain); - Array<InstanceReference> references = get_instance_references(params, src_geometry, domain_size); GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); @@ -158,14 +157,27 @@ static void add_instances_from_geometry_component(InstancesComponent &instances, "rotation", domain, {0, 0, 0}); GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>( "scale", domain, {1, 1, 1}); - GVArray_Typed<int> ids = src_geometry.attribute_get_for_read<int>("id", domain, -1); - - for (const int i : IndexRange(domain_size)) { - const InstanceReference &reference = references[i]; - if (reference.type() != InstanceReference::Type::None) { - const float4x4 matrix = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); - const int handle = instances.add_reference(reference); - instances.add_instance(handle, matrix, ids[i]); + GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); + + /* Skip all of the randomness handling if there is only a single possible instance + * (anything except for collection mode with "Whole Collection" turned off). */ + if (possible_handles.size() == 1) { + const int handle = possible_handles.first(); + for (const int i : IndexRange(domain_size)) { + instances.add_instance(handle, + float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]), + id_attribute[i]); + } + } + else { + const int seed = params.get_input<int>("Seed"); + Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT); + for (const int i : IndexRange(domain_size)) { + const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size(); + const int handle = possible_handles[index]; + instances.add_instance(handle, + float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]), + id_attribute[i]); } } } @@ -179,19 +191,32 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) * rather than making the entire input geometry set real. */ geometry_set = geometry_set_realize_instances(geometry_set); + const Vector<InstanceReference> possible_references = get_instance_references(params); + if (possible_references.is_empty()) { + params.set_output("Geometry", std::move(geometry_set_out)); + return; + } + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + Array<int> possible_handles = add_instance_references(instances, possible_references); + if (geometry_set.has<MeshComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<MeshComponent>(), + possible_handles, + params); } if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<PointCloudComponent>(), + possible_handles, + params); } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read<CurveComponent>(), params); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<CurveComponent>(), + possible_handles, + params); } params.set_output("Geometry", std::move(geometry_set_out)); |