GPv3: LineArt: Baking operators
This patch implements all baking operators for LineArt for GPv3. The old baking operators are renamed with `_legacy_` to distinguishing both when calling the operator. Pull Request: https://projects.blender.org/blender/blender/pulls/125079
This commit is contained in:
@@ -14,6 +14,8 @@ set(INC
|
||||
../../../../extern/curve_fit_nd
|
||||
../../geometry
|
||||
../sculpt_paint
|
||||
../../gpencil_modifiers_legacy
|
||||
../../gpencil_modifiers_legacy/intern/lineart
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
@@ -29,6 +31,7 @@ set(SRC
|
||||
intern/grease_pencil_geom.cc
|
||||
intern/grease_pencil_image_render.cc
|
||||
intern/grease_pencil_layers.cc
|
||||
intern/grease_pencil_lineart.cc
|
||||
intern/grease_pencil_material.cc
|
||||
intern/grease_pencil_ops.cc
|
||||
intern/grease_pencil_primitive.cc
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edgreasepencil
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_kdtree.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_offset_indices.hh"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
/* For shared legacy cache function, will be removed when v2 is removed. */
|
||||
#include "MOD_gpencil_legacy_lineart.h"
|
||||
#include "MOD_lineart.h"
|
||||
|
||||
namespace blender::ed::greasepencil {
|
||||
|
||||
void get_lineart_modifier_limits(const Object &ob,
|
||||
blender::ed::greasepencil::LineartLimitInfo &info)
|
||||
{
|
||||
bool is_first = true;
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilLineart) {
|
||||
const auto *lmd = reinterpret_cast<const GreasePencilLineartModifierData *>(md);
|
||||
if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) {
|
||||
info.min_level = std::min<int>(info.min_level, lmd->level_start);
|
||||
info.max_level = std::max<int>(
|
||||
info.max_level, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
|
||||
info.edge_types |= lmd->edge_types;
|
||||
info.shadow_selection = std::max(info.shadow_selection, lmd->shadow_selection);
|
||||
info.silhouette_selection = std::max(info.silhouette_selection, lmd->silhouette_selection);
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_lineart_modifier_limits(GreasePencilLineartModifierData &lmd,
|
||||
const blender::ed::greasepencil::LineartLimitInfo &info,
|
||||
const bool is_first_lineart)
|
||||
{
|
||||
BLI_assert(lmd.modifier.type == eModifierType_GreasePencilLineart);
|
||||
if (is_first_lineart || lmd.flags & MOD_LINEART_USE_CACHE) {
|
||||
lmd.level_start_override = info.min_level;
|
||||
lmd.level_end_override = info.max_level;
|
||||
lmd.edge_types_override = info.edge_types;
|
||||
lmd.shadow_selection_override = info.shadow_selection;
|
||||
lmd.shadow_use_silhouette_override = info.silhouette_selection;
|
||||
}
|
||||
else {
|
||||
lmd.level_start_override = lmd.level_start;
|
||||
lmd.level_end_override = lmd.level_end;
|
||||
lmd.edge_types_override = lmd.edge_types;
|
||||
lmd.shadow_selection_override = lmd.shadow_selection;
|
||||
lmd.shadow_use_silhouette_override = lmd.silhouette_selection;
|
||||
}
|
||||
}
|
||||
|
||||
GreasePencilLineartModifierData *get_first_lineart_modifier(const Object &ob)
|
||||
{
|
||||
LISTBASE_FOREACH (ModifierData *, i_md, &ob.modifiers) {
|
||||
if (i_md->type == eModifierType_GreasePencilLineart) {
|
||||
return reinterpret_cast<GreasePencilLineartModifierData *>(i_md);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
struct LineartBakeJob {
|
||||
wmWindowManager *wm;
|
||||
void *owner;
|
||||
bool *stop, *do_update;
|
||||
float *progress;
|
||||
|
||||
/* C or ob must have one != nullptr. */
|
||||
bContext *C;
|
||||
blender::Vector<Object *> objects;
|
||||
Scene *scene;
|
||||
Depsgraph *dg;
|
||||
int frame;
|
||||
int frame_begin;
|
||||
int frame_end;
|
||||
int frame_orig;
|
||||
int frame_increment;
|
||||
bool overwrite_frames;
|
||||
};
|
||||
|
||||
static bool clear_strokes(Object *ob, ModifierData *md, int frame)
|
||||
{
|
||||
if (md->type != eModifierType_GreasePencilLineart) {
|
||||
return false;
|
||||
}
|
||||
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
|
||||
|
||||
blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
|
||||
if (!node || !node->is_layer()) {
|
||||
return false;
|
||||
}
|
||||
blender::bke::greasepencil::Layer &layer = node->as_layer();
|
||||
|
||||
if (layer.start_frame_at(frame) == frame) {
|
||||
blender::bke::greasepencil::Drawing *drawing = grease_pencil->get_drawing_at(layer, frame);
|
||||
if (!drawing) {
|
||||
return false;
|
||||
}
|
||||
drawing->strokes_for_write() = {};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lineart_mod_is_disabled(Scene *scene, GreasePencilLineartModifierData *md)
|
||||
{
|
||||
BLI_assert(md->modifier.type == eModifierType_GreasePencilLineart);
|
||||
|
||||
/* Toggle on and off the baked flag as we are only interested in if something else is disabling
|
||||
* it. We can assume that the guard function has already toggled this on for all modifiers that
|
||||
* are sent here. */
|
||||
md->flags &= (~MOD_LINEART_IS_BAKED);
|
||||
|
||||
bool enabled = BKE_modifier_is_enabled(
|
||||
scene, &md->modifier, eModifierMode_Render | eModifierMode_Realtime);
|
||||
|
||||
md->flags |= MOD_LINEART_IS_BAKED;
|
||||
|
||||
return !enabled;
|
||||
}
|
||||
|
||||
static bool bake_strokes(Object *ob,
|
||||
Depsgraph *dg,
|
||||
LineartCache **lc,
|
||||
GreasePencilLineartModifierData *md,
|
||||
int frame,
|
||||
bool is_first)
|
||||
{
|
||||
/* Modifier data sanity check. */
|
||||
if (lineart_mod_is_disabled(DEG_get_evaluated_scene(dg), md)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
|
||||
|
||||
blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(lmd->target_layer);
|
||||
if (!node || !node->is_layer()) {
|
||||
return false;
|
||||
}
|
||||
blender::bke::greasepencil::Layer &layer = node->as_layer();
|
||||
|
||||
blender::bke::greasepencil::Drawing *drawing = nullptr;
|
||||
if (layer.start_frame_at(frame) == frame) {
|
||||
drawing = grease_pencil->get_drawing_at(layer, frame);
|
||||
}
|
||||
else {
|
||||
drawing = grease_pencil->insert_frame(layer, frame);
|
||||
}
|
||||
if (UNLIKELY(!drawing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LineartCache *local_lc = *lc;
|
||||
if (!(*lc)) {
|
||||
MOD_lineart_compute_feature_lines_v3(dg, *lmd, lc, !(ob->dtx & OB_DRAW_IN_FRONT));
|
||||
MOD_lineart_destroy_render_data_v3(lmd);
|
||||
}
|
||||
else {
|
||||
if (is_first || !(lmd->flags & MOD_LINEART_USE_CACHE)) {
|
||||
MOD_lineart_compute_feature_lines_v3(dg, *lmd, &local_lc, !(ob->dtx & OB_DRAW_IN_FRONT));
|
||||
MOD_lineart_destroy_render_data_v3(lmd);
|
||||
}
|
||||
MOD_lineart_chain_clear_picked_flag(local_lc);
|
||||
lmd->cache = local_lc;
|
||||
}
|
||||
|
||||
MOD_lineart_gpencil_generate_v3(
|
||||
lmd->cache,
|
||||
ob->object_to_world(),
|
||||
dg,
|
||||
*drawing,
|
||||
lmd->source_type,
|
||||
lmd->source_object,
|
||||
lmd->source_collection,
|
||||
lmd->level_start,
|
||||
lmd->use_multiple_levels ? lmd->level_end : lmd->level_start,
|
||||
lmd->target_material ? BKE_object_material_index_get(ob, lmd->target_material) : 0,
|
||||
lmd->edge_types,
|
||||
lmd->mask_switches,
|
||||
lmd->material_mask_bits,
|
||||
lmd->intersection_mask,
|
||||
float(lmd->thickness) / 1000.0f,
|
||||
lmd->opacity,
|
||||
lmd->shadow_selection,
|
||||
lmd->silhouette_selection,
|
||||
lmd->source_vertex_group,
|
||||
lmd->vgname,
|
||||
lmd->flags,
|
||||
lmd->calculation_flags);
|
||||
|
||||
if (!(lmd->flags & MOD_LINEART_USE_CACHE)) {
|
||||
/* Clear local cache. */
|
||||
if (!is_first) {
|
||||
MOD_lineart_clear_cache(&local_lc);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bake_single_target(LineartBakeJob *bj, Object *ob, int frame)
|
||||
{
|
||||
bool touched = false;
|
||||
if (G.is_break || ob->type != OB_GREASE_PENCIL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bj->overwrite_frames) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilLineart) {
|
||||
if (clear_strokes(ob, md, frame)) {
|
||||
touched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blender::ed::greasepencil::LineartLimitInfo info;
|
||||
blender::ed::greasepencil::get_lineart_modifier_limits(*ob, info);
|
||||
|
||||
LineartCache *lc = nullptr;
|
||||
bool is_first = true;
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_GreasePencilLineart) {
|
||||
continue;
|
||||
}
|
||||
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
|
||||
blender::ed::greasepencil::set_lineart_modifier_limits(*lmd, info, is_first);
|
||||
|
||||
if (bake_strokes(ob, bj->dg, &lc, lmd, frame, is_first)) {
|
||||
touched = true;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
MOD_lineart_clear_cache(&lc);
|
||||
|
||||
return touched;
|
||||
}
|
||||
|
||||
static void guard_modifiers(LineartBakeJob &bj)
|
||||
{
|
||||
for (const int object : bj.objects.index_range()) {
|
||||
Object *ob = bj.objects[object];
|
||||
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilLineart) {
|
||||
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(
|
||||
md);
|
||||
lmd->flags |= MOD_LINEART_IS_BAKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lineart_bake_startjob(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
{
|
||||
LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
|
||||
bj->stop = &worker_status->stop;
|
||||
bj->do_update = &worker_status->do_update;
|
||||
bj->progress = &worker_status->progress;
|
||||
|
||||
guard_modifiers(*bj);
|
||||
|
||||
for (int frame = bj->frame_begin; frame <= bj->frame_end; frame += bj->frame_increment) {
|
||||
|
||||
if (G.is_break) {
|
||||
G.is_break = false;
|
||||
break;
|
||||
}
|
||||
|
||||
BKE_scene_frame_set(bj->scene, frame);
|
||||
BKE_scene_graph_update_for_newframe(bj->dg);
|
||||
DEG_graph_build_from_view_layer(bj->dg);
|
||||
|
||||
for (const int object : bj->objects.index_range()) {
|
||||
Object *ob = bj->objects[object];
|
||||
if (bake_single_target(bj, ob, frame)) {
|
||||
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(bj->C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update and refresh the progress bar. */
|
||||
*bj->progress = float(frame - bj->frame_begin) / (bj->frame_end - bj->frame_begin);
|
||||
*bj->do_update = true;
|
||||
}
|
||||
|
||||
/* This need to be reset manually. */
|
||||
G.is_break = false;
|
||||
|
||||
/* Restore original frame. */
|
||||
BKE_scene_frame_set(bj->scene, bj->frame_orig);
|
||||
BKE_scene_graph_update_for_newframe(bj->dg);
|
||||
}
|
||||
|
||||
static void lineart_bake_endjob(void *customdata)
|
||||
{
|
||||
LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
|
||||
|
||||
WM_set_locked_interface(CTX_wm_manager(bj->C), false);
|
||||
|
||||
WM_main_add_notifier(NC_SCENE | ND_FRAME, bj->scene);
|
||||
|
||||
for (const int object : bj->objects.index_range()) {
|
||||
Object *ob = bj->objects[object];
|
||||
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, ob);
|
||||
}
|
||||
}
|
||||
|
||||
static void lineart_bake_job_free(void *customdata)
|
||||
{
|
||||
LineartBakeJob *bj = static_cast<LineartBakeJob *>(customdata);
|
||||
MEM_delete(bj);
|
||||
}
|
||||
|
||||
static int lineart_bake_common(bContext *C,
|
||||
wmOperator *op,
|
||||
bool bake_all_targets,
|
||||
bool do_background)
|
||||
{
|
||||
LineartBakeJob *bj = MEM_new<LineartBakeJob>(__func__);
|
||||
|
||||
if (!bake_all_targets) {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (!ob || ob->type != OB_GREASE_PENCIL) {
|
||||
WM_report(RPT_ERROR, "No active object, or active object isn't a Grease Pencil object");
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
bj->objects.append(ob);
|
||||
}
|
||||
else {
|
||||
/* #CTX_DATA_BEGIN is not available for iterating in objects while using the job system. */
|
||||
CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilLineart) {
|
||||
bj->objects.append(ob);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
}
|
||||
bj->C = C;
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
bj->scene = scene;
|
||||
bj->dg = CTX_data_depsgraph_pointer(C);
|
||||
bj->frame_begin = scene->r.sfra;
|
||||
bj->frame_end = scene->r.efra;
|
||||
bj->frame_orig = scene->r.cfra;
|
||||
bj->frame_increment = scene->r.frame_step;
|
||||
bj->overwrite_frames = true;
|
||||
|
||||
if (do_background) {
|
||||
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
|
||||
CTX_wm_window(C),
|
||||
scene,
|
||||
"Line Art",
|
||||
WM_JOB_PROGRESS,
|
||||
WM_JOB_TYPE_LINEART);
|
||||
|
||||
WM_jobs_customdata_set(wm_job, bj, lineart_bake_job_free);
|
||||
WM_jobs_timer(wm_job, 0.1, NC_GPENCIL | ND_DATA | NA_EDITED, NC_GPENCIL | ND_DATA | NA_EDITED);
|
||||
WM_jobs_callbacks(wm_job, lineart_bake_startjob, nullptr, nullptr, lineart_bake_endjob);
|
||||
|
||||
WM_set_locked_interface(CTX_wm_manager(C), true);
|
||||
|
||||
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
wmJobWorkerStatus worker_status = {};
|
||||
lineart_bake_startjob(bj, &worker_status);
|
||||
|
||||
MEM_delete(bj);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int lineart_bake_strokes_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
|
||||
return lineart_bake_common(C, op, bake_all, true);
|
||||
}
|
||||
static int lineart_bake_strokes_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bool bake_all = RNA_boolean_get(op->ptr, "bake_all");
|
||||
return lineart_bake_common(C, op, bake_all, false);
|
||||
}
|
||||
static int lineart_bake_strokes_common_modal(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
Scene *scene = static_cast<Scene *>(op->customdata);
|
||||
|
||||
/* no running blender, remove handler and pass through. */
|
||||
if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LINEART) == 0) {
|
||||
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
|
||||
{
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type != eModifierType_GreasePencilLineart) {
|
||||
continue;
|
||||
}
|
||||
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
|
||||
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ob->data);
|
||||
|
||||
blender::bke::greasepencil::TreeNode *node = grease_pencil->find_node_by_name(
|
||||
lmd->target_layer);
|
||||
if (!node || !node->is_layer()) {
|
||||
return;
|
||||
}
|
||||
blender::bke::greasepencil::Layer &layer = node->as_layer();
|
||||
|
||||
/* Remove all the keyframes in this layer. */
|
||||
grease_pencil->remove_frames(layer, layer.sorted_keys());
|
||||
grease_pencil->insert_frame(layer, 0);
|
||||
|
||||
md->mode |= eModifierMode_Realtime | eModifierMode_Render;
|
||||
|
||||
lmd->flags &= (~MOD_LINEART_IS_BAKED);
|
||||
}
|
||||
DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
static int lineart_gpencil_clear_strokes_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bool clear_all = RNA_boolean_get(op->ptr, "clear_all");
|
||||
|
||||
if (clear_all) {
|
||||
CTX_DATA_BEGIN (C, Object *, ob, visible_objects) {
|
||||
if (ob->type != OB_GREASE_PENCIL) {
|
||||
continue;
|
||||
}
|
||||
lineart_gpencil_clear_strokes_exec_common(ob);
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
BKE_report(op->reports, RPT_INFO, "All Line Art objects are now cleared of bakes");
|
||||
}
|
||||
else {
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (ob->type != OB_GREASE_PENCIL) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
lineart_gpencil_clear_strokes_exec_common(ob);
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, ob);
|
||||
BKE_report(op->reports, RPT_INFO, "Baked strokes are cleared");
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Bake Line Art";
|
||||
ot->description = "Bake Line Art for current Grease Pencil object";
|
||||
ot->idname = "OBJECT_OT_lineart_bake_strokes";
|
||||
|
||||
ot->poll = blender::ed::greasepencil::active_grease_pencil_poll;
|
||||
ot->invoke = lineart_bake_strokes_invoke;
|
||||
ot->exec = lineart_bake_strokes_exec;
|
||||
ot->modal = lineart_bake_strokes_common_modal;
|
||||
|
||||
RNA_def_boolean(ot->srna, "bake_all", false, "Bake All", "Bake all line art modifiers");
|
||||
}
|
||||
|
||||
static void OBJECT_OT_lineart_clear(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Clear Baked Line Art";
|
||||
ot->description = "Clear all strokes in current Grease Pencil object";
|
||||
ot->idname = "OBJECT_OT_lineart_clear";
|
||||
|
||||
ot->poll = blender::ed::greasepencil::active_grease_pencil_poll;
|
||||
ot->exec = lineart_gpencil_clear_strokes_exec;
|
||||
|
||||
RNA_def_boolean(ot->srna, "clear_all", false, "Clear All", "Clear all line art modifier bakes");
|
||||
}
|
||||
|
||||
void ED_operatortypes_grease_pencil_lineart()
|
||||
{
|
||||
WM_operatortype_append(OBJECT_OT_lineart_bake_strokes);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_clear);
|
||||
}
|
||||
@@ -200,6 +200,7 @@ void ED_operatortypes_grease_pencil()
|
||||
ED_operatortypes_grease_pencil_primitives();
|
||||
ED_operatortypes_grease_pencil_weight_paint();
|
||||
ED_operatortypes_grease_pencil_interpolate();
|
||||
ED_operatortypes_grease_pencil_lineart();
|
||||
ED_operatortypes_grease_pencil_trace();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct UndoType;
|
||||
struct ViewDepths;
|
||||
struct View3D;
|
||||
struct ViewContext;
|
||||
struct GreasePencilLineartModifierData;
|
||||
namespace blender {
|
||||
namespace bke {
|
||||
enum class AttrDomain : int8_t;
|
||||
@@ -59,6 +60,7 @@ void ED_operatortypes_grease_pencil_material();
|
||||
void ED_operatortypes_grease_pencil_primitives();
|
||||
void ED_operatortypes_grease_pencil_weight_paint();
|
||||
void ED_operatortypes_grease_pencil_interpolate();
|
||||
void ED_operatortypes_grease_pencil_lineart();
|
||||
void ED_operatortypes_grease_pencil_trace();
|
||||
void ED_operatormacros_grease_pencil();
|
||||
void ED_keymap_grease_pencil(wmKeyConfig *keyconf);
|
||||
@@ -655,4 +657,23 @@ bke::CurvesGeometry trim_curve_segments(const bke::CurvesGeometry &src,
|
||||
bool keep_caps);
|
||||
}; // namespace cutter
|
||||
|
||||
/* Lineart */
|
||||
|
||||
/* Stores the maximum calculation range in the whole modifier stack for line art so the cache can
|
||||
* cover everything that will be visible. */
|
||||
struct LineartLimitInfo {
|
||||
int16_t edge_types;
|
||||
uint8_t min_level;
|
||||
uint8_t max_level;
|
||||
uint8_t shadow_selection;
|
||||
uint8_t silhouette_selection;
|
||||
};
|
||||
|
||||
void get_lineart_modifier_limits(const Object &ob, LineartLimitInfo &info);
|
||||
void set_lineart_modifier_limits(GreasePencilLineartModifierData &lmd,
|
||||
const LineartLimitInfo &info,
|
||||
const bool is_first_lineart);
|
||||
|
||||
GreasePencilLineartModifierData *get_first_lineart_modifier(const Object &ob);
|
||||
|
||||
} // namespace blender::ed::greasepencil
|
||||
|
||||
@@ -171,7 +171,7 @@ void operatortypes_object()
|
||||
WM_operatortype_append(GPENCIL_OT_time_segment_move);
|
||||
|
||||
/* grease pencil line art */
|
||||
WM_operatortypes_lineart();
|
||||
WM_operatortypes_lineart_legacy();
|
||||
|
||||
/* Shader FX. */
|
||||
WM_operatortype_append(OBJECT_OT_shaderfx_add);
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void OBJECT_OT_lineart_bake_strokes(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_bake_strokes_all(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_clear(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_clear_all(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_legacy_bake_strokes(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_legacy_bake_strokes_all(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_legacy_clear(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_lineart_legacy_clear_all(struct wmOperatorType *ot);
|
||||
|
||||
void WM_operatortypes_lineart(void);
|
||||
void WM_operatortypes_lineart_legacy(void);
|
||||
|
||||
struct LineartCache;
|
||||
|
||||
|
||||
@@ -763,12 +763,12 @@ static void bake_panel_draw(const bContext * /*C*/, Panel *panel)
|
||||
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetEnabled(col, !is_baked);
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes_all");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_legacy_bake_strokes");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_legacy_bake_strokes_all");
|
||||
|
||||
col = uiLayoutColumn(layout, false);
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear_all");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_legacy_clear");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_legacy_clear_all");
|
||||
}
|
||||
|
||||
static void composition_panel_draw(const bContext * /*C*/, Panel *panel)
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
@@ -423,18 +425,8 @@ typedef struct LineartData {
|
||||
|
||||
} LineartData;
|
||||
|
||||
/* Stores the maximum calculation range in the whole modifier stack for line art so the cache can
|
||||
* cover everything that will be visible. */
|
||||
struct GreasePencilLineartLimitInfo {
|
||||
int16_t edge_types;
|
||||
uint8_t min_level;
|
||||
uint8_t max_level;
|
||||
uint8_t shadow_selection;
|
||||
uint8_t silhouette_selection;
|
||||
};
|
||||
|
||||
typedef struct LineartCache {
|
||||
GreasePencilLineartLimitInfo LimitInfo;
|
||||
blender::ed::greasepencil::LineartLimitInfo LimitInfo;
|
||||
/** Separate memory pool for chain data and shadow, this goes to the cache, so when we free the
|
||||
* main pool, chains and shadows will still be available. */
|
||||
LineartStaticMemPool chain_data_pool;
|
||||
|
||||
@@ -434,50 +434,50 @@ static int lineart_gpencil_clear_strokes_all_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_lineart_bake_strokes(wmOperatorType *ot)
|
||||
void OBJECT_OT_lineart_legacy_bake_strokes(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Bake Line Art";
|
||||
ot->description = "Bake Line Art for current Grease Pencil object";
|
||||
ot->idname = "OBJECT_OT_lineart_bake_strokes";
|
||||
ot->idname = "OBJECT_OT_lineart_legacy_bake_strokes";
|
||||
|
||||
ot->invoke = lineart_gpencil_bake_strokes_invoke;
|
||||
ot->exec = lineart_gpencil_bake_strokes_exec;
|
||||
ot->modal = lineart_gpencil_bake_strokes_common_modal;
|
||||
}
|
||||
|
||||
void OBJECT_OT_lineart_bake_strokes_all(wmOperatorType *ot)
|
||||
void OBJECT_OT_lineart_legacy_bake_strokes_all(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Bake Line Art (All)";
|
||||
ot->description = "Bake all Grease Pencil objects that have a Line Art modifier";
|
||||
ot->idname = "OBJECT_OT_lineart_bake_strokes_all";
|
||||
ot->idname = "OBJECT_OT_lineart_legacy_bake_strokes_all";
|
||||
|
||||
ot->invoke = lineart_gpencil_bake_strokes_all_invoke;
|
||||
ot->exec = lineart_gpencil_bake_strokes_all_exec;
|
||||
ot->modal = lineart_gpencil_bake_strokes_common_modal;
|
||||
}
|
||||
|
||||
void OBJECT_OT_lineart_clear(wmOperatorType *ot)
|
||||
void OBJECT_OT_lineart_legacy_clear(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Clear Baked Line Art";
|
||||
ot->description = "Clear all strokes in current Grease Pencil object";
|
||||
ot->idname = "OBJECT_OT_lineart_clear";
|
||||
ot->idname = "OBJECT_OT_lineart_legacy_clear";
|
||||
|
||||
ot->exec = lineart_gpencil_clear_strokes_exec;
|
||||
}
|
||||
|
||||
void OBJECT_OT_lineart_clear_all(wmOperatorType *ot)
|
||||
void OBJECT_OT_lineart_legacy_clear_all(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Clear Baked Line Art (All)";
|
||||
ot->description = "Clear all strokes in all Grease Pencil objects that have a Line Art modifier";
|
||||
ot->idname = "OBJECT_OT_lineart_clear_all";
|
||||
ot->idname = "OBJECT_OT_lineart_legacy_clear_all";
|
||||
|
||||
ot->exec = lineart_gpencil_clear_strokes_all_exec;
|
||||
}
|
||||
|
||||
void WM_operatortypes_lineart()
|
||||
void WM_operatortypes_lineart_legacy()
|
||||
{
|
||||
WM_operatortype_append(OBJECT_OT_lineart_bake_strokes);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_bake_strokes_all);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_clear);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_clear_all);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_legacy_bake_strokes);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_legacy_bake_strokes_all);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_legacy_clear);
|
||||
WM_operatortype_append(OBJECT_OT_lineart_legacy_clear_all);
|
||||
}
|
||||
|
||||
@@ -37,48 +37,10 @@
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_grease_pencil.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void get_lineart_modifier_limits(const Object &ob, GreasePencilLineartLimitInfo &info)
|
||||
{
|
||||
bool is_first = true;
|
||||
LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) {
|
||||
if (md->type == eModifierType_GreasePencilLineart) {
|
||||
const auto *lmd = reinterpret_cast<const GreasePencilLineartModifierData *>(md);
|
||||
if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) {
|
||||
info.min_level = std::min<int>(info.min_level, lmd->level_start);
|
||||
info.max_level = std::max<int>(
|
||||
info.max_level, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start);
|
||||
info.edge_types |= lmd->edge_types;
|
||||
info.shadow_selection = std::max(info.shadow_selection, lmd->shadow_selection);
|
||||
info.silhouette_selection = std::max(info.silhouette_selection, lmd->silhouette_selection);
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void set_lineart_modifier_limits(GreasePencilLineartModifierData &lmd,
|
||||
const GreasePencilLineartLimitInfo &info,
|
||||
const bool is_first_lineart)
|
||||
{
|
||||
BLI_assert(lmd.modifier.type == eModifierType_GreasePencilLineart);
|
||||
if (is_first_lineart || lmd.flags & MOD_LINEART_USE_CACHE) {
|
||||
lmd.level_start_override = info.min_level;
|
||||
lmd.level_end_override = info.max_level;
|
||||
lmd.edge_types_override = info.edge_types;
|
||||
lmd.shadow_selection_override = info.shadow_selection;
|
||||
lmd.shadow_use_silhouette_override = info.silhouette_selection;
|
||||
}
|
||||
else {
|
||||
lmd.level_start_override = lmd.level_start;
|
||||
lmd.level_end_override = lmd.level_end;
|
||||
lmd.edge_types_override = lmd.edge_types;
|
||||
lmd.shadow_selection_override = lmd.shadow_selection;
|
||||
lmd.shadow_use_silhouette_override = lmd.silhouette_selection;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_first_lineart(const GreasePencilLineartModifierData &md)
|
||||
{
|
||||
if (md.modifier.type != eModifierType_GreasePencilLineart) {
|
||||
@@ -109,16 +71,6 @@ static bool is_last_line_art(const GreasePencilLineartModifierData &md)
|
||||
return true;
|
||||
}
|
||||
|
||||
static GreasePencilLineartModifierData *get_first_lineart_modifier(const Object &ob)
|
||||
{
|
||||
LISTBASE_FOREACH (ModifierData *, i_md, &ob.modifiers) {
|
||||
if (i_md->type == eModifierType_GreasePencilLineart) {
|
||||
return reinterpret_cast<GreasePencilLineartModifierData *>(i_md);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void init_data(ModifierData *md)
|
||||
{
|
||||
GreasePencilLineartModifierData *gpmd = (GreasePencilLineartModifierData *)md;
|
||||
@@ -693,11 +645,13 @@ static void bake_panel_draw(const bContext * /*C*/, Panel *panel)
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiLayoutSetEnabled(col, !is_baked);
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes_all");
|
||||
uiItemBooleanO(
|
||||
col, IFACE_("Bake All"), ICON_NONE, "OBJECT_OT_lineart_bake_strokes", "bake_all", true);
|
||||
|
||||
col = uiLayoutColumn(layout, false);
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear");
|
||||
uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear_all");
|
||||
uiItemBooleanO(
|
||||
col, IFACE_("Clear All"), ICON_NONE, "OBJECT_OT_lineart_clear", "clear_all", true);
|
||||
}
|
||||
|
||||
static void composition_panel_draw(const bContext * /*C*/, Panel *panel)
|
||||
@@ -848,16 +802,18 @@ static void modify_geometry_set(ModifierData *md,
|
||||
GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
|
||||
auto mmd = reinterpret_cast<GreasePencilLineartModifierData *>(md);
|
||||
|
||||
GreasePencilLineartModifierData *first_lineart = get_first_lineart_modifier(*ctx->object);
|
||||
GreasePencilLineartModifierData *first_lineart =
|
||||
blender::ed::greasepencil::get_first_lineart_modifier(*ctx->object);
|
||||
BLI_assert(first_lineart);
|
||||
|
||||
bool is_first_lineart = (mmd == first_lineart);
|
||||
|
||||
if (is_first_lineart) {
|
||||
mmd->shared_cache = MOD_lineart_init_cache();
|
||||
get_lineart_modifier_limits(*ctx->object, mmd->shared_cache->LimitInfo);
|
||||
ed::greasepencil::get_lineart_modifier_limits(*ctx->object, mmd->shared_cache->LimitInfo);
|
||||
}
|
||||
set_lineart_modifier_limits(*mmd, first_lineart->shared_cache->LimitInfo, is_first_lineart);
|
||||
ed::greasepencil::set_lineart_modifier_limits(
|
||||
*mmd, first_lineart->shared_cache->LimitInfo, is_first_lineart);
|
||||
|
||||
generate_strokes(*md, *ctx, grease_pencil, *first_lineart);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user