WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 358 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_MULTIPLY')
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, 'LINEART')
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);
}
/**
* 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)
static Vector<float> get_keyframe_values(PointerRNA *ptr, PropertyRNA *prop, const bool visual_key)
{
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
* 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);
values = visualkey_get_values(ptr, prop);
}
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;
}
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.
* 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);
const int index = fcu->array_index;
BitVector<> successful_remaps;
Vector<float> values = get_keyframe_values(
reports, ptr, prop, index, nla_context, flag, anim_eval_context, nullptr, successful_remaps);
const bool visual_keyframing = flag & INSERTKEY_MATRIX;
Vector<float> values = get_keyframe_values(&ptr, prop, visual_keyframing);
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;
if (index >= 0 && index < values.size()) {
@ -594,17 +586,19 @@ int insert_keyframe(Main *bmain,
const float nla_mapped_frame = nla_time_remap(
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;
BitVector successful_remaps;
Vector<float> values = get_keyframe_values(reports,
ptr,
prop,
array_index,
nla_context,
flag,
anim_eval_context,
&force_all,
successful_remaps);
BitVector<> successful_remaps = nla_map_keyframe_values_and_generate_reports(
values.as_mutable_span(),
array_index,
ptr,
*prop,
nla_context,
anim_eval_context,
reports,
&force_all);
CombinedKeyingResult combined_result;
@ -971,25 +965,6 @@ int insert_key_action(Main *bmain,
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,
const blender::Span<std::string> rna_paths,
const float scene_frame,

View File

@ -18,6 +18,7 @@
#include <string>
#include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
#include "DNA_ID_enums.h"
#include "DNA_asset_types.h"
@ -31,7 +32,7 @@ namespace blender::asset_system {
class AssetLibrary;
class AssetRepresentation {
class AssetRepresentation : NonCopyable, NonMovable {
AssetIdentifier identifier_;
/**
* 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);
~AssetRepresentation();
AssetRepresentation(const AssetRepresentation &) = delete;
AssetRepresentation(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(AssetRepresentation &&) = delete;
AssetRepresentation &operator=(const AssetRepresentation &) = delete;
const AssetIdentifier &get_identifier() const;
/**

View File

@ -15,6 +15,7 @@
#include "BLI_math_vector_types.hh"
#include "BLI_offset_indices.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.
@ -79,7 +80,7 @@ struct ShrinkwrapTreeData {
blender::Span<blender::float3> face_normals;
blender::Span<blender::float3> vert_normals;
blender::Span<blender::float3> corner_normals;
const bool *sharp_faces;
blender::VArraySpan<bool> sharp_faces;
const ShrinkwrapBoundaryData *boundary;
};

View File

@ -2181,6 +2181,49 @@ static void legacy_object_modifier_build(Object &object, GpencilModifierData &le
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)
{
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);
break;
case eGpencilModifierType_Simplify:
legacy_object_modifier_simplify(object, *gpd_md);
break;
case eGpencilModifierType_Texture:
break;
}

View File

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

View File

@ -26,6 +26,7 @@
#include "BLI_utildefines.h"
#include "BKE_DerivedMesh.hh"
#include "BKE_attribute.hh"
#include "BKE_cdderivedmesh.h"
#include "BKE_modifier.hh"
#include "BKE_shrinkwrap.hh"
@ -96,6 +97,7 @@ bool BKE_shrinkwrap_needs_normals(int shrinkType, int shrinkMode)
bool BKE_shrinkwrap_init_tree(
ShrinkwrapTreeData *data, Mesh *mesh, int shrinkType, int shrinkMode, bool force_normals)
{
using namespace blender::bke;
*data = {};
if (mesh == nullptr) {
@ -115,8 +117,8 @@ bool BKE_shrinkwrap_init_tree(
data->faces = mesh->faces();
data->corner_edges = mesh->corner_edges();
data->vert_normals = mesh->vert_normals();
data->sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face"));
const AttributeAccessor attributes = mesh->attributes();
data->sharp_faces = *attributes.lookup<bool>("sharp_face", AttrDomain::Face);
if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
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];
/* 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]],
treeData->corner_verts[tri[1]],
treeData->corner_verts[tri[2]]};
@ -1205,6 +1207,9 @@ void BKE_shrinkwrap_compute_smooth_normal(const ShrinkwrapTreeData *tree,
/* Use the face normal if flat. */
else if (!tree->face_normals.is_empty()) {
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. */
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)
{
stream << "(\n";
unroll<NumRow>([&](auto i) {
for (int i = 0; i < NumRow; i++) {
stream << "(";
unroll<NumCol>([&](auto j) {
for (int j = 0; j < NumCol; j++) {
/** NOTE: j and i are swapped to follow mathematical convention. */
stream << mat[j][i];
if (j < NumCol - 1) {
stream << ", ";
}
});
}
stream << ")";
if (i < NumRow - 1) {
stream << ",";
}
stream << "\n";
});
}
stream << ")\n";
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_SURFACE_PER_MAT;
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):
return MBC_WIRE_EDGES;
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);
}
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)) {
DRW_vbo_request(cache.batch.all_verts, &mbuflist->vbo.pos);
DRW_vbo_request(cache.batch.all_verts, &mbuflist->vbo.nor);
}
assert_deps_valid(
MBC_SCULPT_OVERLAYS,

View File

@ -103,6 +103,7 @@ static const asset_system::AssetRepresentation *find_asset_from_weak_ref(
asset_system::all_library_reference());
if (!all_library) {
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);

View File

@ -138,7 +138,7 @@ void AssetView::build_items()
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);
if (!show_names) {
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
{
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;
if (shelf_type.draw_context_menu) {
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
{
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') {
return true;
}
@ -260,7 +260,7 @@ std::unique_ptr<ui::AbstractViewItemDragController> AssetViewItem::create_drag_c
return nullptr;
}
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)
{
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;
bool changed = false;

View File

@ -1154,15 +1154,15 @@ void ED_mesh_split_faces(Mesh *mesh)
const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<bool> mesh_sharp_edges = *attributes.lookup_or_default<bool>(
"sharp_edge", bke::AttrDomain::Edge, false);
const bool *sharp_faces = static_cast<const bool *>(
CustomData_get_layer_named(&mesh->face_data, CD_PROP_BOOL, "sharp_face"));
const VArraySpan<bool> sharp_faces = *attributes.lookup<bool>("sharp_face",
bke::AttrDomain::Face);
Array<bool> sharp_edges(mesh->edges_num);
mesh_sharp_edges.materialize(sharp_edges);
threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange 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])) {
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::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)) {
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::SpanAttributeWriter material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Face);
bool *sharp_faces = static_cast<bool *>(CustomData_get_layer_named_for_write(
&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"));
}
bke::SpanAttributeWriter sharp_faces = attributes.lookup_or_add_for_write_span<bool>(
"sharp_face", bke::AttrDomain::Face);
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
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 */
/* The same for vertices normals. */
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++;
}
@ -743,7 +738,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
if (mp_has_normals) {
/* If it turns out that we have complete custom normals for each poly
* 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) {
/* Store the custom normals for later application. */

View File

@ -1062,4 +1062,14 @@
.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 */

View File

@ -119,6 +119,7 @@ typedef enum ModifierType {
eModifierType_GreasePencilOutline = 82,
eModifierType_GreasePencilShrinkwrap = 83,
eModifierType_GreasePencilBuild = 84,
eModifierType_GreasePencilSimplify = 85,
NUM_MODIFIER_TYPES,
} ModifierType;
@ -3404,3 +3405,28 @@ typedef enum GreasePencilBuildFlag {
MOD_GREASE_PENCIL_BUILD_RESTRICT_TIME = (1 << 0),
MOD_GREASE_PENCIL_BUILD_USE_FADING = (1 << 14),
} 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(GreasePencilTimeModifierSegment);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilSimplifyModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilEnvelopeModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilOutlineModifierData);
SDNA_DEFAULT_DECL_STRUCT(GreasePencilShrinkwrapModifierData);
@ -634,6 +635,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(GreasePencilArmatureModifierData),
SDNA_DEFAULT_DECL(GreasePencilTimeModifierSegment),
SDNA_DEFAULT_DECL(GreasePencilTimeModifierData),
SDNA_DEFAULT_DECL(GreasePencilSimplifyModifierData),
SDNA_DEFAULT_DECL(GreasePencilEnvelopeModifierData),
SDNA_DEFAULT_DECL(GreasePencilOutlineModifierData),
SDNA_DEFAULT_DECL(GreasePencilShrinkwrapModifierData),

View File

@ -259,6 +259,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = {
ICON_GP_MULTIFRAME_EDITING,
"Multiple Strokes",
"Generate multiple strokes around original strokes"},
{eModifierType_GreasePencilSimplify,
"GREASE_PENCIL_SIMPLIFY",
ICON_MOD_SIMPLIFY,
"Simplify",
"Simplify stroke reducing number of points"},
{eModifierType_GreasePencilSubdiv,
"GREASE_PENCIL_SUBDIV",
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(GreasePencilWeightProximity);
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(GreasePencilOutline);
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(GreasePencilHook);
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(GreasePencilShrinkwrap);
@ -9995,6 +10002,89 @@ static void rna_def_modifier_grease_pencil_weight_proximity(BlenderRNA *brna)
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)
{
StructRNA *srna;
@ -10904,6 +10994,7 @@ void RNA_def_modifier(BlenderRNA *brna)
rna_def_modifier_grease_pencil_armature(brna);
rna_def_modifier_grease_pencil_time_segment(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_outline(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_offset.cc
intern/MOD_grease_pencil_opacity.cc
intern/MOD_grease_pencil_simplify.cc
intern/MOD_grease_pencil_outline.cc
intern/MOD_grease_pencil_shrinkwrap.cc
intern/MOD_grease_pencil_smooth.cc

View File

@ -93,6 +93,7 @@ extern ModifierTypeInfo modifierType_GreasePencilHook;
extern ModifierTypeInfo modifierType_GreasePencilLineart;
extern ModifierTypeInfo modifierType_GreasePencilArmature;
extern ModifierTypeInfo modifierType_GreasePencilTime;
extern ModifierTypeInfo modifierType_GreasePencilSimplify;
extern ModifierTypeInfo modifierType_GreasePencilEnvelope;
extern ModifierTypeInfo modifierType_GreasePencilOutline;
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(GreasePencilArmature);
INIT_TYPE(GreasePencilTime);
INIT_TYPE(GreasePencilSimplify);
INIT_TYPE(GreasePencilEnvelope);
INIT_TYPE(GreasePencilOutline);
INIT_TYPE(GreasePencilShrinkwrap);

View File

@ -848,6 +848,28 @@ static void engine_render_view_layer(Render *re,
if (use_gpu_context) {
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) {
engine->type->update(engine, re->main, engine->depsgraph);
}