Geometry Nodes: support instance attributes when realizing instances
This patch refactors the instance-realization code and adds new functionality. * Named and anonymous attributes are propagated from instances to the realized geometry. If the same attribute exists on the geometry and on an instance, the attribute on the geometry has precedence. * The id attribute has special handling to avoid creating the same id on many output points. This is necessary to make e.g. the Random Value node work as expected afterwards. Realizing instance attributes has an effect on existing files, especially due to the id attribute. To avoid breaking existing files, the Realize Instances node now has a legacy option that is enabled for all already existing Realize Instances nodes. Removing this legacy behavior does affect some existing files (although not many). We can decide whether it's worth to remove the old behavior as a separate step. This refactor also improves performance when realizing instances. That is mainly due to multi-threading. See D13446 to get the file used for benchmarking. The curve code is not as optimized as it could be yet. That's mainly because the storage for these attributes might change soonish and it wasn't worth optimizing for the current storage format right now. ``` 1,000,000 x mesh vertex: 530 ms -> 130 ms 1,000,000 x simple cube: 1290 ms -> 190 ms 1,000,000 x point: 1000 ms -> 150 ms 1,000,000 x curve spiral: 1740 ms -> 330 ms 1,000,000 x curve line: 1110 ms -> 210 ms 10,000 x subdivided cylinder: 170 ms -> 40 ms 10 x subdivided spiral: 180 ms -> 180 ms ``` Differential Revision: https://developer.blender.org/D13446
This commit is contained in:
@@ -39,7 +39,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 4
|
||||
#define BLENDER_FILE_SUBVERSION 5
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
@@ -57,8 +57,6 @@ struct GeometryInstanceGroup {
|
||||
void geometry_set_gather_instances(const GeometrySet &geometry_set,
|
||||
Vector<GeometryInstanceGroup> &r_instance_groups);
|
||||
|
||||
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@@ -705,6 +705,7 @@ struct CurveEval {
|
||||
* \warning Call #reallocate on the spline's attributes after adding all splines.
|
||||
*/
|
||||
void add_spline(SplinePtr spline);
|
||||
void add_splines(blender::MutableSpan<SplinePtr> splines);
|
||||
void remove_splines(blender::IndexMask mask);
|
||||
|
||||
void translate(const blender::float3 &translation);
|
||||
|
@@ -72,6 +72,8 @@ class DataTypeConversions {
|
||||
const void *from_value,
|
||||
void *to_value) const;
|
||||
|
||||
void convert_to_initialized_n(fn::GSpan from_span, fn::GMutableSpan to_span) const;
|
||||
|
||||
fn::GVArray try_convert(fn::GVArray varray, const CPPType &to_type) const;
|
||||
|
||||
fn::GVMutableArray try_convert(fn::GVMutableArray varray, const CPPType &to_type) const;
|
||||
|
@@ -71,6 +71,13 @@ void CurveEval::add_spline(SplinePtr spline)
|
||||
splines_.append(std::move(spline));
|
||||
}
|
||||
|
||||
void CurveEval::add_splines(MutableSpan<SplinePtr> splines)
|
||||
{
|
||||
for (SplinePtr &spline : splines) {
|
||||
this->add_spline(std::move(spline));
|
||||
}
|
||||
}
|
||||
|
||||
void CurveEval::remove_splines(blender::IndexMask mask)
|
||||
{
|
||||
for (int i = mask.size() - 1; i >= 0; i--) {
|
||||
|
@@ -481,13 +481,18 @@ void GeometrySet::gather_attributes_for_propagation(
|
||||
return;
|
||||
}
|
||||
|
||||
AttributeDomain domain = meta_data.domain;
|
||||
if (dst_component_type != GEO_COMPONENT_TYPE_INSTANCES && domain == ATTR_DOMAIN_INSTANCE) {
|
||||
domain = ATTR_DOMAIN_POINT;
|
||||
}
|
||||
|
||||
auto add_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = meta_data.domain;
|
||||
attribute_kind->domain = domain;
|
||||
attribute_kind->data_type = meta_data.data_type;
|
||||
};
|
||||
auto modify_info = [&](AttributeKind *attribute_kind) {
|
||||
attribute_kind->domain = bke::attribute_domain_highest_priority(
|
||||
{attribute_kind->domain, meta_data.domain});
|
||||
{attribute_kind->domain, domain});
|
||||
attribute_kind->data_type = bke::attribute_data_type_highest_complexity(
|
||||
{attribute_kind->data_type, meta_data.data_type});
|
||||
};
|
||||
|
@@ -207,375 +207,6 @@ void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> se
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
VectorSet<Material *> materials;
|
||||
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
const int tot_transforms = set_group.transforms.size();
|
||||
if (set.has_mesh()) {
|
||||
const Mesh &mesh = *set.get_mesh_for_read();
|
||||
totverts += mesh.totvert * tot_transforms;
|
||||
totloops += mesh.totloop * tot_transforms;
|
||||
totedges += mesh.totedge * tot_transforms;
|
||||
totpolys += mesh.totpoly * tot_transforms;
|
||||
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;
|
||||
for (const int slot_index : IndexRange(mesh.totcol)) {
|
||||
Material *material = mesh.mat[slot_index];
|
||||
materials.add(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't create an empty mesh. */
|
||||
if ((totverts + totloops + totedges + totpolys) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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_parameters_for_eval(new_mesh, &mesh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const int i : IndexRange(materials.size())) {
|
||||
Material *material = materials[i];
|
||||
BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
|
||||
}
|
||||
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();
|
||||
|
||||
Array<int> material_index_map(mesh.totcol);
|
||||
for (const int i : IndexRange(mesh.totcol)) {
|
||||
Material *material = mesh.mat[i];
|
||||
const int new_material_index = materials.index_of(material);
|
||||
material_index_map[i] = new_material_index;
|
||||
}
|
||||
|
||||
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;
|
||||
if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
|
||||
new_poly.mat_nr = material_index_map[new_poly.mat_nr];
|
||||
}
|
||||
else {
|
||||
/* The material index was invalid before. */
|
||||
new_poly.mat_nr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vert_offset += mesh.totvert;
|
||||
loop_offset += mesh.totloop;
|
||||
edge_offset += mesh.totedge;
|
||||
poly_offset += mesh.totpoly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A possible optimization is to only tag the normals dirty when there are transforms that change
|
||||
* normals. */
|
||||
BKE_mesh_normals_tag_dirty(new_mesh);
|
||||
|
||||
return new_mesh;
|
||||
}
|
||||
|
||||
static void join_attributes(Span<GeometryInstanceGroup> set_groups,
|
||||
Span<GeometryComponentType> component_types,
|
||||
const Map<AttributeIDRef, AttributeKind> &attribute_info,
|
||||
GeometryComponent &result)
|
||||
{
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item entry : attribute_info.items()) {
|
||||
const AttributeIDRef attribute_id = 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, AttributeInitDefault());
|
||||
WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id);
|
||||
if (!write_attribute || &write_attribute.varray.type() != cpp_type ||
|
||||
write_attribute.domain != domain_output) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fn::GVMutableArray_GSpan dst_span{write_attribute.varray};
|
||||
|
||||
int offset = 0;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
for (const GeometryComponentType component_type : component_types) {
|
||||
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. */
|
||||
}
|
||||
GVArray source_attribute = component.attribute_try_get_for_read(
|
||||
attribute_id, domain_output, data_type_output);
|
||||
|
||||
if (source_attribute) {
|
||||
fn::GVArray_GSpan src_span{source_attribute};
|
||||
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_assign_n(src_buffer, dst_buffer, domain_size);
|
||||
offset += domain_size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
offset += domain_size * set_group.transforms.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst_span.save();
|
||||
}
|
||||
}
|
||||
|
||||
static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup> set_groups)
|
||||
{
|
||||
/* Count the total number of points. */
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (totpoint == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PointCloud *new_pointcloud = BKE_pointcloud_new_nomain(totpoint);
|
||||
MutableSpan new_positions{(float3 *)new_pointcloud->co, new_pointcloud->totpoint};
|
||||
|
||||
/* Transform each instance's point locations into the new point cloud. */
|
||||
int offset = 0;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
const PointCloud *pointcloud = set.get_pointcloud_for_read();
|
||||
if (pointcloud == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (const float4x4 &transform : set_group.transforms) {
|
||||
for (const int i : IndexRange(pointcloud->totpoint)) {
|
||||
new_positions[offset + i] = transform * float3(pointcloud->co[i]);
|
||||
}
|
||||
offset += pointcloud->totpoint;
|
||||
}
|
||||
}
|
||||
|
||||
return new_pointcloud;
|
||||
}
|
||||
|
||||
static CurveEval *join_curve_splines_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups)
|
||||
{
|
||||
Vector<SplinePtr> new_splines;
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
if (!set.has_curve()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CurveEval &source_curve = *set.get_curve_for_read();
|
||||
for (const SplinePtr &source_spline : source_curve.splines()) {
|
||||
for (const float4x4 &transform : set_group.transforms) {
|
||||
SplinePtr new_spline = source_spline->copy_without_attributes();
|
||||
new_spline->transform(transform);
|
||||
new_splines.append(std::move(new_spline));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new_splines.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CurveEval *new_curve = new CurveEval();
|
||||
for (SplinePtr &new_spline : new_splines) {
|
||||
new_curve->add_spline(std::move(new_spline));
|
||||
}
|
||||
|
||||
new_curve->attributes.reallocate(new_curve->splines().size());
|
||||
return new_curve;
|
||||
}
|
||||
|
||||
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
|
||||
{
|
||||
Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups);
|
||||
if (new_mesh == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
|
||||
dst_component.replace(new_mesh);
|
||||
|
||||
/* Don't copy attributes that are stored directly in the mesh data structs. */
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups,
|
||||
{GEO_COMPONENT_TYPE_MESH},
|
||||
{"position", "material_index", "normal", "shade_smooth", "crease"},
|
||||
attributes);
|
||||
join_attributes(set_groups,
|
||||
{GEO_COMPONENT_TYPE_MESH},
|
||||
attributes,
|
||||
static_cast<GeometryComponent &>(dst_component));
|
||||
}
|
||||
|
||||
static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups,
|
||||
GeometrySet &result)
|
||||
{
|
||||
PointCloud *new_pointcloud = join_pointcloud_position_attribute(set_groups);
|
||||
if (new_pointcloud == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
|
||||
dst_component.replace(new_pointcloud);
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes);
|
||||
join_attributes(set_groups,
|
||||
{GEO_COMPONENT_TYPE_POINT_CLOUD},
|
||||
attributes,
|
||||
static_cast<GeometryComponent &>(dst_component));
|
||||
}
|
||||
|
||||
static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
|
||||
GeometrySet &result)
|
||||
{
|
||||
/* Not yet supported; for now only return the first volume. 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. */
|
||||
for (const GeometryInstanceGroup &set_group : set_groups) {
|
||||
const GeometrySet &set = set_group.geometry_set;
|
||||
if (set.has<VolumeComponent>()) {
|
||||
result.add(*set.get_component_for_read<VolumeComponent>());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Curve point domain attributes must be in the same order on every spline. The order might have
|
||||
* been different on separate instances, so ensure that all splines have the same order. Note that
|
||||
* because #Map is used, the order is not necessarily consistent every time, but it is the same for
|
||||
* every spline, and that's what matters.
|
||||
*/
|
||||
static void sort_curve_point_attributes(const Map<AttributeIDRef, AttributeKind> &info,
|
||||
MutableSpan<SplinePtr> splines)
|
||||
{
|
||||
Vector<AttributeIDRef> new_order;
|
||||
for (Map<AttributeIDRef, AttributeKind>::Item item : info.items()) {
|
||||
if (item.value.domain == ATTR_DOMAIN_POINT) {
|
||||
/* Only sort attributes stored on splines. */
|
||||
new_order.append(item.key);
|
||||
}
|
||||
}
|
||||
for (SplinePtr &spline : splines) {
|
||||
spline->attributes.reorder(new_order);
|
||||
}
|
||||
}
|
||||
|
||||
static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
|
||||
{
|
||||
CurveEval *curve = join_curve_splines_and_builtin_attributes(set_groups);
|
||||
if (curve == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
|
||||
dst_component.replace(curve);
|
||||
|
||||
Map<AttributeIDRef, AttributeKind> attributes;
|
||||
geometry_set_gather_instances_attribute_info(
|
||||
set_groups,
|
||||
{GEO_COMPONENT_TYPE_CURVE},
|
||||
{"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"},
|
||||
attributes);
|
||||
join_attributes(set_groups,
|
||||
{GEO_COMPONENT_TYPE_CURVE},
|
||||
attributes,
|
||||
static_cast<GeometryComponent &>(dst_component));
|
||||
sort_curve_point_attributes(attributes, curve->splines());
|
||||
curve->assert_valid_point_attributes();
|
||||
}
|
||||
|
||||
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;
|
||||
geometry_set_gather_instances(geometry_set, set_groups);
|
||||
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);
|
||||
join_instance_groups_curve(set_groups, new_geometry_set);
|
||||
|
||||
return new_geometry_set;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
void InstancesComponent::foreach_referenced_geometry(
|
||||
|
@@ -239,6 +239,23 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
|
||||
functions->convert_single_to_uninitialized(from_value, to_value);
|
||||
}
|
||||
|
||||
void DataTypeConversions::convert_to_initialized_n(fn::GSpan from_span,
|
||||
fn::GMutableSpan to_span) const
|
||||
{
|
||||
const CPPType &from_type = from_span.type();
|
||||
const CPPType &to_type = to_span.type();
|
||||
BLI_assert(from_span.size() == to_span.size());
|
||||
BLI_assert(this->is_convertible(from_type, to_type));
|
||||
const fn::MultiFunction *fn = this->get_conversion_multi_function(
|
||||
MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
|
||||
fn::MFParamsBuilder params{*fn, from_span.size()};
|
||||
params.add_readonly_single_input(from_span);
|
||||
to_type.destruct_n(to_span.data(), to_span.size());
|
||||
params.add_uninitialized_single_output(to_span);
|
||||
fn::MFContextBuilder context;
|
||||
fn->call_auto(IndexRange(from_span.size()), params, context);
|
||||
}
|
||||
|
||||
class GVArray_For_ConvertedGVArray : public fn::GVArrayImpl {
|
||||
private:
|
||||
fn::GVArray varray_;
|
||||
|
@@ -2434,6 +2434,20 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 301, 5)) {
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
if (ntree->type != NTREE_GEOMETRY) {
|
||||
continue;
|
||||
}
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
if (node->type != GEO_NODE_REALIZE_INSTANCES) {
|
||||
continue;
|
||||
}
|
||||
node->custom1 |= GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
@@ -31,7 +31,10 @@ set(INC
|
||||
|
||||
set(SRC
|
||||
intern/mesh_to_curve_convert.cc
|
||||
intern/realize_instances.cc
|
||||
|
||||
GEO_mesh_to_curve.hh
|
||||
GEO_realize_instances.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
52
source/blender/geometry/GEO_realize_instances.hh
Normal file
52
source/blender/geometry/GEO_realize_instances.hh
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_geometry_set.hh"
|
||||
|
||||
namespace blender::geometry {
|
||||
|
||||
struct RealizeInstancesOptions {
|
||||
/**
|
||||
* The default is to generate new ids for every element (when there was any id attribute in the
|
||||
* input). This avoids having a geometry that contains the same id many times.
|
||||
* When this is `true` the ids on the original geometries are kept unchanged and ids on instances
|
||||
* are ignored. Ids are zero initialized when the original geometry did not have an id.
|
||||
*/
|
||||
bool keep_original_ids = false;
|
||||
/**
|
||||
* When `true` the output geometry will contain all the generic attributes that existed on
|
||||
* instances. Otherwise, instance attributes are ignored.
|
||||
*/
|
||||
bool realize_instance_attributes = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Join all instances into a single geometry component for each geometry type. For example, all
|
||||
* mesh instances (including the already realized mesh) are joined into a single mesh. The output
|
||||
* geometry set does not contain any instances. If the input did not contain any instances, it is
|
||||
* returned directly.
|
||||
*
|
||||
* The `id` attribute has special handling. If there is an id attribute on any component, the
|
||||
* output will contain an `id` attribute as well. The output id is generated by mixing/hashing ids
|
||||
* of instances and of the instanced geometry data.
|
||||
*/
|
||||
GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOptions &options);
|
||||
|
||||
GeometrySet realize_instances_legacy(GeometrySet geometry_set);
|
||||
|
||||
} // namespace blender::geometry
|
1347
source/blender/geometry/intern/realize_instances.cc
Normal file
1347
source/blender/geometry/intern/realize_instances.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2290,6 +2290,10 @@ typedef enum GeometryNodeDeleteGeometryMode {
|
||||
GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE = 2,
|
||||
} GeometryNodeDeleteGeometryMode;
|
||||
|
||||
typedef enum GeometryNodeRealizeInstancesFlag {
|
||||
GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR = (1 << 0),
|
||||
} GeometryNodeRealizeInstancesFlag;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -11275,6 +11275,17 @@ static void def_geo_viewer(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_realize_instances(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "legacy_behavior", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "custom1", GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Legacy Behavior", "Behave like before instance attributes existed");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void rna_def_shader_node(BlenderRNA *brna)
|
||||
|
@@ -30,6 +30,7 @@ set(INC
|
||||
../depsgraph
|
||||
../editors/include
|
||||
../functions
|
||||
../geometry
|
||||
../makesdna
|
||||
../makesrna
|
||||
../nodes
|
||||
|
@@ -28,6 +28,8 @@
|
||||
#include "NOD_derived_node_tree.hh"
|
||||
#include "NOD_geometry_nodes_eval_log.hh"
|
||||
|
||||
#include "GEO_realize_instances.hh"
|
||||
|
||||
struct Depsgraph;
|
||||
struct ModifierData;
|
||||
|
||||
@@ -36,7 +38,6 @@ namespace blender::nodes {
|
||||
using bke::AnonymousAttributeFieldInput;
|
||||
using bke::AttributeFieldInput;
|
||||
using bke::AttributeIDRef;
|
||||
using bke::geometry_set_realize_instances;
|
||||
using bke::GeometryComponentFieldContext;
|
||||
using bke::GeometryFieldInput;
|
||||
using bke::OutputAttribute;
|
||||
|
@@ -394,7 +394,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", Poin
|
||||
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
|
||||
DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
|
||||
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
|
||||
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
|
||||
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
|
||||
DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
|
||||
DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
|
||||
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
|
||||
|
@@ -201,7 +201,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -247,7 +247,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -102,7 +102,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
|
@@ -113,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
combine_attributes(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -324,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -132,7 +132,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
const std::string result_name = params.extract_input<std::string>("Result");
|
||||
const std::string source_name = params.extract_input<std::string>("Attribute");
|
||||
|
@@ -186,7 +186,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
|
@@ -131,7 +131,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -285,7 +285,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -226,7 +226,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -206,11 +206,11 @@ static void node_geo_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);
|
||||
geometry_set = geometry::realize_instances_legacy(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);
|
||||
geometry_set_target = geometry::realize_instances_legacy(geometry_set_target);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_calc_proximity(
|
||||
|
@@ -296,7 +296,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const GeometryNodeAttributeRandomizeMode operation =
|
||||
static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation);
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(),
|
||||
|
@@ -104,7 +104,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -134,7 +134,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
separate_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -487,8 +487,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
return;
|
||||
}
|
||||
|
||||
dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
|
||||
src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
|
||||
dst_geometry_set = geometry::realize_instances_legacy(dst_geometry_set);
|
||||
src_geometry_set = geometry::realize_instances_legacy(src_geometry_set);
|
||||
|
||||
if (dst_geometry_set.has<MeshComponent>()) {
|
||||
transfer_attribute(params,
|
||||
|
@@ -533,7 +533,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -311,7 +311,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
|
@@ -149,7 +149,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_default_remaining_outputs();
|
||||
|
@@ -32,7 +32,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Curve", geometry_set);
|
||||
return;
|
||||
|
@@ -101,7 +101,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
|
||||
const CurveEval *curve = curve_component.get_for_read();
|
||||
|
@@ -70,7 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Curve", geometry_set);
|
||||
return;
|
||||
|
@@ -243,7 +243,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Curve", geometry_set);
|
||||
return;
|
||||
|
@@ -351,7 +351,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Geometry", geometry_set);
|
||||
|
@@ -296,7 +296,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_curve()) {
|
||||
params.set_output("Geometry", GeometrySet());
|
||||
|
@@ -630,7 +630,7 @@ static void delete_mesh_selection(MeshComponent &component,
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
const bool invert = params.extract_input<bool>("Invert");
|
||||
const std::string selection_name = params.extract_input<std::string>("Selection");
|
||||
|
@@ -41,7 +41,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_mesh()) {
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
|
@@ -66,7 +66,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
|
@@ -31,7 +31,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_mesh()) {
|
||||
params.set_default_remaining_outputs();
|
||||
|
@@ -229,7 +229,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
/* 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);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
const Vector<InstanceReference> possible_references = get_instance_references(params);
|
||||
if (possible_references.is_empty()) {
|
||||
|
@@ -203,7 +203,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
|
||||
|
@@ -105,7 +105,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
|
@@ -150,7 +150,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
/* TODO: This is not necessary-- the input geometry set can be read only,
|
||||
* but it must be rewritten to handle instance groups. */
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (params.lazy_output_is_required("Geometry 1")) {
|
||||
params.set_output("Geometry 1",
|
||||
|
@@ -57,7 +57,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
|
||||
|
@@ -248,7 +248,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
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);
|
||||
geometry_set_in = geometry::realize_instances_legacy(geometry_set_in);
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params);
|
||||
|
@@ -285,8 +285,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
|
||||
const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
|
||||
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
target_geometry_set = geometry::realize_instances_legacy(target_geometry_set);
|
||||
|
||||
static const Array<GeometryComponentType> types = {
|
||||
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
|
||||
|
@@ -60,7 +60,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const std::string selection_name = params.extract_input<std::string>("Selection");
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
|
@@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
|
||||
geometry_set = geometry_set_realize_instances(geometry_set);
|
||||
geometry_set = geometry::realize_instances_legacy(geometry_set);
|
||||
|
||||
if (!geometry_set.has_mesh()) {
|
||||
params.set_output("Geometry", geometry_set);
|
||||
|
@@ -24,6 +24,8 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "GEO_realize_instances.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_join_geometry_cc {
|
||||
@@ -190,7 +192,10 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
|
||||
instances.add_instance(handle, float4x4::identity());
|
||||
}
|
||||
|
||||
GeometrySet joined_components = bke::geometry_set_realize_instances(instances_geometry_set);
|
||||
geometry::RealizeInstancesOptions options;
|
||||
options.keep_original_ids = true;
|
||||
options.realize_instance_attributes = false;
|
||||
GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options);
|
||||
result.add(joined_components.get_component_for_write<Component>());
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "GEO_realize_instances.hh"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
@@ -27,10 +29,20 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
b.add_output<decl::Geometry>(N_("Geometry"));
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "legacy_behavior", 0, nullptr, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const bool legacy_behavior = params.node().custom1 & GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
|
||||
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
geometry_set = bke::geometry_set_realize_instances(geometry_set);
|
||||
geometry::RealizeInstancesOptions options;
|
||||
options.keep_original_ids = legacy_behavior;
|
||||
options.realize_instance_attributes = !legacy_behavior;
|
||||
geometry_set = geometry::realize_instances(geometry_set, options);
|
||||
params.set_output("Geometry", std::move(geometry_set));
|
||||
}
|
||||
|
||||
@@ -45,6 +57,7 @@ void register_node_type_geo_realize_instances()
|
||||
geo_node_type_base(
|
||||
&ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0);
|
||||
ntype.declare = file_ns::node_declare;
|
||||
ntype.draw_buttons_ex = file_ns::node_layout;
|
||||
ntype.geometry_node_execute = file_ns::node_geo_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
Reference in New Issue
Block a user