Geometry Nodes: Fill instances separately in the curve fill node

With this commit, each referenced instance data will be converted to
a geometry instances and processed separately. This should result in
a large speedup when the instances component has many insances
referring to the same data.

This change can act as a blueprint for other nodes that need to
implement similar behavior. It adds some helper functions on the
instances component to make that easier.

Thanks to Erik Abrahamsson for a proof of concept patch.

Differential Revision: https://developer.blender.org/D12572
This commit is contained in:
Hans Goudey 2021-09-21 14:20:54 -05:00
parent 29e3545194
commit 6d162d35e2
3 changed files with 88 additions and 13 deletions

View File

@ -580,6 +580,9 @@ class InstancesComponent : public GeometryComponent {
blender::Span<InstanceReference> references() const;
void ensure_geometry_instances();
GeometrySet &geometry_set_from_reference(const int reference_index);
blender::Span<int> instance_reference_handles() const;
blender::MutableSpan<int> instance_reference_handles();
blender::MutableSpan<blender::float4x4> instance_transforms();
@ -588,6 +591,7 @@ class InstancesComponent : public GeometryComponent {
blender::Span<int> instance_ids() const;
int instances_amount() const;
int references_amount() const;
blender::Span<int> almost_unique_ids() const;

View File

@ -24,6 +24,7 @@
#include "DNA_collection_types.h"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "attribute_access_intern.hh"
@ -32,6 +33,7 @@ using blender::Map;
using blender::MutableSpan;
using blender::Set;
using blender::Span;
using blender::VectorSet;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@ -119,6 +121,52 @@ blender::Span<int> InstancesComponent::instance_ids() const
return instance_ids_;
}
/**
* If references have a collection or object type, convert them into geometry instances. This
* will join geometry components from nested instances if necessary. After that, the geometry
* sets can be edited.
*/
void InstancesComponent::ensure_geometry_instances()
{
VectorSet<InstanceReference> new_references;
new_references.reserve(references_.size());
for (const InstanceReference &reference : references_) {
if (reference.type() == InstanceReference::Type::Object) {
GeometrySet geometry_set;
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
const int handle = instances.add_reference(reference.object());
instances.add_instance(handle, float4x4::identity());
new_references.add_new(geometry_set);
}
else if (reference.type() == InstanceReference::Type::Collection) {
GeometrySet geometry_set;
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
const int handle = instances.add_reference(reference.collection());
instances.add_instance(handle, float4x4::identity());
new_references.add_new(geometry_set);
}
else {
new_references.add_new(reference);
}
}
references_ = std::move(new_references);
}
/**
* With write access to the instances component, the data in the instanced geometry sets can be
* changed. This is a function on the component rather than each reference to ensure const
* correct-ness for that reason.
*/
GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index)
{
/* If this assert fails, it means #ensure_geometry_instances must be called first. */
BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet);
/* The const cast is okay because the instance's hash in the set
* is not changed by adjusting the data inside the geometry set. */
return const_cast<GeometrySet &>(references_[reference_index].geometry_set());
}
/**
* Returns a handle for the given reference.
* If the reference exists already, the handle of the existing reference is returned.
@ -139,6 +187,11 @@ int InstancesComponent::instances_amount() const
return instance_transforms_.size();
}
int InstancesComponent::references_amount() const
{
return references_.size();
}
bool InstancesComponent::is_empty() const
{
return this->instance_reference_handles_.size() == 0;

View File

@ -124,37 +124,55 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul
return mesh;
}
static Mesh *curve_fill_calculate(GeoNodeExecParams &params, const CurveComponent &component)
static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode)
{
const CurveEval &curve = *component.get_for_read();
if (curve.splines().size() == 0) {
return nullptr;
if (!geometry_set.has_curve()) {
return;
}
const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
const CurveEval &curve = *geometry_set.get_curve_for_read();
if (curve.splines().is_empty()) {
geometry_set.replace_curve(nullptr);
return;
}
const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ?
CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES :
CDT_INSIDE_WITH_HOLES;
const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type);
return cdt_to_mesh(results);
Mesh *mesh = cdt_to_mesh(results);
geometry_set.replace_mesh(mesh);
geometry_set.replace_curve(nullptr);
}
static void geo_node_curve_fill_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Mesh", GeometrySet());
const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
instances.ensure_geometry_instances();
threading::parallel_for(IndexRange(instances.references_amount()), 16, [&](IndexRange range) {
for (int i : range) {
GeometrySet &geometry_set = instances.geometry_set_from_reference(i);
geometry_set = bke::geometry_set_realize_instances(geometry_set);
curve_fill_calculate(geometry_set, mode);
}
});
params.set_output("Mesh", std::move(geometry_set));
return;
}
Mesh *mesh = curve_fill_calculate(params,
*geometry_set.get_component_for_read<CurveComponent>());
params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
curve_fill_calculate(geometry_set, mode);
params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes