WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 370 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
24 changed files with 711 additions and 104 deletions
Showing only changes of commit ca8ce120e9 - Show all commits

View File

@ -162,6 +162,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu):
self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR') self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR')
self.operator_modifier_add(layout, 'GREASE_PENCIL_MULTIPLY') self.operator_modifier_add(layout, 'GREASE_PENCIL_MULTIPLY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_OUTLINE') self.operator_modifier_add(layout, 'GREASE_PENCIL_OUTLINE')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SIMPLIFY')
self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV') self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV')
self.operator_modifier_add(layout, 'LINEART') self.operator_modifier_add(layout, 'LINEART')
layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) layout.template_modifier_asset_menu_items(catalog_path=self.bl_label)

View File

@ -203,58 +203,41 @@ static void get_keyframe_values_create_reports(ReportList *reports,
MEM_freeN(str_failed_indices); MEM_freeN(str_failed_indices);
} }
/** static Vector<float> get_keyframe_values(PointerRNA *ptr, PropertyRNA *prop, const bool visual_key)
* Retrieve current property values to keyframe,
* possibly applying NLA correction when necessary.
*
* \param r_successful_remaps: Enables bits for indices which are both intended to be remapped and
* were successfully remapped. Bitmap allocated so it must be freed afterward.
*/
static Vector<float> get_keyframe_values(ReportList *reports,
PointerRNA ptr,
PropertyRNA *prop,
int index,
NlaKeyframingContext *nla_context,
eInsertKeyFlags flag,
const AnimationEvalContext *anim_eval_context,
bool *r_force_all,
blender::BitVector<> &r_successful_remaps)
{ {
Vector<float> values; Vector<float> values;
if ((flag & INSERTKEY_MATRIX) && visualkey_can_use(&ptr, prop)) { if (visual_key && visualkey_can_use(ptr, prop)) {
/* Visual-keying is only available for object and pchan datablocks, as /* Visual-keying is only available for object and pchan datablocks, as
* it works by keyframing using a value extracted from the final matrix * it works by keyframing using a value extracted from the final matrix
* instead of using the kt system to extract a value. * instead of using the kt system to extract a value.
*/ */
values = visualkey_get_values(&ptr, prop); values = visualkey_get_values(ptr, prop);
} }
else { else {
values = get_rna_values(&ptr, prop); values = get_rna_values(ptr, prop);
} }
r_successful_remaps.resize(values.size());
/* adjust the value for NLA factors */
BKE_animsys_nla_remap_keyframe_values(nla_context,
&ptr,
prop,
values.as_mutable_span(),
index,
anim_eval_context,
r_force_all,
r_successful_remaps);
get_keyframe_values_create_reports(reports,
ptr,
prop,
index,
values.size(),
r_force_all ? *r_force_all : false,
r_successful_remaps);
return values; return values;
} }
static BitVector<> nla_map_keyframe_values_and_generate_reports(
const MutableSpan<float> values,
const int index,
PointerRNA &ptr,
PropertyRNA &prop,
NlaKeyframingContext *nla_context,
const AnimationEvalContext *anim_eval_context,
ReportList *reports,
bool *force_all)
{
BitVector<> successful_remaps(values.size(), false);
BKE_animsys_nla_remap_keyframe_values(
nla_context, &ptr, &prop, values, index, anim_eval_context, force_all, successful_remaps);
get_keyframe_values_create_reports(
reports, ptr, &prop, index, values.size(), false, successful_remaps);
return successful_remaps;
}
/** /**
* Move the point where a key is about to be inserted to be inside the main cycle range. * Move the point where a key is about to be inserted to be inside the main cycle range.
* Returns the type of the cycle if it is enabled and valid. * Returns the type of the cycle if it is enabled and valid.
@ -429,9 +412,18 @@ bool insert_keyframe_direct(ReportList *reports,
update_autoflags_fcurve_direct(fcu, prop); update_autoflags_fcurve_direct(fcu, prop);
const int index = fcu->array_index; const int index = fcu->array_index;
BitVector<> successful_remaps; const bool visual_keyframing = flag & INSERTKEY_MATRIX;
Vector<float> values = get_keyframe_values( Vector<float> values = get_keyframe_values(&ptr, prop, visual_keyframing);
reports, ptr, prop, index, nla_context, flag, anim_eval_context, nullptr, successful_remaps);
BitVector<> successful_remaps = nla_map_keyframe_values_and_generate_reports(
values.as_mutable_span(),
index,
ptr,
*prop,
nla_context,
anim_eval_context,
reports,
nullptr);
float current_value = 0.0f; float current_value = 0.0f;
if (index >= 0 && index < values.size()) { if (index >= 0 && index < values.size()) {
@ -594,17 +586,19 @@ int insert_keyframe(Main *bmain,
const float nla_mapped_frame = nla_time_remap( const float nla_mapped_frame = nla_time_remap(
anim_eval_context, &id_ptr, adt, act, &nla_cache, &nla_context); anim_eval_context, &id_ptr, adt, act, &nla_cache, &nla_context);
const bool visual_keyframing = flag & INSERTKEY_MATRIX;
Vector<float> values = get_keyframe_values(&ptr, prop, visual_keyframing);
bool force_all; bool force_all;
BitVector successful_remaps; BitVector<> successful_remaps = nla_map_keyframe_values_and_generate_reports(
Vector<float> values = get_keyframe_values(reports, values.as_mutable_span(),
ptr, array_index,
prop, ptr,
array_index, *prop,
nla_context, nla_context,
flag, anim_eval_context,
anim_eval_context, reports,
&force_all, &force_all);
successful_remaps);
CombinedKeyingResult combined_result; CombinedKeyingResult combined_result;
@ -971,25 +965,6 @@ int insert_key_action(Main *bmain,
return inserted_keys; return inserted_keys;
} }
static blender::Vector<float> get_keyframe_values(PointerRNA *ptr,
PropertyRNA *prop,
const bool visual_key)
{
Vector<float> values;
if (visual_key && visualkey_can_use(ptr, prop)) {
/* Visual-keying is only available for object and pchan datablocks, as
* it works by keyframing using a value extracted from the final matrix
* instead of using the kt system to extract a value.
*/
values = visualkey_get_values(ptr, prop);
}
else {
values = get_rna_values(ptr, prop);
}
return values;
}
void insert_key_rna(PointerRNA *rna_pointer, void insert_key_rna(PointerRNA *rna_pointer,
const blender::Span<std::string> rna_paths, const blender::Span<std::string> rna_paths,
const float scene_frame, const float scene_frame,

View File

@ -18,6 +18,7 @@
#include <string> #include <string>
#include "BLI_string_ref.hh" #include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
#include "DNA_ID_enums.h" #include "DNA_ID_enums.h"
#include "DNA_asset_types.h" #include "DNA_asset_types.h"
@ -31,7 +32,7 @@ namespace blender::asset_system {
class AssetLibrary; class AssetLibrary;
class AssetRepresentation { class AssetRepresentation : NonCopyable, NonMovable {
AssetIdentifier identifier_; AssetIdentifier identifier_;
/** /**
* Indicate if this is a local or external asset, and as such, which of the union members below * Indicate if this is a local or external asset, and as such, which of the union members below
@ -69,11 +70,6 @@ class AssetRepresentation {
const AssetLibrary &owner_asset_library); const AssetLibrary &owner_asset_library);
~AssetRepresentation(); ~AssetRepresentation();
AssetRepresentation(const AssetRepresentation &) = delete;
AssetRepresentation(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(const AssetRepresentation &) = delete;
const AssetIdentifier &get_identifier() const; const AssetIdentifier &get_identifier() const;
/** /**

View File

@ -15,6 +15,7 @@
#include "BLI_math_vector_types.hh" #include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.hh" #include "BLI_offset_indices.hh"
#include "BLI_span.hh" #include "BLI_span.hh"
#include "BLI_virtual_array.hh"
/* /*
* Shrinkwrap is composed by a set of functions and options that define the type of shrink. * Shrinkwrap is composed by a set of functions and options that define the type of shrink.
@ -79,7 +80,7 @@ struct ShrinkwrapTreeData {
blender::Span<blender::float3> face_normals; blender::Span<blender::float3> face_normals;
blender::Span<blender::float3> vert_normals; blender::Span<blender::float3> vert_normals;
blender::Span<blender::float3> corner_normals; blender::Span<blender::float3> corner_normals;
const bool *sharp_faces; blender::VArraySpan<bool> sharp_faces;
const ShrinkwrapBoundaryData *boundary; const ShrinkwrapBoundaryData *boundary;
}; };

View File

@ -2181,6 +2181,49 @@ static void legacy_object_modifier_build(Object &object, GpencilModifierData &le
false); false);
} }
static void legacy_object_modifier_simplify(Object &object, GpencilModifierData &legacy_md)
{
ModifierData &md = legacy_object_modifier_common(
object, eModifierType_GreasePencilSimplify, legacy_md);
auto &md_simplify = reinterpret_cast<GreasePencilSimplifyModifierData &>(md);
auto &legacy_md_simplify = reinterpret_cast<SimplifyGpencilModifierData &>(legacy_md);
switch (legacy_md_simplify.mode) {
case GP_SIMPLIFY_FIXED:
md_simplify.mode = MOD_GREASE_PENCIL_SIMPLIFY_FIXED;
break;
case GP_SIMPLIFY_ADAPTIVE:
md_simplify.mode = MOD_GREASE_PENCIL_SIMPLIFY_ADAPTIVE;
break;
case GP_SIMPLIFY_SAMPLE:
md_simplify.mode = MOD_GREASE_PENCIL_SIMPLIFY_SAMPLE;
break;
case GP_SIMPLIFY_MERGE:
md_simplify.mode = MOD_GREASE_PENCIL_SIMPLIFY_MERGE;
break;
}
md_simplify.step = legacy_md_simplify.step;
md_simplify.factor = legacy_md_simplify.factor;
md_simplify.length = legacy_md_simplify.length;
md_simplify.sharp_threshold = legacy_md_simplify.sharp_threshold;
md_simplify.distance = legacy_md_simplify.distance;
legacy_object_modifier_influence(md_simplify.influence,
legacy_md_simplify.layername,
legacy_md_simplify.layer_pass,
legacy_md_simplify.flag & GP_SIMPLIFY_INVERT_LAYER,
legacy_md_simplify.flag & GP_SIMPLIFY_INVERT_LAYERPASS,
&legacy_md_simplify.material,
legacy_md_simplify.pass_index,
legacy_md_simplify.flag & GP_SIMPLIFY_INVERT_MATERIAL,
legacy_md_simplify.flag & GP_SIMPLIFY_INVERT_PASS,
"",
false,
nullptr,
false);
}
static void legacy_object_modifiers(Main & /*bmain*/, Object &object) static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
{ {
BLI_assert(BLI_listbase_is_empty(&object.modifiers)); BLI_assert(BLI_listbase_is_empty(&object.modifiers));
@ -2265,6 +2308,8 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object)
legacy_object_modifier_build(object, *gpd_md); legacy_object_modifier_build(object, *gpd_md);
break; break;
case eGpencilModifierType_Simplify: case eGpencilModifierType_Simplify:
legacy_object_modifier_simplify(object, *gpd_md);
break;
case eGpencilModifierType_Texture: case eGpencilModifierType_Texture:
break; break;
} }

View File

@ -1281,6 +1281,9 @@ void update_normals(PBVH &pbvh, SubdivCCG *subdiv_ccg)
{ {
Vector<PBVHNode *> nodes = search_gather( Vector<PBVHNode *> nodes = search_gather(
&pbvh, [&](PBVHNode &node) { return update_search(&node, PBVH_UpdateNormals); }); &pbvh, [&](PBVHNode &node) { return update_search(&node, PBVH_UpdateNormals); });
if (nodes.is_empty()) {
return;
}
if (pbvh.header.type == PBVH_BMESH) { if (pbvh.header.type == PBVH_BMESH) {
bmesh_normals_update(nodes); bmesh_normals_update(nodes);

View File

@ -26,6 +26,7 @@
#include "BLI_utildefines.h" #include "BLI_utildefines.h"
#include "BKE_DerivedMesh.hh" #include "BKE_DerivedMesh.hh"
#include "BKE_attribute.hh"
#include "BKE_cdderivedmesh.h" #include "BKE_cdderivedmesh.h"
#include "BKE_modifier.hh" #include "BKE_modifier.hh"
#include "BKE_shrinkwrap.hh" #include "BKE_shrinkwrap.hh"
@ -96,6 +97,7 @@ bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
bool BKE_shrinkwrap_init_tree( bool BKE_shrinkwrap_init_tree(
ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals) ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
{ {
using namespace blender::bke;
*data = {}; *data = {};
if (mesh == nullptr) { if (mesh == nullptr) {
@ -115,8 +117,8 @@ bool BKE_shrinkwrap_init_tree(
data->faces = mesh->faces(); data->faces = mesh->faces();
data->corner_edges = mesh->corner_edges(); data->corner_edges = mesh->corner_edges();
data->vert_normals = mesh->vert_normals(); data->vert_normals = mesh->vert_normals();
data->sharp_faces = static_cast<const bool *>( const AttributeAccessor attributes = mesh->attributes();
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face")); data->sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) { if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2); data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2);
@ -1160,7 +1162,7 @@ void BKE_shrinkwrap_compute_smooth_normal(const ShrinkwrapTreeData *tree,
const int face_i = tree->mesh->corner_tri_faces()[corner_tri_idx]; const int face_i = tree->mesh->corner_tri_faces()[corner_tri_idx];
/* Interpolate smooth normals if enabled. */ /* Interpolate smooth normals if enabled. */
if (!(tree->sharp_faces && tree->sharp_faces[face_i])) { if (tree->sharp_faces.is_empty() || tree->sharp_faces[face_i]) {
const int vert_indices[3] = {treeData->corner_verts[tri[0]], const int vert_indices[3] = {treeData->corner_verts[tri[0]],
treeData->corner_verts[tri[1]], treeData->corner_verts[tri[1]],
treeData->corner_verts[tri[2]]}; treeData->corner_verts[tri[2]]};
@ -1205,6 +1207,9 @@ void BKE_shrinkwrap_compute_smooth_normal(const ShrinkwrapTreeData *tree,
/* Use the face normal if flat. */ /* Use the face normal if flat. */
else if (!tree->face_normals.is_empty()) { else if (!tree->face_normals.is_empty()) {
copy_v3_v3(r_no, tree->face_normals[face_i]); copy_v3_v3(r_no, tree->face_normals[face_i]);
if (transform) {
BLI_space_transform_invert_normal(transform, r_no);
}
} }
/* Finally fallback to the corner_tris normal. */ /* Finally fallback to the corner_tris normal. */
else { else {

View File

@ -470,21 +470,22 @@ struct alignas(Alignment) MatBase : public vec_struct_base<VecBase<T, NumRow>, N
friend std::ostream &operator<<(std::ostream &stream, const MatBase &mat) friend std::ostream &operator<<(std::ostream &stream, const MatBase &mat)
{ {
stream << "(\n"; stream << "(\n";
unroll<NumRow>([&](auto i) { for (int i = 0; i < NumRow; i++) {
stream << "("; stream << "(";
unroll<NumCol>([&](auto j) { for (int j = 0; j < NumCol; j++) {
/** NOTE: j and i are swapped to follow mathematical convention. */ /** NOTE: j and i are swapped to follow mathematical convention. */
stream << mat[j][i]; stream << mat[j][i];
if (j < NumCol - 1) { if (j < NumCol - 1) {
stream << ", "; stream << ", ";
} }
}); }
stream << ")"; stream << ")";
if (i < NumRow - 1) { if (i < NumRow - 1) {
stream << ","; stream << ",";
} }
stream << "\n"; stream << "\n";
}); }
stream << ")\n"; stream << ")\n";
return stream; return stream;
} }

View File

@ -105,7 +105,7 @@ static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index)
MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SCULPT_OVERLAYS | MBC_VIEWER_ATTRIBUTE_OVERLAY | MBC_WIRE_EDGES | MBC_WIRE_LOOPS | MBC_SCULPT_OVERLAYS | MBC_VIEWER_ATTRIBUTE_OVERLAY |
MBC_SURFACE_PER_MAT; MBC_SURFACE_PER_MAT;
case BUFFER_INDEX(vbo.nor): case BUFFER_INDEX(vbo.nor):
return MBC_SURFACE | MBC_EDIT_LNOR | MBC_WIRE_LOOPS | MBC_SURFACE_PER_MAT; return MBC_SURFACE | MBC_EDIT_LNOR | MBC_WIRE_LOOPS | MBC_SURFACE_PER_MAT | MBC_ALL_VERTS;
case BUFFER_INDEX(vbo.edge_fac): case BUFFER_INDEX(vbo.edge_fac):
return MBC_WIRE_EDGES; return MBC_WIRE_EDGES;
case BUFFER_INDEX(vbo.weights): case BUFFER_INDEX(vbo.weights):
@ -1555,9 +1555,10 @@ void DRW_mesh_batch_cache_create_requested(TaskGraph *task_graph,
} }
drw_add_attributes_vbo(cache.batch.surface, mbuflist, &cache.attr_used); drw_add_attributes_vbo(cache.batch.surface, mbuflist, &cache.attr_used);
} }
assert_deps_valid(MBC_ALL_VERTS, {BUFFER_INDEX(vbo.pos)}); assert_deps_valid(MBC_ALL_VERTS, {BUFFER_INDEX(vbo.pos), BUFFER_INDEX(vbo.nor)});
if (DRW_batch_requested(cache.batch.all_verts, GPU_PRIM_POINTS)) { if (DRW_batch_requested(cache.batch.all_verts, GPU_PRIM_POINTS)) {
DRW_vbo_request(cache.batch.all_verts, &mbuflist->vbo.pos); DRW_vbo_request(cache.batch.all_verts, &mbuflist->vbo.pos);
DRW_vbo_request(cache.batch.all_verts, &mbuflist->vbo.nor);
} }
assert_deps_valid( assert_deps_valid(
MBC_SCULPT_OVERLAYS, MBC_SCULPT_OVERLAYS,

View File

@ -103,6 +103,7 @@ static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
asset_system::all_library_reference()); asset_system::all_library_reference());
if (!all_library) { if (!all_library) {
BKE_report(reports, RPT_WARNING, "Asset loading is unfinished"); BKE_report(reports, RPT_WARNING, "Asset loading is unfinished");
return nullptr;
} }
const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref); const std::string full_path = all_library->resolve_asset_weak_reference_to_full_path(weak_ref);

View File

@ -138,7 +138,7 @@ void AssetView::build_items()
return handle_get_preview_or_type_icon_id(&asset_handle); return handle_get_preview_or_type_icon_id(&asset_handle);
}(); }();
AssetViewItem &item = add_item<AssetViewItem>( AssetViewItem &item = this->add_item<AssetViewItem>(
asset_handle, identifier, asset->get_name(), preview_id); asset_handle, identifier, asset->get_name(), preview_id);
if (!show_names) { if (!show_names) {
item.hide_label(); item.hide_label();
@ -222,7 +222,7 @@ void AssetViewItem::build_grid_tile(uiLayout &layout) const
void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
{ {
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view()); const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
const AssetShelfType &shelf_type = *asset_view.shelf_.type; const AssetShelfType &shelf_type = *asset_view.shelf_.type;
if (shelf_type.draw_context_menu) { if (shelf_type.draw_context_menu) {
asset_system::AssetRepresentation *asset = handle_get_representation(&asset_); asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
@ -245,7 +245,7 @@ std::optional<bool> AssetViewItem::should_be_active() const
bool AssetViewItem::is_filtered_visible() const bool AssetViewItem::is_filtered_visible() const
{ {
const AssetView &asset_view = dynamic_cast<const AssetView &>(get_view()); const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
if (asset_view.search_string[0] == '\0') { if (asset_view.search_string[0] == '\0') {
return true; return true;
} }
@ -260,7 +260,7 @@ std::unique_ptr<ui::AbstractViewItemDragController> AssetViewItem::create_drag_c
return nullptr; return nullptr;
} }
asset_system::AssetRepresentation *asset = handle_get_representation(&asset_); asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
return std::make_unique<AssetDragController>(get_view(), *asset); return std::make_unique<AssetDragController>(this->get_view(), *asset);
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */

View File

@ -1108,7 +1108,7 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso) static bool gpencil_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso)
{ {
ToolSettings *ts = CTX_data_tool_settings(C); ToolSettings *ts = CTX_data_tool_settings(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *obact = gso->object; Object *obact = gso->object;
bool changed = false; bool changed = false;

View File

@ -1154,15 +1154,15 @@ void ED_mesh_split_faces(Mesh *mesh)
const bke::AttributeAccessor attributes = mesh->attributes(); const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> mesh_sharp_edges = *attributes.lookup_or_default<bool>( const VArray<bool> mesh_sharp_edges = *attributes.lookup_or_default<bool>(
"sharp_edge", bke::AttrDomain::Edge, false); "sharp_edge", bke::AttrDomain::Edge, false);
const bool *sharp_faces = static_cast<const bool *>( const VArraySpan<bool> sharp_faces = *attributes.lookup<bool>("sharp_face",
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face")); bke::AttrDomain::Face);
Array<bool> sharp_edges(mesh->edges_num); Array<bool> sharp_edges(mesh->edges_num);
mesh_sharp_edges.materialize(sharp_edges); mesh_sharp_edges.materialize(sharp_edges);
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) { threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
for (const int face_i : range) { for (const int face_i : range) {
if (sharp_faces && sharp_faces[face_i]) { if (!sharp_faces.is_empty() && sharp_faces[face_i]) {
for (const int edge : corner_edges.slice(polys[face_i])) { for (const int edge : corner_edges.slice(polys[face_i])) {
sharp_edges[edge] = true; sharp_edges[edge] = true;
} }

View File

@ -1010,6 +1010,8 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
vwpaint::update_cache_invariants(C, vp, ss, op, mouse); vwpaint::update_cache_invariants(C, vp, ss, op, mouse);
vwpaint::init_session_data(ts, ob); vwpaint::init_session_data(ts, ob);
/* Brush may have changed after initialization. */
brush = BKE_paint_brush(&vp->paint);
if (ELEM(brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { if (ELEM(brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) {
wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * mesh->verts_num, __func__); wpd->precomputed_weight = (float *)MEM_mallocN(sizeof(float) * mesh->verts_num, __func__);
} }

View File

@ -622,13 +622,8 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
bke::MutableAttributeAccessor attributes = mesh->attributes_for_write(); bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>( bke::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Face); "material_index", bke::AttrDomain::Face);
bke::SpanAttributeWriter sharp_faces = attributes.lookup_or_add_for_write_span<bool>(
bool *sharp_faces = static_cast<bool *>(CustomData_get_layer_named_for_write( "sharp_face", bke::AttrDomain::Face);
&mesh->face_data, CD_PROP_BOOL, "sharp_face", mesh->faces_num));
if (!sharp_faces) {
sharp_faces = static_cast<bool *>(CustomData_add_layer_named(
&mesh->face_data, CD_PROP_BOOL, CD_SET_DEFAULT, mesh->faces_num, "sharp_face"));
}
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives(); COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals(); COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals();
@ -673,7 +668,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
if (mp_has_normals) { /* vertex normals, same implementation as for the triangles */ if (mp_has_normals) { /* vertex normals, same implementation as for the triangles */
/* The same for vertices normals. */ /* The same for vertices normals. */
uint vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]}; uint vertex_normal_indices[3] = {first_normal, normal_indices[1], normal_indices[2]};
sharp_faces[face_index] = is_flat_face(vertex_normal_indices, nor, 3); sharp_faces.span[face_index] = is_flat_face(vertex_normal_indices, nor, 3);
normal_indices++; normal_indices++;
} }
@ -743,7 +738,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
if (mp_has_normals) { if (mp_has_normals) {
/* If it turns out that we have complete custom normals for each poly /* If it turns out that we have complete custom normals for each poly
* and we want to use custom normals, this will be overridden. */ * and we want to use custom normals, this will be overridden. */
sharp_faces[face_index] = is_flat_face(normal_indices, nor, vcount); sharp_faces.span[face_index] = is_flat_face(normal_indices, nor, vcount);
if (use_custom_normals) { if (use_custom_normals) {
/* Store the custom normals for later application. */ /* Store the custom normals for later application. */

View File

@ -1062,4 +1062,14 @@
.percentage_fac = 0.0f, \ .percentage_fac = 0.0f, \
} }
#define _DNA_DEFAULT_GreasePencilSimplifyModifierData \
{ \
.factor = 0.0f, \
.mode = MOD_GREASE_PENCIL_SIMPLIFY_FIXED, \
.step = 1, \
.length = 0.1f, \
.distance = 0.1f, \
}
/* clang-format off */ /* clang-format off */

View File

@ -119,6 +119,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilOutline = 82, eModifierType_GreasePencilOutline = 82,
eModifierType_GreasePencilShrinkwrap = 83, eModifierType_GreasePencilShrinkwrap = 83,
eModifierType_GreasePencilBuild = 84, eModifierType_GreasePencilBuild = 84,
eModifierType_GreasePencilSimplify = 85,
NUM_MODIFIER_TYPES, NUM_MODIFIER_TYPES,
} ModifierType; } ModifierType;
@ -3404,3 +3405,28 @@ typedef enum GreasePencilBuildFlag {
MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME = (1 << 0), MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME = (1 << 0),
MOD_GREASE_PENCIL_BUILD_USE_FADING = (1 << 14), MOD_GREASE_PENCIL_BUILD_USE_FADING = (1 << 14),
} GreasePencilBuildFlag; } GreasePencilBuildFlag;
typedef struct GreasePencilSimplifyModifierData {
ModifierData modifier;
GreasePencilModifierInfluenceData influence;
/** #GreasePencilSimplifyModifierMode. */
short mode;
char _pad[4];
/** Every n vertex to keep. */
short step;
float factor;
/** For sampling. */
float length;
float sharp_threshold;
/** Merge distance */
float distance;
} GreasePencilSimplifyModifierData;
typedef enum GreasePencilSimplifyModifierMode {
MOD_GREASE_PENCIL_SIMPLIFY_FIXED = 0,
MOD_GREASE_PENCIL_SIMPLIFY_ADAPTIVE = 1,
MOD_GREASE_PENCIL_SIMPLIFY_SAMPLE = 2,
MOD_GREASE_PENCIL_SIMPLIFY_MERGE = 3,
} GreasePencilSimplifyModifierMode;

View File

@ -357,6 +357,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilHookModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilArmatureModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilArmatureModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierSegment); SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierSegment);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilSimplifyModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilEnvelopeModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilEnvelopeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilOutlineModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilOutlineModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilShrinkwrapModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilShrinkwrapModifierData);
@ -634,6 +635,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilArmatureModifierData), SDNA_DEFAULT_DECL(GreasePencilArmatureModifierData),
SDNA_DEFAULT_DECL(GreasePencilTimeModifierSegment), SDNA_DEFAULT_DECL(GreasePencilTimeModifierSegment),
SDNA_DEFAULT_DECL(GreasePencilTimeModifierData), SDNA_DEFAULT_DECL(GreasePencilTimeModifierData),
SDNA_DEFAULT_DECL(GreasePencilSimplifyModifierData),
SDNA_DEFAULT_DECL(GreasePencilEnvelopeModifierData), SDNA_DEFAULT_DECL(GreasePencilEnvelopeModifierData),
SDNA_DEFAULT_DECL(GreasePencilOutlineModifierData), SDNA_DEFAULT_DECL(GreasePencilOutlineModifierData),
SDNA_DEFAULT_DECL(GreasePencilShrinkwrapModifierData), SDNA_DEFAULT_DECL(GreasePencilShrinkwrapModifierData),

View File

@ -259,6 +259,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_GP_MULTIFRAME_EDITING, ICON_GP_MULTIFRAME_EDITING,
"Multiple Strokes", "Multiple Strokes",
"Generate multiple strokes around original strokes"}, "Generate multiple strokes around original strokes"},
{eModifierType_GreasePencilSimplify,
"GREASE_PENCIL_SIMPLIFY",
ICON_MOD_SIMPLIFY,
"Simplify",
"Simplify stroke reducing number of points"},
{eModifierType_GreasePencilSubdiv, {eModifierType_GreasePencilSubdiv,
"GREASE_PENCIL_SUBDIV", "GREASE_PENCIL_SUBDIV",
ICON_MOD_SUBSURF, ICON_MOD_SUBSURF,
@ -1992,6 +1997,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightAngle);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilArray); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilArray);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightProximity); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightProximity);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilHook); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilHook);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilSimplify);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilEnvelope); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilEnvelope);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOutline); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOutline);
RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilShrinkwrap); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilShrinkwrap);
@ -2008,6 +2014,7 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightAngle);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightProximity); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightProximity);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilHook); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilHook);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilArmature); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilArmature);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilSimplify);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilEnvelope); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilEnvelope);
RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilShrinkwrap); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilShrinkwrap);
@ -9995,6 +10002,89 @@ static void rna_def_modifier_grease_pencil_weight_proximity(BlenderRNA *brna)
RNA_define_lib_overridable(false); RNA_define_lib_overridable(false);
} }
static void rna_def_modifier_grease_pencil_simplify(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem prop_gpencil_simplify_mode_items[] = {
{MOD_GREASE_PENCIL_SIMPLIFY_FIXED,
"FIXED",
ICON_IPO_CONSTANT,
"Fixed",
"Delete alternating vertices in the stroke, except extremes"},
{MOD_GREASE_PENCIL_SIMPLIFY_ADAPTIVE,
"ADAPTIVE",
ICON_IPO_EASE_IN_OUT,
"Adaptive",
"Use a Ramer-Douglas-Peucker algorithm to simplify the stroke preserving main shape"},
{MOD_GREASE_PENCIL_SIMPLIFY_SAMPLE,
"SAMPLE",
ICON_IPO_EASE_IN_OUT,
"Sample",
"Re-sample the stroke with segments of the specified length"},
{MOD_GREASE_PENCIL_SIMPLIFY_MERGE,
"MERGE",
ICON_IPO_EASE_IN_OUT,
"Merge",
"Simplify the stroke by merging vertices closer than a given distance"},
{0, nullptr, 0, nullptr, nullptr},
};
srna = RNA_def_struct(brna, "GreasePencilSimplifyModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Simplify Modifier", "Simplify Stroke modifier");
RNA_def_struct_sdna(srna, "GreasePencilSimplifyModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SIMPLIFY);
rna_def_modifier_grease_pencil_layer_filter(srna);
rna_def_modifier_grease_pencil_material_filter(
srna, "rna_GreasePencilSimplifyModifier_material_filter_set");
rna_def_modifier_grease_pencil_vertex_group(
srna, "rna_GreasePencilSimplifyModifier_vertex_group_name_set");
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, nullptr, "factor");
RNA_def_property_range(prop, 0, 100.0);
RNA_def_property_ui_range(prop, 0, 5.0f, 1.0f, 3);
RNA_def_property_ui_text(prop, "Factor", "Factor of Simplify");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_gpencil_simplify_mode_items);
RNA_def_property_ui_text(prop, "Mode", "How to simplify the stroke");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "step");
RNA_def_property_range(prop, 1, 50);
RNA_def_property_ui_text(prop, "Iterations", "Number of times to apply simplify");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, nullptr, "length");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_range(prop, 0.005, 1.0, 0.05, 3);
RNA_def_property_ui_text(prop, "Length", "Length of each segment");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, nullptr, "sharp_threshold");
RNA_def_property_range(prop, 0, M_PI);
RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1);
RNA_def_property_ui_text(
prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, nullptr, "distance");
RNA_def_property_range(prop, 0, FLT_MAX);
RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3);
RNA_def_property_ui_text(prop, "Distance", "Distance between points");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_grease_pencil_armature(BlenderRNA *brna) static void rna_def_modifier_grease_pencil_armature(BlenderRNA *brna)
{ {
StructRNA *srna; StructRNA *srna;
@ -10904,6 +10994,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_armature(brna); rna_def_modifier_grease_pencil_armature(brna);
rna_def_modifier_grease_pencil_time_segment(brna); rna_def_modifier_grease_pencil_time_segment(brna);
rna_def_modifier_grease_pencil_time(brna); rna_def_modifier_grease_pencil_time(brna);
rna_def_modifier_grease_pencil_simplify(brna);
rna_def_modifier_grease_pencil_envelope(brna); rna_def_modifier_grease_pencil_envelope(brna);
rna_def_modifier_grease_pencil_outline(brna); rna_def_modifier_grease_pencil_outline(brna);
rna_def_modifier_grease_pencil_shrinkwrap(brna); rna_def_modifier_grease_pencil_shrinkwrap(brna);

View File

@ -63,6 +63,7 @@ set(SRC
intern/MOD_grease_pencil_noise.cc intern/MOD_grease_pencil_noise.cc
intern/MOD_grease_pencil_offset.cc intern/MOD_grease_pencil_offset.cc
intern/MOD_grease_pencil_opacity.cc intern/MOD_grease_pencil_opacity.cc
intern/MOD_grease_pencil_simplify.cc
intern/MOD_grease_pencil_outline.cc intern/MOD_grease_pencil_outline.cc
intern/MOD_grease_pencil_shrinkwrap.cc intern/MOD_grease_pencil_shrinkwrap.cc
intern/MOD_grease_pencil_smooth.cc intern/MOD_grease_pencil_smooth.cc

View File

@ -93,6 +93,7 @@ extern ModifierTypeInfo modifierType_GreasePencilHook;
extern ModifierTypeInfo modifierType_GreasePencilLineart; extern ModifierTypeInfo modifierType_GreasePencilLineart;
extern ModifierTypeInfo modifierType_GreasePencilArmature; extern ModifierTypeInfo modifierType_GreasePencilArmature;
extern ModifierTypeInfo modifierType_GreasePencilTime; extern ModifierTypeInfo modifierType_GreasePencilTime;
extern ModifierTypeInfo modifierType_GreasePencilSimplify;
extern ModifierTypeInfo modifierType_GreasePencilEnvelope; extern ModifierTypeInfo modifierType_GreasePencilEnvelope;
extern ModifierTypeInfo modifierType_GreasePencilOutline; extern ModifierTypeInfo modifierType_GreasePencilOutline;
extern ModifierTypeInfo modifierType_GreasePencilShrinkwrap; extern ModifierTypeInfo modifierType_GreasePencilShrinkwrap;

View File

@ -0,0 +1,427 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup modifiers
*/
#include "BLI_index_mask.hh"
#include "BLI_kdtree.h"
#include "BLT_translation.hh"
#include "BLO_read_write.hh"
#include "DNA_defaults.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "BKE_curves_utils.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_modifier.hh"
#include "GEO_resample_curves.hh"
#include "GEO_simplify_curves.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "MOD_grease_pencil_util.hh"
#include "MOD_ui_common.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
namespace blender {
static void init_data(ModifierData *md)
{
auto *gpmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilSimplifyModifierData), modifier);
modifier::greasepencil::init_influence_data(&gpmd->influence, true);
}
static void free_data(ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
modifier::greasepencil::free_influence_data(&mmd->influence);
}
static void copy_data(const ModifierData *md, ModifierData *target, int flag)
{
const auto *gmd = reinterpret_cast<const GreasePencilSimplifyModifierData *>(md);
auto *tgmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(target);
BKE_modifier_copydata_generic(md, target, flag);
modifier::greasepencil::copy_influence_data(&gmd->influence, &tgmd->influence, flag);
}
static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
{
const auto *mmd = reinterpret_cast<const GreasePencilSimplifyModifierData *>(md);
BLO_write_struct(writer, GreasePencilSimplifyModifierData, mmd);
modifier::greasepencil::write_influence_data(writer, &mmd->influence);
}
static void blend_read(BlendDataReader *reader, ModifierData *md)
{
auto *mmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
modifier::greasepencil::read_influence_data(reader, &mmd->influence);
}
static IndexMask simplify_fixed(const bke::CurvesGeometry &curves,
const int step,
IndexMaskMemory &memory)
{
const OffsetIndices points_by_curve = curves.points_by_curve();
const Array<int> point_to_curve_map = curves.point_to_curve_map();
return IndexMask::from_predicate(
curves.points_range(), GrainSize(2048), memory, [&](const int64_t i) {
const int curve_i = point_to_curve_map[i];
const IndexRange points = points_by_curve[curve_i];
if (points.drop_front(1).drop_back(1).contains(i)) {
const int local_i = i - points.start();
return local_i % int(math::pow(2.0f, float(step - 1))) == 0;
}
return false;
});
}
static int curve_merge_by_distance(const IndexRange points,
const Span<float> distances,
const IndexMask &selection,
const float merge_distance,
MutableSpan<int> r_merge_indices)
{
/* We use a KDTree_1d here, because we can only merge neighboring points in the curves. */
KDTree_1d *tree = BLI_kdtree_1d_new(selection.size());
/* The selection is an IndexMask of the points just in this curve. */
selection.foreach_index_optimized<int64_t>([&](const int64_t i, const int64_t pos) {
BLI_kdtree_1d_insert(tree, pos, &distances[i - points.first()]);
});
BLI_kdtree_1d_balance(tree);
Array<int> selection_merge_indices(selection.size(), -1);
const int duplicate_count = BLI_kdtree_1d_calc_duplicates_fast(
tree, merge_distance, false, selection_merge_indices.data());
BLI_kdtree_1d_free(tree);
array_utils::fill_index_range<int>(r_merge_indices);
selection.foreach_index([&](const int src_index, const int pos) {
const int merge_index = selection_merge_indices[pos];
if (merge_index != -1) {
const int src_merge_index = selection[merge_index] - points.first();
r_merge_indices[src_index - points.first()] = src_merge_index;
}
});
return duplicate_count;
}
/* NOTE: The code here is an adapted version of #blender::geometry::point_merge_by_distance. */
static bke::CurvesGeometry curves_merge_by_distance(
const bke::CurvesGeometry &src_curves,
const float merge_distance,
const IndexMask &selection,
const bke::AnonymousAttributePropagationInfo &propagation_info)
{
const int src_point_size = src_curves.points_num();
if (src_point_size == 0) {
return {};
}
const OffsetIndices<int> points_by_curve = src_curves.points_by_curve();
const VArray<bool> cyclic = src_curves.cyclic();
src_curves.ensure_evaluated_lengths();
bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
std::atomic<int> total_duplicate_count = 0;
Array<Array<int>> merge_indices_per_curve(src_curves.curves_num());
threading::parallel_for(src_curves.curves_range(), 512, [&](const IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
merge_indices_per_curve[curve_i].reinitialize(points.size());
Array<float> distances_along_curve(points.size());
distances_along_curve.first() = 0.0f;
const Span<float> lengths = src_curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
distances_along_curve.as_mutable_span().drop_front(1).copy_from(lengths);
MutableSpan<int> merge_indices = merge_indices_per_curve[curve_i].as_mutable_span();
array_utils::fill_index_range<int>(merge_indices);
const int duplicate_count = curve_merge_by_distance(points,
distances_along_curve,
selection.slice_content(points),
merge_distance,
merge_indices);
/* Write the curve size. The counts will be accumulated to offsets below. */
dst_offsets[curve_i] = points.size() - duplicate_count;
total_duplicate_count += duplicate_count;
}
});
const int dst_point_size = src_point_size - total_duplicate_count;
dst_curves.resize(dst_point_size, src_curves.curves_num());
offset_indices::accumulate_counts_to_offsets(dst_offsets);
int merged_points = 0;
Array<int> src_to_dst_indices(src_point_size);
for (const int curve_i : src_curves.curves_range()) {
const IndexRange points = points_by_curve[curve_i];
const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
for (const int i : points.index_range()) {
const int point_i = points.start() + i;
src_to_dst_indices[point_i] = point_i - merged_points;
if (merge_indices[i] != i) {
merged_points++;
}
}
}
Array<int> point_merge_counts(dst_point_size, 0);
for (const int curve_i : src_curves.curves_range()) {
const IndexRange points = points_by_curve[curve_i];
const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
for (const int i : points.index_range()) {
const int merge_index = merge_indices[i];
const int point_src = points.start() + merge_index;
const int dst_index = src_to_dst_indices[point_src];
point_merge_counts[dst_index]++;
}
}
Array<int> map_offsets_data(dst_point_size + 1);
map_offsets_data.as_mutable_span().drop_back(1).copy_from(point_merge_counts);
OffsetIndices<int> map_offsets = offset_indices::accumulate_counts_to_offsets(map_offsets_data);
point_merge_counts.fill(0);
Array<int> merge_map_indices(src_point_size);
for (const int curve_i : src_curves.curves_range()) {
const IndexRange points = points_by_curve[curve_i];
const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
for (const int i : points.index_range()) {
const int point_i = points.start() + i;
const int merge_index = merge_indices[i];
const int dst_index = src_to_dst_indices[points.start() + merge_index];
merge_map_indices[map_offsets[dst_index].first() + point_merge_counts[dst_index]] = point_i;
point_merge_counts[dst_index]++;
}
}
bke::AttributeAccessor src_attributes = src_curves.attributes();
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
src_attributes.for_all([&](const bke::AttributeIDRef &id,
const bke::AttributeMetaData &meta_data) {
if (id.is_anonymous() && !propagation_info.propagate(id.anonymous_id())) {
return true;
}
if (meta_data.domain != bke::AttrDomain::Point) {
return true;
}
bke::GAttributeReader src_attribute = src_attributes.lookup(id);
bke::attribute_math::convert_to_static_type(src_attribute.varray.type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<bke::attribute_math::DefaultMixer<T>>) {
bke::SpanAttributeWriter<T> dst_attribute =
dst_attributes.lookup_or_add_for_write_only_span<T>(id, bke::AttrDomain::Point);
VArraySpan<T> src = src_attribute.varray.typed<T>();
threading::parallel_for(dst_curves.points_range(), 1024, [&](IndexRange range) {
for (const int dst_point_i : range) {
/* Create a separate mixer for every point to avoid allocating temporary buffers
* in the mixer the size of the result curves and to improve memory locality. */
bke::attribute_math::DefaultMixer<T> mixer{dst_attribute.span.slice(dst_point_i, 1)};
Span<int> src_merge_indices = merge_map_indices.as_span().slice(
map_offsets[dst_point_i]);
for (const int src_point_i : src_merge_indices) {
mixer.mix_in(0, src[src_point_i]);
}
mixer.finalize();
}
});
dst_attribute.finish();
}
});
return true;
});
return dst_curves;
}
static void simplify_drawing(const GreasePencilSimplifyModifierData &mmd,
const Object &ob,
bke::greasepencil::Drawing &drawing)
{
IndexMaskMemory memory;
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexMask strokes = modifier::greasepencil::get_filtered_stroke_mask(
&ob, curves, mmd.influence, memory);
if (strokes.is_empty()) {
return;
}
switch (mmd.mode) {
case MOD_GREASE_PENCIL_SIMPLIFY_FIXED: {
const IndexMask points_to_keep = simplify_fixed(curves, mmd.step, memory);
drawing.strokes_for_write() = bke::curves_copy_point_selection(curves, points_to_keep, {});
break;
}
case MOD_GREASE_PENCIL_SIMPLIFY_ADAPTIVE: {
const IndexMask points_to_delete = geometry::simplify_curve_attribute(
curves.positions(),
strokes,
curves.points_by_curve(),
curves.cyclic(),
mmd.factor,
curves.positions(),
memory);
drawing.strokes_for_write().remove_points(points_to_delete, {});
break;
}
case MOD_GREASE_PENCIL_SIMPLIFY_SAMPLE: {
drawing.strokes_for_write() = geometry::resample_to_length(
curves, strokes, VArray<float>::ForSingle(mmd.length, curves.curves_num()), {});
break;
}
case MOD_GREASE_PENCIL_SIMPLIFY_MERGE: {
const OffsetIndices points_by_curve = curves.points_by_curve();
const Array<int> point_to_curve_map = curves.point_to_curve_map();
const IndexMask points = IndexMask::from_predicate(
curves.points_range(), GrainSize(2048), memory, [&](const int64_t i) {
const int curve_i = point_to_curve_map[i];
const IndexRange points = points_by_curve[curve_i];
if (points.drop_front(1).drop_back(1).contains(i)) {
return true;
}
return false;
});
drawing.strokes_for_write() = curves_merge_by_distance(curves, mmd.distance, points, {});
break;
}
default:
break;
}
drawing.tag_topology_changed();
}
static void modify_geometry_set(ModifierData *md,
const ModifierEvalContext *ctx,
bke::GeometrySet *geometry_set)
{
const auto *mmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
if (!geometry_set->has_grease_pencil()) {
return;
}
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
const int current_frame = grease_pencil.runtime->eval_frame;
IndexMaskMemory mask_memory;
const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask(
grease_pencil, mmd->influence, mask_memory);
const Vector<bke::greasepencil::Drawing *> drawings =
modifier::greasepencil::get_drawings_for_write(grease_pencil, layer_mask, current_frame);
threading::parallel_for_each(drawings, [&](bke::greasepencil::Drawing *drawing) {
simplify_drawing(*mmd, *ctx->object, *drawing);
});
}
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
{
auto *mmd = reinterpret_cast<GreasePencilSimplifyModifierData *>(md);
modifier::greasepencil::foreach_influence_ID_link(&mmd->influence, ob, walk, user_data);
}
static void panel_draw(const bContext * /*C*/, Panel *panel)
{
uiLayout *layout = panel->layout;
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
int mode = RNA_enum_get(ptr, "mode");
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "mode", UI_ITEM_NONE, nullptr, ICON_NONE);
if (mode == MOD_GREASE_PENCIL_SIMPLIFY_FIXED) {
uiItemR(layout, ptr, "step", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else if (mode == MOD_GREASE_PENCIL_SIMPLIFY_ADAPTIVE) {
uiItemR(layout, ptr, "factor", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else if (mode == MOD_GREASE_PENCIL_SIMPLIFY_SAMPLE) {
uiItemR(layout, ptr, "length", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(layout, ptr, "sharp_threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
}
else if (mode == MOD_GREASE_PENCIL_SIMPLIFY_MERGE) {
uiItemR(layout, ptr, "distance", UI_ITEM_NONE, nullptr, ICON_NONE);
}
modifier_panel_end(layout, ptr);
}
static void panel_register(ARegionType *region_type)
{
modifier_panel_register(region_type, eModifierType_GreasePencilSimplify, panel_draw);
}
} // namespace blender
ModifierTypeInfo modifierType_GreasePencilSimplify = {
/*idname*/ "GreasePencilSimplifyModifier",
/*name*/ N_("Simplify"),
/*struct_name*/ "GreasePencilSimplifyModifierData",
/*struct_size*/ sizeof(GreasePencilSimplifyModifierData),
/*srna*/ &RNA_GreasePencilSimplifyModifier,
/*type*/ ModifierTypeType::Nonconstructive,
/*flags*/
eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode |
eModifierTypeFlag_EnableInEditmode,
/*icon*/ ICON_MOD_SIMPLIFY,
/*copy_data*/ blender::copy_data,
/*deform_verts*/ nullptr,
/*deform_matrices*/ nullptr,
/*deform_verts_EM*/ nullptr,
/*deform_matrices_EM*/ nullptr,
/*modify_mesh*/ nullptr,
/*modify_geometry_set*/ blender::modify_geometry_set,
/*init_data*/ blender::init_data,
/*required_data_mask*/ nullptr,
/*free_data*/ blender::free_data,
/*is_disabled*/ nullptr,
/*update_depsgraph*/ nullptr,
/*depends_on_time*/ nullptr,
/*depends_on_normals*/ nullptr,
/*foreach_ID_link*/ blender::foreach_ID_link,
/*foreach_tex_link*/ nullptr,
/*free_runtime_data*/ nullptr,
/*panel_register*/ blender::panel_register,
/*blend_write*/ blender::blend_write,
/*blend_read*/ blender::blend_read,
/*foreach_cache*/ nullptr,
};

View File

@ -284,6 +284,7 @@ void modifier_type_init(ModifierTypeInfo *types[])
INIT_TYPE(GreasePencilLineart); INIT_TYPE(GreasePencilLineart);
INIT_TYPE(GreasePencilArmature); INIT_TYPE(GreasePencilArmature);
INIT_TYPE(GreasePencilTime); INIT_TYPE(GreasePencilTime);
INIT_TYPE(GreasePencilSimplify);
INIT_TYPE(GreasePencilEnvelope); INIT_TYPE(GreasePencilEnvelope);
INIT_TYPE(GreasePencilOutline); INIT_TYPE(GreasePencilOutline);
INIT_TYPE(GreasePencilShrinkwrap); INIT_TYPE(GreasePencilShrinkwrap);

View File

@ -848,6 +848,28 @@ static void engine_render_view_layer(Render *re,
if (use_gpu_context) { if (use_gpu_context) {
DRW_render_context_enable(engine->re); DRW_render_context_enable(engine->re);
} }
else if (engine->has_grease_pencil && use_grease_pencil && G.background) {
/* Workaround for specific NVidia drivers which crash on Linux when OptiX context is
* initialized prior to OpenGL context. This affects driver versions 545.29.06, 550.54.14,
* and 550.67 running on kernel 6.8.
*
* The idea here is to initialize GPU context before giving control to the render engine in
* cases when we know that the GPU context will definitely be needed later on.
*
* Only do it for background renders to avoid possible extra global locking during the
* context initialization. For the non-background renders the GPU context is already
* initialized for the Blender interface and no workaround is needed.
*
* Technically it is enough to only call WM_init_gpu() here, but it expects to only be called
* once, and from here it is not possible to know whether GPU sub-system is initialized or
* not. So instead temporarily enable the render context, which will take care of the GPU
* context initialization.
*
* For demo file and tracking progress of possible fixes on driver side refer to #120007. */
DRW_render_context_enable(engine->re);
DRW_render_context_disable(engine->re);
}
if (engine->type->update) { if (engine->type->update) {
engine->type->update(engine, re->main, engine->depsgraph); engine->type->update(engine, re->main, engine->depsgraph);
} }