WIP: Brush assets project #106303
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue