Geometry Nodes: support grease pencil in Join Geometry and Realize Instances nodes #124361
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user
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