Geometry Nodes: support instancing collections
The Point Instance node can instance entire collections now. Before, only individual collections were supported. Randomly selecting objects from the collection on a per point basis is not support, yet. Last part of D9739. Ref T82372.
This commit is contained in:
@@ -26,16 +26,30 @@ extern "C" {
|
||||
|
||||
struct Object;
|
||||
struct GeometrySet;
|
||||
struct Collection;
|
||||
|
||||
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
|
||||
|
||||
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
|
||||
|
||||
typedef enum InstancedDataType {
|
||||
INSTANCE_DATA_TYPE_OBJECT = 0,
|
||||
INSTANCE_DATA_TYPE_COLLECTION = 1,
|
||||
} InstancedDataType;
|
||||
|
||||
typedef struct InstancedData {
|
||||
InstancedDataType type;
|
||||
union {
|
||||
struct Object *object;
|
||||
struct Collection *collection;
|
||||
} data;
|
||||
} InstancedData;
|
||||
|
||||
int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
|
||||
float (**r_positions)[3],
|
||||
float (**r_rotations)[3],
|
||||
float (**r_scales)[3],
|
||||
struct Object ***r_objects);
|
||||
struct InstancedData **r_instanced_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -35,6 +35,7 @@
|
||||
struct Mesh;
|
||||
struct PointCloud;
|
||||
struct Object;
|
||||
struct Collection;
|
||||
|
||||
/* Each geometry component has a specific type. The type determines what kind of data the component
|
||||
* stores. Functions modifying a geometry will usually just modify a subset of the component types.
|
||||
@@ -358,7 +359,7 @@ class InstancesComponent : public GeometryComponent {
|
||||
blender::Vector<blender::float3> positions_;
|
||||
blender::Vector<blender::float3> rotations_;
|
||||
blender::Vector<blender::float3> scales_;
|
||||
blender::Vector<const Object *> objects_;
|
||||
blender::Vector<InstancedData> instanced_data_;
|
||||
|
||||
public:
|
||||
InstancesComponent();
|
||||
@@ -366,12 +367,20 @@ class InstancesComponent : public GeometryComponent {
|
||||
GeometryComponent *copy() const override;
|
||||
|
||||
void clear();
|
||||
void add_instance(const Object *object,
|
||||
void add_instance(Object *object,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation = {0, 0, 0},
|
||||
blender::float3 scale = {1, 1, 1});
|
||||
void add_instance(Collection *collection,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation = {0, 0, 0},
|
||||
blender::float3 scale = {1, 1, 1});
|
||||
void add_instance(InstancedData data,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation,
|
||||
blender::float3 scale);
|
||||
|
||||
blender::Span<const Object *> objects() const;
|
||||
blender::Span<InstancedData> instanced_data() const;
|
||||
blender::Span<blender::float3> positions() const;
|
||||
blender::Span<blender::float3> rotations() const;
|
||||
blender::Span<blender::float3> scales() const;
|
||||
|
@@ -460,31 +460,54 @@ GeometryComponent *InstancesComponent::copy() const
|
||||
new_component->positions_ = positions_;
|
||||
new_component->rotations_ = rotations_;
|
||||
new_component->scales_ = scales_;
|
||||
new_component->objects_ = objects_;
|
||||
new_component->instanced_data_ = instanced_data_;
|
||||
return new_component;
|
||||
}
|
||||
|
||||
void InstancesComponent::clear()
|
||||
{
|
||||
objects_.clear();
|
||||
instanced_data_.clear();
|
||||
positions_.clear();
|
||||
rotations_.clear();
|
||||
scales_.clear();
|
||||
}
|
||||
void InstancesComponent::add_instance(const Object *object,
|
||||
|
||||
void InstancesComponent::add_instance(Object *object,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation,
|
||||
blender::float3 scale)
|
||||
{
|
||||
objects_.append(object);
|
||||
InstancedData data;
|
||||
data.type = INSTANCE_DATA_TYPE_OBJECT;
|
||||
data.data.object = object;
|
||||
this->add_instance(data, position, rotation, scale);
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(Collection *collection,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation,
|
||||
blender::float3 scale)
|
||||
{
|
||||
InstancedData data;
|
||||
data.type = INSTANCE_DATA_TYPE_COLLECTION;
|
||||
data.data.collection = collection;
|
||||
this->add_instance(data, position, rotation, scale);
|
||||
}
|
||||
|
||||
void InstancesComponent::add_instance(InstancedData data,
|
||||
blender::float3 position,
|
||||
blender::float3 rotation,
|
||||
blender::float3 scale)
|
||||
{
|
||||
instanced_data_.append(data);
|
||||
positions_.append(position);
|
||||
rotations_.append(rotation);
|
||||
scales_.append(scale);
|
||||
}
|
||||
|
||||
Span<const Object *> InstancesComponent::objects() const
|
||||
Span<InstancedData> InstancesComponent::instanced_data() const
|
||||
{
|
||||
return objects_;
|
||||
return instanced_data_;
|
||||
}
|
||||
|
||||
Span<float3> InstancesComponent::positions() const
|
||||
@@ -509,8 +532,11 @@ MutableSpan<float3> InstancesComponent::positions()
|
||||
|
||||
int InstancesComponent::instances_amount() const
|
||||
{
|
||||
BLI_assert(positions_.size() == objects_.size());
|
||||
return objects_.size();
|
||||
const int size = instanced_data_.size();
|
||||
BLI_assert(positions_.size() == size);
|
||||
BLI_assert(rotations_.size() == size);
|
||||
BLI_assert(scales_.size() == size);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool InstancesComponent::is_empty() const
|
||||
@@ -538,7 +564,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set,
|
||||
float (**r_positions)[3],
|
||||
float (**r_rotations)[3],
|
||||
float (**r_scales)[3],
|
||||
Object ***r_objects)
|
||||
InstancedData **r_instanced_data)
|
||||
{
|
||||
const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
|
||||
if (component == nullptr) {
|
||||
@@ -547,7 +573,7 @@ int BKE_geometry_set_instances(const GeometrySet *geometry_set,
|
||||
*r_positions = (float(*)[3])component->positions().data();
|
||||
*r_rotations = (float(*)[3])component->rotations().data();
|
||||
*r_scales = (float(*)[3])component->scales().data();
|
||||
*r_objects = (Object **)component->objects().data();
|
||||
*r_instanced_data = (InstancedData *)component->instanced_data().data();
|
||||
return component->instances_amount();
|
||||
}
|
||||
|
||||
|
@@ -816,15 +816,13 @@ static void make_duplis_instances_component(const DupliContext *ctx)
|
||||
float(*positions)[3];
|
||||
float(*rotations)[3];
|
||||
float(*scales)[3];
|
||||
Object **objects;
|
||||
InstancedData *instanced_data;
|
||||
const int amount = BKE_geometry_set_instances(
|
||||
ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &objects);
|
||||
ctx->object->runtime.geometry_set_eval, &positions, &rotations, &scales, &instanced_data);
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Object *object = objects[i];
|
||||
if (object == NULL) {
|
||||
continue;
|
||||
}
|
||||
InstancedData *data = &instanced_data[i];
|
||||
|
||||
float scale_matrix[4][4];
|
||||
size_to_mat4(scale_matrix, scales[i]);
|
||||
float rotation_matrix[4][4];
|
||||
@@ -833,14 +831,43 @@ static void make_duplis_instances_component(const DupliContext *ctx)
|
||||
mul_m4_m4m4(instance_offset_matrix, rotation_matrix, scale_matrix);
|
||||
copy_v3_v3(instance_offset_matrix[3], positions[i]);
|
||||
|
||||
float matrix[4][4];
|
||||
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix);
|
||||
make_dupli(ctx, object, matrix, i);
|
||||
if (data->type == INSTANCE_DATA_TYPE_OBJECT) {
|
||||
Object *object = data->data.object;
|
||||
if (object != NULL) {
|
||||
float matrix[4][4];
|
||||
mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrix);
|
||||
make_dupli(ctx, object, matrix, i);
|
||||
|
||||
float space_matrix[4][4];
|
||||
mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat);
|
||||
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
|
||||
make_recursive_duplis(ctx, object, space_matrix, i);
|
||||
float space_matrix[4][4];
|
||||
mul_m4_m4m4(space_matrix, instance_offset_matrix, object->imat);
|
||||
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
|
||||
make_recursive_duplis(ctx, object, space_matrix, i);
|
||||
}
|
||||
}
|
||||
else if (data->type == INSTANCE_DATA_TYPE_COLLECTION) {
|
||||
Collection *collection = data->data.collection;
|
||||
if (collection != NULL) {
|
||||
float collection_matrix[4][4];
|
||||
unit_m4(collection_matrix);
|
||||
sub_v3_v3(collection_matrix[3], collection->instance_offset);
|
||||
mul_m4_m4_pre(collection_matrix, instance_offset_matrix);
|
||||
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
|
||||
|
||||
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
|
||||
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
|
||||
if (object == ctx->object) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float instance_matrix[4][4];
|
||||
mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat);
|
||||
|
||||
make_dupli(ctx, object, instance_matrix, i);
|
||||
make_recursive_duplis(ctx, object, collection_matrix, i);
|
||||
}
|
||||
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3182,6 +3182,13 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
|
||||
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_buts_point_instance(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "instance_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_buts_attribute_fill(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
@@ -3219,6 +3226,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
case GEO_NODE_ATTRIBUTE_MATH:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_math;
|
||||
break;
|
||||
case GEO_NODE_POINT_INSTANCE:
|
||||
ntype->draw_buttons = node_geometry_buts_point_instance;
|
||||
break;
|
||||
case GEO_NODE_ATTRIBUTE_FILL:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_fill;
|
||||
break;
|
||||
|
@@ -1480,6 +1480,11 @@ typedef enum GeometryNodeUseAttributeFlag {
|
||||
GEO_NODE_USE_ATTRIBUTE_B = (1 << 1),
|
||||
} GeometryNodeUseAttributeFlag;
|
||||
|
||||
typedef enum GeometryNodePointInstanceType {
|
||||
GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0,
|
||||
GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1,
|
||||
} GeometryNodePointInstanceType;
|
||||
|
||||
typedef enum GeometryNodeAttributeInputMode {
|
||||
GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE = 0,
|
||||
GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1,
|
||||
|
@@ -8415,6 +8415,32 @@ static void def_geo_attribute_math(StructRNA *srna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_point_instance(StructRNA *srna)
|
||||
{
|
||||
static const EnumPropertyItem instance_type_items[] = {
|
||||
{GEO_NODE_POINT_INSTANCE_TYPE_OBJECT,
|
||||
"OBJECT",
|
||||
ICON_NONE,
|
||||
"Object",
|
||||
"Instance an individual object on all points"},
|
||||
{GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION,
|
||||
"COLLECTION",
|
||||
ICON_NONE,
|
||||
"Collection",
|
||||
"Instance an entire collection on all points"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "custom1");
|
||||
RNA_def_property_enum_items(prop, instance_type_items);
|
||||
RNA_def_property_enum_default(prop, GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
|
||||
RNA_def_property_ui_text(prop, "Instance Type", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_geo_attribute_mix(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
@@ -272,7 +272,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform"
|
||||
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
|
||||
DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
|
||||
DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, 0, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
|
||||
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, 0, "POINT_INSTANCE", PointInstance, "Point Instance", "")
|
||||
DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
|
||||
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "")
|
||||
DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RANDOM_ATTRIBUTE", RandomAttribute, "Random Attribute", "")
|
||||
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
|
||||
|
@@ -218,12 +218,12 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
|
||||
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
|
||||
for (const InstancesComponent *component : src_components) {
|
||||
const int size = component->instances_amount();
|
||||
Span<const Object *> objects = component->objects();
|
||||
Span<InstancedData> instanced_data = component->instanced_data();
|
||||
Span<float3> positions = component->positions();
|
||||
Span<float3> rotations = component->rotations();
|
||||
Span<float3> scales = component->scales();
|
||||
for (const int i : IndexRange(size)) {
|
||||
dst_component.add_instance(objects[i], positions[i], rotations[i], scales[i]);
|
||||
dst_component.add_instance(instanced_data[i], positions[i], rotations[i], scales[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
static bNodeSocketTemplate geo_node_point_instance_in[] = {
|
||||
{SOCK_GEOMETRY, N_("Geometry")},
|
||||
{SOCK_OBJECT, N_("Object")},
|
||||
{SOCK_COLLECTION, N_("Collection")},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -35,9 +36,21 @@ static bNodeSocketTemplate geo_node_point_instance_out[] = {
|
||||
|
||||
namespace blender::nodes {
|
||||
|
||||
static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
|
||||
{
|
||||
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
|
||||
bNodeSocket *collection_socket = object_socket->next;
|
||||
|
||||
GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node->custom1;
|
||||
|
||||
nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
|
||||
nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
|
||||
}
|
||||
|
||||
static void add_instances_from_geometry_component(InstancesComponent &instances,
|
||||
const GeometryComponent &src_geometry,
|
||||
Object *object)
|
||||
Object *object,
|
||||
Collection *collection)
|
||||
{
|
||||
Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
|
||||
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
|
||||
@@ -47,30 +60,51 @@ static void add_instances_from_geometry_component(InstancesComponent &instances,
|
||||
"scale", ATTR_DOMAIN_POINT, {1, 1, 1});
|
||||
|
||||
for (const int i : IndexRange(positions.size())) {
|
||||
instances.add_instance(object, positions[i], rotations[i], scales[i]);
|
||||
if (object != nullptr) {
|
||||
instances.add_instance(object, positions[i], rotations[i], scales[i]);
|
||||
}
|
||||
if (collection != nullptr) {
|
||||
instances.add_instance(collection, positions[i], rotations[i], scales[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void geo_node_point_instance_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)params.node().custom1;
|
||||
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
|
||||
GeometrySet geometry_set_out;
|
||||
|
||||
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
|
||||
"Object");
|
||||
Object *object = params.handle_map().lookup(object_handle);
|
||||
Object *object = nullptr;
|
||||
Collection *collection = nullptr;
|
||||
|
||||
if (object != nullptr && object != params.self_object()) {
|
||||
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
add_instances_from_geometry_component(
|
||||
instances, *geometry_set.get_component_for_read<MeshComponent>(), object);
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
add_instances_from_geometry_component(
|
||||
instances, *geometry_set.get_component_for_read<PointCloudComponent>(), object);
|
||||
if (type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT) {
|
||||
bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
|
||||
"Object");
|
||||
object = params.handle_map().lookup(object_handle);
|
||||
/* Avoid accidental recursion of instances. */
|
||||
if (object == params.self_object()) {
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
else if (type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) {
|
||||
bke::PersistentCollectionHandle collection_handle =
|
||||
params.extract_input<bke::PersistentCollectionHandle>("Collection");
|
||||
collection = params.handle_map().lookup(collection_handle);
|
||||
}
|
||||
|
||||
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
|
||||
if (geometry_set.has<MeshComponent>()) {
|
||||
add_instances_from_geometry_component(
|
||||
instances, *geometry_set.get_component_for_read<MeshComponent>(), object, collection);
|
||||
}
|
||||
if (geometry_set.has<PointCloudComponent>()) {
|
||||
add_instances_from_geometry_component(
|
||||
instances,
|
||||
*geometry_set.get_component_for_read<PointCloudComponent>(),
|
||||
object,
|
||||
collection);
|
||||
}
|
||||
|
||||
params.set_output("Geometry", std::move(geometry_set_out));
|
||||
}
|
||||
@@ -82,6 +116,7 @@ void register_node_type_geo_point_instance()
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0);
|
||||
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
|
||||
node_type_update(&ntype, blender::nodes::geo_node_point_instance_update);
|
||||
ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
Reference in New Issue
Block a user