Geometry Nodes: support grease pencil in Join Geometry and Realize Instances nodes #124361

Merged
Jacques Lucke merged 11 commits from JacquesLucke/blender:grease-pencil-realize-instances into main 2024-07-11 13:49:10 +02:00
2 changed files with 270 additions and 13 deletions

View File

@ -195,12 +195,14 @@ GeometrySet join_geometries(const Span<GeometrySet> geometries,
{
GeometrySet result;
result.name = geometries.is_empty() ? "" : geometries[0].name;
static const Array<GeometryComponent::Type> supported_types({GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Instance,
GeometryComponent::Type::Volume,
GeometryComponent::Type::Curve,
GeometryComponent::Type::Edit});
static const Array<GeometryComponent::Type> supported_types(
{GeometryComponent::Type::Mesh,
GeometryComponent::Type::PointCloud,
GeometryComponent::Type::Instance,
GeometryComponent::Type::Volume,
GeometryComponent::Type::Curve,
GeometryComponent::Type::GreasePencil,
GeometryComponent::Type::Edit});
for (const GeometryComponent::Type type : supported_types) {
join_component_type(type, geometries, propagation_info, result);
}

View File

@ -15,6 +15,7 @@
#include "BKE_deform.hh"
#include "BKE_geometry_nodes_gizmos_transforms.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.hh"
@ -177,6 +178,22 @@ struct RealizeCurveTask {
uint32_t id = 0;
};
struct GreasePencilRealizeInfo {
const GreasePencil *grease_pencil = nullptr;
/** Matches the order in #AllGreasePencilsInfo.attributes. */
Array<std::optional<GVArraySpan>> attributes;
/** Maps old material indices to new material indices. */
Array<int> material_index_map;
};
struct RealizeGreasePencilTask {
/** Index where the first layer is realized in the final grease pencil. */
int start_index;
const GreasePencilRealizeInfo *grease_pencil_info;
float4x4 transform;
AttributeFallbacksArray attribute_fallbacks;
};
struct RealizeEditDataTask {
const bke::GeometryComponentEditData *edit_data;
float4x4 transform;
@ -226,6 +243,17 @@ struct AllCurvesInfo {
bool create_custom_normal_attribute = false;
};
struct AllGreasePencilsInfo {
/** Ordering of all attributes that are propagated to the output grease pencil generically. */
OrderedAttributes attributes;
/** Ordering of the original grease pencils that are joined. */
VectorSet<const GreasePencil *> order;
/** Preprocessed data about every original grease pencil. This is ordered by #order. */
Array<GreasePencilRealizeInfo> realize_info;
/** Ordered materials on the output grease pencil. */
VectorSet<Material *> materials;
};
struct AllInstancesInfo {
/** Stores an array of void pointer to attributes for each component. */
Vector<AttributeFallbacksArray> attribute_fallback;
@ -240,6 +268,7 @@ struct GatherTasks {
Vector<RealizePointCloudTask> pointcloud_tasks;
Vector<RealizeMeshTask> mesh_tasks;
Vector<RealizeCurveTask> curve_tasks;
Vector<RealizeGreasePencilTask> grease_pencil_tasks;
Vector<RealizeEditDataTask> edit_data_tasks;
/* Volumes only have very simple support currently. Only the first found volume is put into the
@ -252,6 +281,7 @@ struct GatherOffsets {
int pointcloud_offset = 0;
MeshElementStartIndices mesh_offsets;
CurvesElementStartIndices curves_offsets;
int grease_pencil_layer_offset = 0;
};
struct GatherTasksInfo {
@ -259,6 +289,7 @@ struct GatherTasksInfo {
const AllPointCloudsInfo &pointclouds;
const AllMeshesInfo &meshes;
const AllCurvesInfo &curves;
const AllGreasePencilsInfo &grease_pencils;
const OrderedAttributes &instances_attriubutes;
bool create_id_attribute_on_any_component = false;
@ -294,6 +325,8 @@ struct InstanceContext {
AttributeFallbacksArray meshes;
/** Ordered by #AllCurvesInfo.attributes. */
AttributeFallbacksArray curves;
/** Ordered by #AllGreasePencilsInfo.attributes. */
AttributeFallbacksArray grease_pencils;
/** Ordered by #AllInstancesInfo.attributes. */
AttributeFallbacksArray instances;
/** Id mixed from all parent instances. */
@ -303,6 +336,7 @@ struct InstanceContext {
: pointclouds(gather_info.pointclouds.attributes.size()),
meshes(gather_info.meshes.attributes.size()),
curves(gather_info.curves.attributes.size()),
grease_pencils(gather_info.grease_pencils.attributes.size()),
instances(gather_info.instances_attriubutes.size())
{
// empty
@ -531,6 +565,8 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
gather_info, instances, gather_info.meshes.attributes);
Vector<std::pair<int, GSpan>> curve_attributes_to_override = prepare_attribute_fallbacks(
gather_info, instances, gather_info.curves.attributes);
Vector<std::pair<int, GSpan>> grease_pencil_attributes_to_override = prepare_attribute_fallbacks(
gather_info, instances, gather_info.grease_pencils.attributes);
Vector<std::pair<int, GSpan>> instance_attributes_to_override = prepare_attribute_fallbacks(
gather_info, instances, gather_info.instances_attriubutes);
@ -556,6 +592,9 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info,
for (const std::pair<int, GSpan> &pair : curve_attributes_to_override) {
instance_context.curves.array[pair.first] = pair.second[i];
}
for (const std::pair<int, GSpan> &pair : grease_pencil_attributes_to_override) {
instance_context.grease_pencils.array[pair.first] = pair.second[i];
}
for (const std::pair<int, GSpan> &pair : instance_attributes_to_override) {
instance_context.instances.array[pair.first] = pair.second[i];
}
@ -652,6 +691,23 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
}
break;
}
case bke::GeometryComponent::Type::GreasePencil: {
const auto &grease_pencil_component = *static_cast<const bke::GreasePencilComponent *>(
component);
const GreasePencil *grease_pencil = grease_pencil_component.get();
if (grease_pencil != nullptr && !grease_pencil->layers().is_empty()) {
const int grease_pencil_index = gather_info.grease_pencils.order.index_of(grease_pencil);
const GreasePencilRealizeInfo &grease_pencil_info =
gather_info.grease_pencils.realize_info[grease_pencil_index];
gather_info.r_tasks.grease_pencil_tasks.append(
{gather_info.r_offsets.grease_pencil_layer_offset,
&grease_pencil_info,
base_transform,
base_instance_context.grease_pencils});
gather_info.r_offsets.grease_pencil_layer_offset += grease_pencil->layers().size();
}
break;
}
case bke::GeometryComponent::Type::Instance: {
if (current_depth == target_depth) {
gather_info.instances.attribute_fallback.append(base_instance_context.instances);
@ -690,10 +746,6 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
}
break;
}
case bke::GeometryComponent::Type::GreasePencil: {
/* TODO. Do nothing for now. */
break;
}
}
}
}
@ -1890,6 +1942,200 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
/** \} */
/* -------------------------------------------------------------------- */
/** \name Grease Pencil
* \{ */
static OrderedAttributes gather_generic_grease_pencil_attributes_to_propagate(
const bke::GeometrySet &in_geometry_set,
const RealizeInstancesOptions &options,
const VariedDepthOptions &varied_depth_options)
{
Vector<bke::GeometryComponent::Type> src_component_types;
src_component_types.append(bke::GeometryComponent::Type::GreasePencil);
if (options.realize_instance_attributes) {
src_component_types.append(bke::GeometryComponent::Type::Instance);
}
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
gather_attributes_for_propagation(in_geometry_set,
src_component_types,
bke::GeometryComponent::Type::GreasePencil,
varied_depth_options.depths,
varied_depth_options.selection,
options.propagation_info,
attributes_to_propagate);
OrderedAttributes ordered_attributes;
for (auto &&item : attributes_to_propagate.items()) {
ordered_attributes.ids.add_new(item.key);
ordered_attributes.kinds.append(item.value);
}
return ordered_attributes;
}
static void gather_grease_pencils_to_realize(const bke::GeometrySet &geometry_set,
VectorSet<const GreasePencil *> &r_grease_pencils)
{
if (const GreasePencil *grease_pencil = geometry_set.get_grease_pencil()) {
if (!grease_pencil->layers().is_empty()) {
r_grease_pencils.add(grease_pencil);
}
}
if (const Instances *instances = geometry_set.get_instances()) {
instances->foreach_referenced_geometry([&](const bke::GeometrySet &instance_geometry_set) {
gather_grease_pencils_to_realize(instance_geometry_set, r_grease_pencils);
});
}
}
static AllGreasePencilsInfo preprocess_grease_pencils(
const bke::GeometrySet &geometry_set,
const RealizeInstancesOptions &options,
const VariedDepthOptions &varied_depth_options)
{
AllGreasePencilsInfo info;
info.attributes = gather_generic_grease_pencil_attributes_to_propagate(
geometry_set, options, varied_depth_options);
gather_grease_pencils_to_realize(geometry_set, info.order);
info.realize_info.reinitialize(info.order.size());
for (const int grease_pencil_index : info.realize_info.index_range()) {
GreasePencilRealizeInfo &grease_pencil_info = info.realize_info[grease_pencil_index];
const GreasePencil *grease_pencil = info.order[grease_pencil_index];
grease_pencil_info.grease_pencil = grease_pencil;
bke::AttributeAccessor attributes = grease_pencil->attributes();
grease_pencil_info.attributes.reinitialize(info.attributes.size());
for (const int attribute_index : info.attributes.index_range()) {
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
const eCustomDataType data_type = info.attributes.kinds[attribute_index].data_type;
const bke::AttrDomain domain = info.attributes.kinds[attribute_index].domain;
if (attributes.contains(attribute_id)) {
GVArray attribute = *attributes.lookup_or_default(attribute_id, domain, data_type);
grease_pencil_info.attributes[attribute_index].emplace(std::move(attribute));
}
}
grease_pencil_info.material_index_map.reinitialize(grease_pencil->material_array_num);
for (const int i : IndexRange(grease_pencil->material_array_num)) {
Material *material = grease_pencil->material_array[i];
grease_pencil_info.material_index_map[i] = info.materials.index_of_or_add(material);
}
}
return info;
}
static void execute_realize_grease_pencil_task(
const RealizeGreasePencilTask &task,
const OrderedAttributes &ordered_attributes,
GreasePencil &dst_grease_pencil,
MutableSpan<GSpanAttributeWriter> dst_attribute_writers)
{
const GreasePencilRealizeInfo &grease_pencil_info = *task.grease_pencil_info;
const GreasePencil &src_grease_pencil = *grease_pencil_info.grease_pencil;
const Span<const bke::greasepencil::Layer *> src_layers = src_grease_pencil.layers();
const IndexRange dst_layers_slice{task.start_index, src_layers.size()};
const Span<bke::greasepencil::Layer *> dst_layers = dst_grease_pencil.layers_for_write().slice(
dst_layers_slice);
for (const int layer_i : src_layers.index_range()) {
const bke::greasepencil::Layer &src_layer = *src_layers[layer_i];
bke::greasepencil::Layer &dst_layer = *dst_layers[layer_i];
dst_layer.set_local_transform(task.transform * src_layer.local_transform());
const bke::greasepencil::Drawing *src_drawing = src_grease_pencil.get_eval_drawing(src_layer);
if (!src_drawing) {
continue;
}
bke::greasepencil::Drawing &dst_drawing = *dst_grease_pencil.get_eval_drawing(dst_layer);
const bke::CurvesGeometry &src_curves = src_drawing->strokes();
bke::CurvesGeometry &dst_curves = dst_drawing.strokes_for_write();
JacquesLucke marked this conversation as resolved Outdated

Might as well separate bke::MutableAttributeAccessor attributes to a separate line for prettier wrapping and so it's easier to add something else where in the future

Might as well separate `bke::MutableAttributeAccessor attributes` to a separate line for prettier wrapping and so it's easier to add something else where in the future
dst_curves = src_curves;
/* Remap materials. */
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
bke::SpanAttributeWriter<int> material_indices =
dst_attributes.lookup_or_add_for_write_span<int>("material_index", bke::AttrDomain::Curve);
for (int &material_index : material_indices.span) {
if (material_index >= 0 && material_index < src_grease_pencil.material_array_num) {
material_index = grease_pencil_info.material_index_map[material_index];
}
}
material_indices.finish();
}
copy_generic_attributes_to_result(
grease_pencil_info.attributes,
task.attribute_fallbacks,
ordered_attributes,
[&](const bke::AttrDomain domain) {
BLI_assert(domain == bke::AttrDomain::Layer);
UNUSED_VARS_NDEBUG(domain);
return dst_layers_slice;
},
dst_attribute_writers);
}
static void execute_realize_grease_pencil_tasks(
const AllGreasePencilsInfo &all_grease_pencils_info,
const Span<RealizeGreasePencilTask> tasks,
const OrderedAttributes &ordered_attributes,
bke::GeometrySet &r_realized_geometry)
{
if (tasks.is_empty()) {
return;
}
/* Allocate new grease pencil. */
GreasePencil *dst_grease_pencil = BKE_grease_pencil_new_nomain();
r_realized_geometry.replace_grease_pencil(dst_grease_pencil);
/* Prepare layer names. This is currently quadratic in the number of layers because layer names
* are made unique. */
for (const RealizeGreasePencilTask &task : tasks) {
const GreasePencil &src_grease_pencil = *task.grease_pencil_info->grease_pencil;
for (const bke::greasepencil::Layer *src_layer : src_grease_pencil.layers()) {
bke::greasepencil::Layer &dst_layer = dst_grease_pencil->add_layer(src_layer->name());
dst_grease_pencil->insert_frame(dst_layer, dst_grease_pencil->runtime->eval_frame);
}
}
/* Transfer material pointers. The material indices are updated for each task separately. */
if (!all_grease_pencils_info.materials.is_empty()) {
dst_grease_pencil->material_array_num = all_grease_pencils_info.materials.size();
dst_grease_pencil->material_array = MEM_cnew_array<Material *>(
dst_grease_pencil->material_array_num, __func__);
uninitialized_copy_n(all_grease_pencils_info.materials.data(),
dst_grease_pencil->material_array_num,
dst_grease_pencil->material_array);
}
/* Prepare generic output attributes. */
bke::MutableAttributeAccessor dst_attributes = dst_grease_pencil->attributes_for_write();
Vector<GSpanAttributeWriter> dst_attribute_writers;
for (const int attribute_index : ordered_attributes.index_range()) {
const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index];
const eCustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type;
dst_attribute_writers.append(dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, bke::AttrDomain::Point, data_type));
}
/* Actually execute all tasks. */
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
for (const int task_index : task_range) {
const RealizeGreasePencilTask &task = tasks[task_index];
execute_realize_grease_pencil_task(
task, ordered_attributes, *dst_grease_pencil, dst_attribute_writers);
}
});
/* Tag modified attributes. */
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
dst_attribute.finish();
}
}
/* -------------------------------------------------------------------- */
/** \name Edit Data
* \{ */
@ -1900,6 +2146,7 @@ static void execute_realize_edit_data_tasks(const Span<RealizeEditDataTask> task
if (tasks.is_empty()) {
return;
}
auto &component = r_realized_geometry.get_component_for_write<bke::GeometryComponentEditData>();
for (const RealizeEditDataTask &task : tasks) {
if (!component.curves_edit_hints_) {
@ -1935,7 +2182,8 @@ static void remove_id_attribute_from_instances(bke::GeometrySet &geometry_set)
});
}
/** Propagate instances from the old geometry set to the new geometry set if they are not realized.
/** Propagate instances from the old geometry set to the new geometry set if they are not
* realized.
*/
static void propagate_instances_to_keep(
const bke::GeometrySet &geometry_set,
@ -1980,8 +2228,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
{
/* The algorithm works in three steps:
* 1. Preprocess each unique geometry that is instanced (e.g. each `Mesh`).
* 2. Gather "tasks" that need to be executed to realize the instances. Each task corresponds to
* instances of the previously preprocessed geometry.
* 2. Gather "tasks" that need to be executed to realize the instances. Each task corresponds
* to instances of the previously preprocessed geometry.
* 3. Execute all tasks in parallel.
*/
@ -2001,6 +2249,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
geometry_set, options, varied_depth_option);
AllMeshesInfo all_meshes_info = preprocess_meshes(geometry_set, options, varied_depth_option);
AllCurvesInfo all_curves_info = preprocess_curves(geometry_set, options, varied_depth_option);
AllGreasePencilsInfo all_grease_pencils_info = preprocess_grease_pencils(
geometry_set, options, varied_depth_option);
OrderedAttributes all_instance_attributes = gather_generic_instance_attributes_to_propagate(
geometry_set, options, varied_depth_option);
@ -2011,6 +2261,7 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
GatherTasksInfo gather_info = {all_pointclouds_info,
all_meshes_info,
all_curves_info,
all_grease_pencils_info,
all_instance_attributes,
create_id_attribute,
varied_depth_option.selection,
@ -2058,6 +2309,10 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
gather_info.r_tasks.curve_tasks,
all_curves_info.attributes,
new_geometry_set);
execute_realize_grease_pencil_tasks(all_grease_pencils_info,
gather_info.r_tasks.grease_pencil_tasks,
all_grease_pencils_info.attributes,
new_geometry_set);
execute_realize_edit_data_tasks(gather_info.r_tasks.edit_data_tasks, new_geometry_set);
});
if (gather_info.r_tasks.first_volume) {