Geometry Nodes: Make Random ID a builtin attribute, remove sockets

In order to address feedback that the "Stable ID" was not easy enough
to use, remove the "Stable ID" output from the distribution node and
the input from the instance on points node. Instead, the nodes write
or read a builtin named attribute called `id`. In the future we may
add more attributes like `edge_id` and `face_id`.

The downside is that more behavior is invisible, which is les
expected now that most attributes are passed around with node links.
This behavior will have to be explained in the manual.

The random value node's "ID" input that had an implicit index input
is converted to a special implicit input that uses the `id` attribute
if possible, but otherwise defaults to the index. There is no way to
tell in the UI which it uses, except by knowing that rule and checking
in the spreadsheet for the id attribute.

Because it isn't always possible to create stable randomness, this
attribute does not always exist, and it will be possible to remove it
when we have the attribute remove node back, to improve performance.

Differential Revision: https://developer.blender.org/D12903
This commit is contained in:
Hans Goudey 2021-10-20 10:54:54 -05:00
parent 1a96045eec
commit 40c3b8836b
Notes: blender-bot 2023-02-13 17:29:55 +01:00
Referenced by commit e79e86018e, Fix T92387: crash in legacy point distribute node
Referenced by issue #92005, Make "Stable ID" a built-in attribute
11 changed files with 339 additions and 147 deletions

View File

@ -760,6 +760,24 @@ class AttributeFieldInput : public fn::FieldInput {
bool is_equal_to(const fn::FieldNode &other) const override;
};
class IDAttributeFieldInput : public fn::FieldInput {
public:
IDAttributeFieldInput() : fn::FieldInput(CPPType::get<int>())
{
}
static fn::Field<int> Create();
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
std::string socket_inspection_name() const override;
uint64_t hash() const override;
bool is_equal_to(const fn::FieldNode &other) const override;
};
class AnonymousAttributeFieldInput : public fn::FieldInput {
private:
/**

View File

@ -345,11 +345,18 @@ GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
const void *data = CustomData_get_layer(custom_data, stored_type_);
const void *data;
if (stored_as_named_attribute_) {
data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
}
else {
data = CustomData_get_layer(custom_data, stored_type_);
}
if (data == nullptr) {
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
return as_read_attribute_(data, domain_size);
}
@ -368,11 +375,21 @@ GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
if (data == nullptr) {
return {};
}
void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
void *new_data;
if (stored_as_named_attribute_) {
new_data = CustomData_duplicate_referenced_layer_named(
custom_data, stored_type_, name_.c_str(), domain_size);
}
else {
new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size);
}
if (data != new_data) {
custom_data_access_.update_custom_data_pointers(component);
data = new_data;
}
if (update_on_write_ != nullptr) {
update_on_write_(component);
}
@ -390,7 +407,19 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
}
const int domain_size = component.attribute_domain_size(domain_);
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
int layer_index;
if (stored_as_named_attribute_) {
for (const int i : IndexRange(custom_data->totlayer)) {
if (custom_data_layer_matches_attribute_id(custom_data->layers[i], name_)) {
layer_index = i;
break;
}
}
}
else {
layer_index = CustomData_get_layer_index(custom_data, stored_type_);
}
const bool delete_success = CustomData_free_layer(
custom_data, stored_type_, domain_size, layer_index);
if (delete_success) {
@ -409,14 +438,25 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
if (custom_data == nullptr) {
return false;
}
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
/* Exists already. */
return false;
}
const int domain_size = component.attribute_domain_size(domain_);
const bool success = add_builtin_type_custom_data_layer_from_init(
*custom_data, stored_type_, domain_size, initializer);
bool success;
if (stored_as_named_attribute_) {
if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) {
/* Exists already. */
return false;
}
success = add_custom_data_layer_from_attribute_init(
name_, *custom_data, stored_type_, domain_size, initializer);
}
else {
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
/* Exists already. */
return false;
}
success = add_builtin_type_custom_data_layer_from_init(
*custom_data, stored_type_, domain_size, initializer);
}
if (success) {
custom_data_access_.update_custom_data_pointers(component);
}
@ -429,8 +469,10 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
if (custom_data == nullptr) {
return false;
}
const void *data = CustomData_get_layer(custom_data, stored_type_);
return data != nullptr;
if (stored_as_named_attribute_) {
return CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()) != nullptr;
}
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
}
ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
@ -1353,6 +1395,54 @@ bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
return false;
}
static StringRef get_random_id_attribute_name(const AttributeDomain domain)
{
switch (domain) {
case ATTR_DOMAIN_POINT:
return "id";
default:
return "";
}
}
const GVArray *IDAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const
{
if (const GeometryComponentFieldContext *geometry_context =
dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
const GeometryComponent &component = geometry_context->geometry_component();
const AttributeDomain domain = geometry_context->domain();
const StringRef name = get_random_id_attribute_name(domain);
GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32);
if (attribute) {
BLI_assert(attribute->size() == component.attribute_domain_size(domain));
return scope.add(std::move(attribute));
}
/* Use the index as the fallback if no random ID attribute exists. */
return fn::IndexFieldInput::get_index_varray(mask, scope);
}
return nullptr;
}
std::string IDAttributeFieldInput::socket_inspection_name() const
{
return TIP_("ID / Index");
}
uint64_t IDAttributeFieldInput::hash() const
{
/* All random ID attribute inputs are the same within the same evaluation context. */
return 92386459827;
}
bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
{
/* All random ID attribute inputs are the same within the same evaluation context. */
return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
}
const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const
{

View File

@ -227,6 +227,9 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* This provider is used to provide access to builtin attributes. It supports making internal types
* available as different types. For example, the vertex position attribute is stored as part of
* the #MVert struct, but is exposed as float3 attribute.
*
* It also supports named builtin attributes, and will look up attributes in #CustomData by name
* if the stored type is the same as the attribute type.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
@ -238,6 +241,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
const UpdateOnWrite update_on_write_;
bool stored_as_named_attribute_;
public:
BuiltinCustomDataLayerProvider(std::string attribute_name,
@ -257,7 +261,8 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
custom_data_access_(custom_data_access),
as_read_attribute_(as_read_attribute),
as_write_attribute_(as_write_attribute),
update_on_write_(update_on_write)
update_on_write_(update_on_write),
stored_as_named_attribute_(data_type_ == stored_type_)
{
}

View File

@ -623,6 +623,103 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
}
static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
const CustomDataType data_type,
const Span<SplinePtr> splines)
{
switch (initializer.type) {
case AttributeInit::Type::Default:
/* This function shouldn't be called in this case, since there
* is no need to copy anything to the new custom data array. */
BLI_assert_unreachable();
return {};
case AttributeInit::Type::VArray:
return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
case AttributeInit::Type::MoveArray:
int total_size = 0;
for (const SplinePtr &spline : splines) {
total_size += spline->size();
}
return std::make_unique<fn::GVArray_For_GSpan>(
GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
total_size));
}
BLI_assert_unreachable();
return {};
}
static bool create_point_attribute(GeometryComponent &component,
const AttributeIDRef &attribute_id,
const AttributeInit &initializer,
const CustomDataType data_type)
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
MutableSpan<SplinePtr> splines = curve->splines();
/* First check the one case that allows us to avoid copying the input data. */
if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) {
MEM_freeN(source_data);
return false;
}
return true;
}
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create(attribute_id, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
BLI_assert_unreachable();
return false;
}
}
/* With a default initializer type, we can keep the values at their initial values. */
if (initializer.type == AttributeInit::Type::Default) {
return true;
}
WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, splines);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
}
return true;
}
static bool remove_point_attribute(GeometryComponent &component,
const AttributeIDRef &attribute_id)
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
return false;
}
/* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
bool layer_freed = false;
for (SplinePtr &spline : curve->splines()) {
layer_freed = spline->attributes.remove(attribute_id);
}
return layer_freed;
}
/**
* Virtual array for any control point data accessed with spans and an offset array.
*/
@ -980,15 +1077,17 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
public:
BuiltinPointAttributeProvider(std::string attribute_name,
const CreatableEnum creatable,
const DeletableEnum deletable,
const GetSpan get_span,
const GetMutableSpan get_mutable_span,
const UpdateOnWrite update_on_write)
: BuiltinAttributeProvider(std::move(attribute_name),
ATTR_DOMAIN_POINT,
bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
CreatableEnum::NonCreatable,
creatable,
WritableEnum::Writable,
DeletableEnum::NonDeletable),
deletable),
get_span_(get_span),
get_mutable_span_(get_mutable_span),
update_on_write_(update_on_write)
@ -1041,15 +1140,20 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return point_data_gvarray(spans, offsets);
}
bool try_delete(GeometryComponent &UNUSED(component)) const final
bool try_delete(GeometryComponent &component) const final
{
return false;
if (deletable_ == DeletableEnum::NonDeletable) {
return false;
}
return remove_point_attribute(component, name_);
}
bool try_create(GeometryComponent &UNUSED(component),
const AttributeInit &UNUSED(initializer)) const final
bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final
{
return false;
if (createable_ == CreatableEnum::NonCreatable) {
return false;
}
return create_point_attribute(component, name_, initializer, CD_PROP_INT32);
}
bool exists(const GeometryComponent &component) const final
@ -1064,6 +1168,10 @@ template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttribu
return false;
}
if (!curve->splines().first()->attributes.get_for_read(name_)) {
return false;
}
bool has_point = false;
for (const SplinePtr &spline : curve->splines()) {
if (spline->size() != 0) {
@ -1090,6 +1198,8 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo
PositionAttributeProvider()
: BuiltinPointAttributeProvider(
"position",
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::NonDeletable,
[](const Spline &spline) { return spline.positions(); },
[](Spline &spline) { return spline.positions(); },
[](Spline &spline) { spline.mark_cache_invalid(); })
@ -1319,39 +1429,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr) {
return false;
}
/* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
bool layer_freed = false;
for (SplinePtr &spline : curve->splines()) {
layer_freed = spline->attributes.remove(attribute_id);
}
return layer_freed;
}
static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
const CustomDataType data_type,
const int total_size)
{
switch (initializer.type) {
case AttributeInit::Type::Default:
/* This function shouldn't be called in this case, since there
* is no need to copy anything to the new custom data array. */
BLI_assert_unreachable();
return {};
case AttributeInit::Type::VArray:
return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
case AttributeInit::Type::MoveArray:
return std::make_unique<fn::GVArray_For_GSpan>(
GSpan(*bke::custom_data_type_to_cpp_type(data_type),
static_cast<const AttributeInitMove &>(initializer).data,
total_size));
}
BLI_assert_unreachable();
return {};
return remove_point_attribute(component, attribute_id);
}
bool try_create(GeometryComponent &component,
@ -1364,55 +1442,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
if (domain != ATTR_DOMAIN_POINT) {
return false;
}
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
MutableSpan<SplinePtr> splines = curve->splines();
/* First check the one case that allows us to avoid copying the input data. */
if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) {
MEM_freeN(source_data);
return false;
}
return true;
}
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create(attribute_id, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
BLI_assert_unreachable();
return false;
}
}
/* With a default initializer type, we can keep the values at their initial values. */
if (initializer.type == AttributeInit::Type::Default) {
return true;
}
WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
const int total_size = curve->control_point_offsets().last();
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
}
return true;
return create_point_attribute(component, attribute_id, initializer, data_type);
}
bool foreach_attribute(const GeometryComponent &component,
@ -1487,14 +1517,32 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BezierHandleAttributeProvider handles_start(false);
static BezierHandleAttributeProvider handles_end(true);
static BuiltinPointAttributeProvider<int> id(
"id",
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
[](const Spline &spline) {
std::optional<GSpan> span = spline.attributes.get_for_read("id");
return span ? span->typed<int>() : Span<int>();
},
[](Spline &spline) {
std::optional<GMutableSpan> span = spline.attributes.get_for_write("id");
return span ? span->typed<int>() : MutableSpan<int>();
},
{});
static BuiltinPointAttributeProvider<float> radius(
"radius",
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::NonDeletable,
[](const Spline &spline) { return spline.radii(); },
[](Spline &spline) { return spline.radii(); },
nullptr);
static BuiltinPointAttributeProvider<float> tilt(
"tilt",
BuiltinAttributeProvider::NonCreatable,
BuiltinAttributeProvider::NonDeletable,
[](const Spline &spline) { return spline.tilts(); },
[](Spline &spline) { return spline.tilts(); },
[](Spline &spline) { spline.mark_cache_invalid(); });
@ -1502,7 +1550,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static DynamicPointAttributeProvider point_custom_data;
return ComponentAttributeProviders(
{&position, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
{&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
{&spline_custom_data, &point_custom_data});
}

View File

@ -912,6 +912,19 @@ static GVMutableArrayPtr make_derived_write_attribute(void *data, const int doma
MutableSpan<StructT>((StructT *)data, domain_size));
}
template<typename T>
static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
template<typename T>
static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
MutableSpan<T>((T *)data, domain_size));
}
static float3 get_vertex_position(const MVert &vert)
{
return float3(vert.co);
@ -1274,6 +1287,18 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static NormalAttributeProvider normal;
static BuiltinCustomDataLayerProvider id("id",
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
nullptr);
static BuiltinCustomDataLayerProvider material_index(
"material_index",
ATTR_DOMAIN_FACE,
@ -1335,14 +1360,15 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access);
return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease},
{&uvs,
&vertex_colors,
&corner_custom_data,
&vertex_groups,
&point_custom_data,
&edge_custom_data,
&face_custom_data});
return ComponentAttributeProviders(
{&position, &id, &material_index, &shade_smooth, &normal, &crease},
{&uvs,
&vertex_colors,
&corner_custom_data,
&vertex_groups,
&point_custom_data,
&edge_custom_data,
&face_custom_data});
}
} // namespace blender::bke

View File

@ -202,8 +202,19 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
make_array_read_attribute<float>,
make_array_write_attribute<float>,
nullptr);
static BuiltinCustomDataLayerProvider id("id",
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
point_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data});
}
} // namespace blender::bke

View File

@ -418,6 +418,8 @@ class IndexFieldInput final : public FieldInput {
public:
IndexFieldInput();
static GVArray *get_index_varray(IndexMask mask, ResourceScope &scope);
const GVArray *get_varray_for_context(const FieldContext &context,
IndexMask mask,
ResourceScope &scope) const final;

View File

@ -519,15 +519,20 @@ IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
{
}
GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope)
{
auto index_func = [](int i) { return i; };
return &scope.construct<
fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
mask.min_array_size(), mask.min_array_size(), index_func);
}
const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context),
IndexMask mask,
ResourceScope &scope) const
{
/* TODO: Investigate a similar method to IndexRange::as_span() */
auto index_func = [](int i) { return i; };
return &scope.construct<
fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
mask.min_array_size(), mask.min_array_size(), index_func);
return get_index_varray(mask, scope);
}
uint64_t IndexFieldInput::hash() const

View File

@ -335,8 +335,8 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
}
const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()];
if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) {
const bNode &bnode = *socket.bnode();
if (socket.typeinfo()->type == SOCK_VECTOR) {
const bNode &bnode = *socket.bnode();
if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) {
StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode ==
GEO_NODE_CURVE_HANDLE_LEFT ?
@ -349,6 +349,10 @@ static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
return true;
}
if (socket.typeinfo()->type == SOCK_INT) {
if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
new (r_value) Field<int>(std::make_shared<bke::IDAttributeFieldInput>());
return true;
}
new (r_value) Field<int>(std::make_shared<fn::IndexFieldInput>());
return true;
}

View File

@ -58,7 +58,6 @@ static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBui
b.add_output<decl::Geometry>("Points");
b.add_output<decl::Vector>("Normal").field_source();
b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
b.add_output<decl::Int>("Stable ID").field_source();
}
static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
@ -329,7 +328,6 @@ namespace {
struct AttributeOutputs {
StrongAnonymousAttributeID normal_id;
StrongAnonymousAttributeID rotation_id;
StrongAnonymousAttributeID stable_id_id;
};
} // namespace
@ -339,19 +337,16 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
OutputAttribute_Typed<int> id_attribute;
OutputAttribute_Typed<int> id_attribute = point_component.attribute_try_get_for_output_only<int>(
"id", ATTR_DOMAIN_POINT);
MutableSpan<int> ids = id_attribute.as_span();
OutputAttribute_Typed<float3> normal_attribute;
OutputAttribute_Typed<float3> rotation_attribute;
MutableSpan<int> ids;
MutableSpan<float3> normals;
MutableSpan<float3> rotations;
if (attribute_outputs.stable_id_id) {
id_attribute = point_component.attribute_try_get_for_output_only<int>(
attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT);
ids = id_attribute.as_span();
}
if (attribute_outputs.normal_id) {
normal_attribute = point_component.attribute_try_get_for_output_only<float3>(
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
@ -379,9 +374,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
if (!ids.is_empty()) {
ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
}
ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
float3 normal;
if (!normals.is_empty() || !rotations.is_empty()) {
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
@ -394,9 +388,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
}
}
if (id_attribute) {
id_attribute.save();
}
id_attribute.save();
if (normal_attribute) {
normal_attribute.save();
}
@ -512,6 +505,10 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
}
if (positions.is_empty()) {
return;
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
@ -551,9 +548,6 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
if (params.output_is_required("Rotation")) {
attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
}
if (params.output_is_required("Stable ID")) {
attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id");
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
point_distribution_calculate(
@ -575,11 +569,6 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
"Rotation",
AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
}
if (attribute_outputs.stable_id_id) {
params.set_output(
"Stable ID",
AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id)));
}
}
} // namespace blender::nodes

View File

@ -48,11 +48,6 @@ static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b)
.subtype(PROP_XYZ)
.supports_field()
.description("Scale of the instances");
b.add_input<decl::Int>("Stable ID")
.supports_field()
.description(
"ID for every instance that is used to identify it over time even when the number of "
"instances changes. Used for example for motion blur");
b.add_output<decl::Geometry>("Instances");
}
@ -91,12 +86,11 @@ static void add_instances_from_component(InstancesComponent &dst_component,
const VArray<float3> *scales = nullptr;
/* The evaluator could use the component's stable IDs as a destination directly, but only the
* selected indices should be copied. */
const VArray<int> *stable_ids = nullptr;
GVArray_Typed<int> stable_ids = src_component.attribute_get_for_read("id", ATTR_DOMAIN_POINT, 0);
field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
field_evaluator.add(params.get_input<Field<int>>("Stable ID"), &stable_ids);
field_evaluator.evaluate();
GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>(