From f5dc34ec9c05cc8f1163313baafe634f4798c29b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 11 Dec 2020 18:00:37 +0100 Subject: 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. --- source/blender/blenkernel/BKE_geometry_set.h | 16 +++++- source/blender/blenkernel/BKE_geometry_set.hh | 15 ++++-- source/blender/blenkernel/intern/geometry_set.cc | 46 ++++++++++++---- source/blender/blenkernel/intern/object_dupli.c | 53 +++++++++++++----- source/blender/editors/space_node/drawnode.c | 10 ++++ source/blender/makesdna/DNA_node_types.h | 5 ++ source/blender/makesrna/intern/rna_nodetree.c | 26 +++++++++ source/blender/nodes/NOD_static_types.h | 2 +- .../nodes/geometry/nodes/node_geo_join_geometry.cc | 4 +- .../geometry/nodes/node_geo_point_instance.cc | 63 +++++++++++++++++----- 10 files changed, 196 insertions(+), 44 deletions(-) (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index 026f4d39d51..87bb96be145 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -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 } diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index b7cb9320086..617e0c45ae4 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -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 positions_; blender::Vector rotations_; blender::Vector scales_; - blender::Vector objects_; + blender::Vector 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 objects() const; + blender::Span instanced_data() const; blender::Span positions() const; blender::Span rotations() const; blender::Span scales() const; diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 28695d769d3..e6a67b191f8 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -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) +{ + 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) { - objects_.append(object); + instanced_data_.append(data); positions_.append(position); rotations_.append(rotation); scales_.append(scale); } -Span InstancesComponent::objects() const +Span InstancesComponent::instanced_data() const { - return objects_; + return instanced_data_; } Span InstancesComponent::positions() const @@ -509,8 +532,11 @@ MutableSpan 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(); 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(); } diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index d5434710e23..8fa708f31cb 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -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); + } + } + 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); - 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); + make_dupli(ctx, object, instance_matrix, i); + make_recursive_duplis(ctx, object, collection_matrix, i); + } + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; + } + } } } diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index f1ab433c639..f8c71791054 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -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; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index e04867438f1..8e463a8b406 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -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, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 461b6af506a..34d922fe2c8 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -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; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index d1032647ba8..491f49cf425 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -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", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index dedc3213a1f..80ac45aed4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -218,12 +218,12 @@ static void join_components(Span src_components, Geo InstancesComponent &dst_component = result.get_component_for_write(); for (const InstancesComponent *component : src_components) { const int size = component->instances_amount(); - Span objects = component->objects(); + Span instanced_data = component->instanced_data(); Span positions = component->positions(); Span rotations = component->rotations(); Span 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]); } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 6d979e3b7da..e030bc3eec6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -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( "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("Geometry"); GeometrySet geometry_set_out; - bke::PersistentObjectHandle object_handle = params.extract_input( - "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(); - if (geometry_set.has()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read(), object); - } - if (geometry_set.has()) { - add_instances_from_geometry_component( - instances, *geometry_set.get_component_for_read(), object); + if (type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT) { + bke::PersistentObjectHandle object_handle = params.extract_input( + "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("Collection"); + collection = params.handle_map().lookup(collection_handle); + } + + InstancesComponent &instances = geometry_set_out.get_component_for_write(); + if (geometry_set.has()) { + add_instances_from_geometry_component( + instances, *geometry_set.get_component_for_read(), object, collection); + } + if (geometry_set.has()) { + add_instances_from_geometry_component( + instances, + *geometry_set.get_component_for_read(), + 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); } -- cgit v1.2.3