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:
parent
29e3545194
commit
6d162d35e2
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -124,37 +124,55 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul
|
|||
return mesh;
|
||||
}
|
||||
|
||||
static Mesh *curve_fill_calculate(GeoNodeExecParams ¶ms, 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
|
||||
|
|
Loading…
Reference in New Issue