1037 lines
34 KiB
C++
1037 lines
34 KiB
C++
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include <cstdio>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_math_geom.h"
|
|
#include "BLI_math_matrix.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_string_utils.hh"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_gpencil_legacy_types.h"
|
|
#include "DNA_gpencil_modifier_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "BKE_colortools.h"
|
|
#include "BKE_deform.h"
|
|
#include "BKE_gpencil_geom_legacy.h"
|
|
#include "BKE_gpencil_legacy.h"
|
|
#include "BKE_gpencil_modifier_legacy.h"
|
|
#include "BKE_lattice.hh"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_lib_query.h"
|
|
#include "BKE_material.h"
|
|
#include "BKE_modifier.hh"
|
|
#include "BKE_object.hh"
|
|
#include "BKE_object_types.hh"
|
|
#include "BKE_screen.hh"
|
|
#include "BKE_shrinkwrap.h"
|
|
|
|
#include "DEG_depsgraph.hh"
|
|
#include "DEG_depsgraph_query.hh"
|
|
|
|
#include "MOD_gpencil_legacy_lineart.h"
|
|
#include "MOD_gpencil_legacy_modifiertypes.h"
|
|
|
|
#include "BLO_read_write.hh"
|
|
|
|
#include "CLG_log.h"
|
|
|
|
static CLG_LogRef LOG = {"bke.gpencil_modifier"};
|
|
static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {
|
|
nullptr};
|
|
#if 0
|
|
/* Note that GPencil actually does not support these at the moment, but might do in the future. */
|
|
static GpencilVirtualModifierData virtualModifierCommonData;
|
|
#endif
|
|
|
|
/* Lattice Modifier ---------------------------------- */
|
|
/* Usually, evaluation of the lattice modifier is self-contained.
|
|
* However, since GP's modifiers operate on a per-stroke basis,
|
|
* we need to these two extra functions that called before/after
|
|
* each loop over all the geometry being evaluated.
|
|
*/
|
|
|
|
void BKE_gpencil_cache_data_init(Depsgraph *depsgraph, Object *ob)
|
|
{
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
switch (md->type) {
|
|
case eGpencilModifierType_Lattice: {
|
|
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
|
Object *latob = nullptr;
|
|
|
|
latob = mmd->object;
|
|
if ((!latob) || (latob->type != OB_LATTICE)) {
|
|
return;
|
|
}
|
|
if (mmd->cache_data) {
|
|
BKE_lattice_deform_data_destroy(mmd->cache_data);
|
|
}
|
|
|
|
/* init deform data */
|
|
mmd->cache_data = BKE_lattice_deform_data_create(latob, ob);
|
|
break;
|
|
}
|
|
case eGpencilModifierType_Shrinkwrap: {
|
|
ShrinkwrapGpencilModifierData *mmd = (ShrinkwrapGpencilModifierData *)md;
|
|
ob = mmd->target;
|
|
if (!ob) {
|
|
return;
|
|
}
|
|
if (mmd->cache_data) {
|
|
BKE_shrinkwrap_free_tree(mmd->cache_data);
|
|
MEM_SAFE_FREE(mmd->cache_data);
|
|
}
|
|
Object *ob_target = DEG_get_evaluated_object(depsgraph, ob);
|
|
Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
|
|
mmd->cache_data = static_cast<ShrinkwrapTreeData *>(
|
|
MEM_callocN(sizeof(ShrinkwrapTreeData), __func__));
|
|
if (BKE_shrinkwrap_init_tree(
|
|
mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false)) {
|
|
}
|
|
else {
|
|
MEM_SAFE_FREE(mmd->cache_data);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_cache_data_clear(Object *ob)
|
|
{
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
switch (md->type) {
|
|
case eGpencilModifierType_Lattice: {
|
|
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
|
if ((mmd) && (mmd->cache_data)) {
|
|
BKE_lattice_deform_data_destroy(mmd->cache_data);
|
|
mmd->cache_data = nullptr;
|
|
}
|
|
break;
|
|
}
|
|
case eGpencilModifierType_Shrinkwrap: {
|
|
ShrinkwrapGpencilModifierData *mmd = (ShrinkwrapGpencilModifierData *)md;
|
|
if ((mmd) && (mmd->cache_data)) {
|
|
BKE_shrinkwrap_free_tree(mmd->cache_data);
|
|
MEM_SAFE_FREE(mmd->cache_data);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************************** */
|
|
/* Modifier Methods - Evaluation Loops, etc. */
|
|
|
|
GpencilModifierData *BKE_gpencil_modifiers_get_virtual_modifierlist(
|
|
const Object *ob, GpencilVirtualModifierData * /*virtual_modifier_data*/)
|
|
{
|
|
GpencilModifierData *md = static_cast<GpencilModifierData *>(ob->greasepencil_modifiers.first);
|
|
|
|
#if 0
|
|
/* Note that GPencil actually does not support these at the moment,
|
|
* but might do in the future. */
|
|
*virtual_modifier_data = virtualModifierCommonData;
|
|
if (ob->parent) {
|
|
if (ob->parent->type == OB_ARMATURE && ob->partype == PARSKEL) {
|
|
virtual_modifier_data->amd.object = ob->parent;
|
|
virtual_modifier_data->amd.modifier.next = md;
|
|
virtual_modifier_data->amd.deformflag = ((bArmature *)(ob->parent->data))->deformflag;
|
|
md = &virtual_modifier_data->amd.modifier;
|
|
}
|
|
else if (ob->parent->type == OB_LATTICE && ob->partype == PARSKEL) {
|
|
virtual_modifier_data->lmd.object = ob->parent;
|
|
virtual_modifier_data->lmd.modifier.next = md;
|
|
md = &virtual_modifier_data->lmd.modifier;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return md;
|
|
}
|
|
|
|
bool BKE_gpencil_has_geometry_modifiers(Object *ob)
|
|
{
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (mti && mti->generate_strokes) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BKE_gpencil_has_time_modifiers(Object *ob)
|
|
{
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (mti && mti->remap_time) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BKE_gpencil_has_transform_modifiers(Object *ob)
|
|
{
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
/* Only if enabled in edit mode. */
|
|
if (!GPENCIL_MODIFIER_EDIT(md, true) && GPENCIL_MODIFIER_ACTIVE(md, false)) {
|
|
if (ELEM(md->type,
|
|
eGpencilModifierType_Armature,
|
|
eGpencilModifierType_Hook,
|
|
eGpencilModifierType_Lattice,
|
|
eGpencilModifierType_Offset))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const Object *ob)
|
|
{
|
|
GpencilLineartLimitInfo info = {0};
|
|
bool is_first = true;
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
if (md->type == eGpencilModifierType_Lineart) {
|
|
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
|
|
if (is_first || (lmd->flags & LRT_GPENCIL_USE_CACHE)) {
|
|
info.min_level = MIN2(info.min_level, lmd->level_start);
|
|
info.max_level = MAX2(info.max_level,
|
|
(lmd->use_multiple_levels ? lmd->level_end : lmd->level_start));
|
|
info.edge_types |= lmd->edge_types;
|
|
info.shadow_selection = MAX2(lmd->shadow_selection, info.shadow_selection);
|
|
info.silhouette_selection = MAX2(lmd->silhouette_selection, info.silhouette_selection);
|
|
is_first = false;
|
|
}
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
void BKE_gpencil_set_lineart_modifier_limits(GpencilModifierData *md,
|
|
const GpencilLineartLimitInfo *info,
|
|
const bool is_first_lineart)
|
|
{
|
|
BLI_assert(md->type == eGpencilModifierType_Lineart);
|
|
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
|
|
if (is_first_lineart || lmd->flags & LRT_GPENCIL_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;
|
|
}
|
|
}
|
|
|
|
bool BKE_gpencil_is_first_lineart_in_stack(const Object *ob, const GpencilModifierData *md)
|
|
{
|
|
if (md->type != eGpencilModifierType_Lineart) {
|
|
return false;
|
|
}
|
|
LISTBASE_FOREACH (GpencilModifierData *, gmd, &ob->greasepencil_modifiers) {
|
|
if (gmd->type == eGpencilModifierType_Lineart) {
|
|
if (gmd == md) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
/* If we reach here it means md is not in ob's modifier stack. */
|
|
BLI_assert(false);
|
|
return false;
|
|
}
|
|
|
|
int BKE_gpencil_time_modifier_cfra(Depsgraph *depsgraph,
|
|
Scene *scene,
|
|
Object *ob,
|
|
bGPDlayer *gpl,
|
|
const int cfra,
|
|
const bool is_render)
|
|
{
|
|
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
|
|
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
|
int nfra = cfra;
|
|
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) {
|
|
continue;
|
|
}
|
|
|
|
if (mti->remap_time) {
|
|
nfra = mti->remap_time(md, depsgraph, scene, ob, gpl, cfra);
|
|
/* if the frame number changed, don't evaluate more and return */
|
|
if (nfra != cfra) {
|
|
return nfra;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if no time modifier, return original frame number */
|
|
return nfra;
|
|
}
|
|
|
|
void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd)
|
|
{
|
|
DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd);
|
|
int ctime = int(DEG_get_ctime(depsgraph));
|
|
|
|
/* update active frame */
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV);
|
|
}
|
|
|
|
if (DEG_is_active(depsgraph)) {
|
|
bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id);
|
|
|
|
/* sync "actframe" changes back to main-db too,
|
|
* so that editing tools work with copy-on-write
|
|
* when the current frame changes
|
|
*/
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) {
|
|
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_init()
|
|
{
|
|
/* Initialize modifier types */
|
|
gpencil_modifier_type_init(modifier_gpencil_types); /* `MOD_gpencil_legacy_util.cc`. */
|
|
|
|
#if 0
|
|
/* Note that GPencil actually does not support these at the moment,
|
|
* but might do in the future. */
|
|
/* Initialize global common storage used for virtual modifier list. */
|
|
GpencilModifierData *md;
|
|
md = BKE_gpencil_modifier_new(eGpencilModifierType_Armature);
|
|
virtualModifierCommonData.amd = *((ArmatureGpencilModifierData *)md);
|
|
BKE_gpencil_modifier_free(md);
|
|
|
|
md = BKE_gpencil_modifier_new(eGpencilModifierType_Lattice);
|
|
virtualModifierCommonData.lmd = *((LatticeGpencilModifierData *)md);
|
|
BKE_gpencil_modifier_free(md);
|
|
|
|
virtualModifierCommonData.amd.modifier.mode |= eGpencilModifierMode_Virtual;
|
|
virtualModifierCommonData.lmd.modifier.mode |= eGpencilModifierMode_Virtual;
|
|
#endif
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifier_new(int type)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(GpencilModifierType(type));
|
|
GpencilModifierData *md = static_cast<GpencilModifierData *>(
|
|
MEM_callocN(mti->struct_size, mti->struct_name));
|
|
|
|
/* NOTE: this name must be made unique later. */
|
|
STRNCPY_UTF8(md->name, DATA_(mti->name));
|
|
|
|
md->type = type;
|
|
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
|
|
md->flag = eGpencilModifierFlag_OverrideLibrary_Local;
|
|
/* Only expand the parent panel at first. */
|
|
md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
|
|
|
|
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) {
|
|
md->mode |= eGpencilModifierMode_Editmode;
|
|
}
|
|
|
|
if (mti->init_data) {
|
|
mti->init_data(md);
|
|
}
|
|
|
|
return md;
|
|
}
|
|
|
|
static void modifier_free_data_id_us_cb(void * /*user_data*/,
|
|
Object * /*ob*/,
|
|
ID **idpoin,
|
|
int cb_flag)
|
|
{
|
|
ID *id = *idpoin;
|
|
if (id != nullptr && (cb_flag & IDWALK_CB_USER) != 0) {
|
|
id_us_min(id);
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
if (mti->foreach_ID_link) {
|
|
mti->foreach_ID_link(md, nullptr, modifier_free_data_id_us_cb, nullptr);
|
|
}
|
|
}
|
|
|
|
if (mti->free_data) {
|
|
mti->free_data(md);
|
|
}
|
|
if (md->error) {
|
|
MEM_freeN(md->error);
|
|
}
|
|
|
|
MEM_freeN(md);
|
|
}
|
|
|
|
void BKE_gpencil_modifier_free(GpencilModifierData *md)
|
|
{
|
|
BKE_gpencil_modifier_free_ex(md, 0);
|
|
}
|
|
|
|
void BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *gmd)
|
|
{
|
|
if (modifiers && gmd) {
|
|
const GpencilModifierTypeInfo *gmti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(gmd->type));
|
|
BLI_uniquename(modifiers,
|
|
gmd,
|
|
DATA_(gmti->name),
|
|
'.',
|
|
offsetof(GpencilModifierData, name),
|
|
sizeof(gmd->name));
|
|
}
|
|
}
|
|
|
|
bool BKE_gpencil_modifier_depends_ontime(GpencilModifierData *md)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
return mti->depends_on_time && mti->depends_on_time(md);
|
|
}
|
|
|
|
const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType type)
|
|
{
|
|
/* type unsigned, no need to check < 0 */
|
|
if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && type > 0 &&
|
|
modifier_gpencil_types[type]->name[0] != '\0')
|
|
{
|
|
return modifier_gpencil_types[type];
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void BKE_gpencil_modifierType_panel_id(GpencilModifierType type, char *r_idname)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(type);
|
|
BLI_string_join(r_idname, BKE_ST_MAXNAME, GPENCIL_MODIFIER_TYPE_PANEL_PREFIX, mti->name);
|
|
}
|
|
|
|
void BKE_gpencil_modifier_panel_expand(GpencilModifierData *md)
|
|
{
|
|
md->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT;
|
|
}
|
|
|
|
void BKE_gpencil_modifier_copydata_generic(const GpencilModifierData *md_src,
|
|
GpencilModifierData *md_dst)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md_src->type));
|
|
|
|
/* md_dst may have already be fully initialized with some extra allocated data,
|
|
* we need to free it now to avoid memleak. */
|
|
if (mti->free_data) {
|
|
mti->free_data(md_dst);
|
|
}
|
|
|
|
const size_t data_size = sizeof(GpencilModifierData);
|
|
const char *md_src_data = ((const char *)md_src) + data_size;
|
|
char *md_dst_data = ((char *)md_dst) + data_size;
|
|
BLI_assert(data_size <= size_t(mti->struct_size));
|
|
memcpy(md_dst_data, md_src_data, size_t(mti->struct_size) - data_size);
|
|
}
|
|
|
|
static void gpencil_modifier_copy_data_id_us_cb(void * /*user_data*/,
|
|
Object * /*ob*/,
|
|
ID **idpoin,
|
|
int cb_flag)
|
|
{
|
|
ID *id = *idpoin;
|
|
if (id != nullptr && (cb_flag & IDWALK_CB_USER) != 0) {
|
|
id_us_plus(id);
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md,
|
|
GpencilModifierData *target,
|
|
const int flag)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
target->mode = md->mode;
|
|
target->flag = md->flag;
|
|
target->ui_expand_flag = md->ui_expand_flag; /* Expand the parent panel by default. */
|
|
|
|
if (mti->copy_data) {
|
|
mti->copy_data(md, target);
|
|
}
|
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
if (mti->foreach_ID_link) {
|
|
mti->foreach_ID_link(target, nullptr, gpencil_modifier_copy_data_id_us_cb, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_copydata(GpencilModifierData *md, GpencilModifierData *target)
|
|
{
|
|
BKE_gpencil_modifier_copydata_ex(md, target, 0);
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifiers_findby_type(Object *ob, GpencilModifierType type)
|
|
{
|
|
GpencilModifierData *md = static_cast<GpencilModifierData *>(ob->greasepencil_modifiers.first);
|
|
|
|
for (; md; md = md->next) {
|
|
if (md->type == type) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return md;
|
|
}
|
|
|
|
void BKE_gpencil_modifier_set_error(GpencilModifierData *md, const char *format, ...)
|
|
{
|
|
char buffer[512];
|
|
va_list ap;
|
|
const char *format_tip = TIP_(format);
|
|
|
|
va_start(ap, format);
|
|
vsnprintf(buffer, sizeof(buffer), format_tip, ap);
|
|
va_end(ap);
|
|
buffer[sizeof(buffer) - 1] = '\0';
|
|
|
|
if (md->error) {
|
|
MEM_freeN(md->error);
|
|
}
|
|
|
|
md->error = BLI_strdup(buffer);
|
|
|
|
CLOG_STR_ERROR(&LOG, md->error);
|
|
}
|
|
|
|
bool BKE_gpencil_modifier_is_nonlocal_in_liboverride(const Object *ob,
|
|
const GpencilModifierData *gmd)
|
|
{
|
|
return (ID_IS_OVERRIDE_LIBRARY(ob) &&
|
|
(gmd == nullptr || (gmd->flag & eGpencilModifierFlag_OverrideLibrary_Local) == 0));
|
|
}
|
|
|
|
void BKE_gpencil_modifiers_foreach_ID_link(Object *ob,
|
|
GreasePencilIDWalkFunc walk,
|
|
void *user_data)
|
|
{
|
|
GpencilModifierData *md = static_cast<GpencilModifierData *>(ob->greasepencil_modifiers.first);
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (mti->foreach_ID_link) {
|
|
mti->foreach_ID_link(md, ob, walk, user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifiers_foreach_tex_link(Object *ob,
|
|
GreasePencilTexWalkFunc walk,
|
|
void *user_data)
|
|
{
|
|
GpencilModifierData *md = static_cast<GpencilModifierData *>(ob->greasepencil_modifiers.first);
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (mti->foreach_tex_link) {
|
|
mti->foreach_tex_link(md, ob, walk, user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifiers_findby_name(Object *ob, const char *name)
|
|
{
|
|
return static_cast<GpencilModifierData *>(
|
|
BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name)));
|
|
}
|
|
|
|
/**
|
|
* Remap grease pencil frame (Time modifier)
|
|
* \param depsgraph: Current depsgraph
|
|
* \param scene: Current scene
|
|
* \param ob: Grease pencil object
|
|
* \param gpl: Grease pencil layer
|
|
* \return New frame number
|
|
*/
|
|
static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl)
|
|
{
|
|
const bool is_render = bool(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
|
const bool time_remap = BKE_gpencil_has_time_modifiers(ob);
|
|
int cfra_eval = int(DEG_get_ctime(depsgraph));
|
|
|
|
int remap_cfra = cfra_eval;
|
|
if (time_remap) {
|
|
remap_cfra = BKE_gpencil_time_modifier_cfra(depsgraph, scene, ob, gpl, cfra_eval, is_render);
|
|
}
|
|
|
|
return remap_cfra;
|
|
}
|
|
|
|
bGPDframe *BKE_gpencil_frame_retime_get(Depsgraph *depsgraph,
|
|
Scene *scene,
|
|
Object *ob,
|
|
bGPDlayer *gpl)
|
|
{
|
|
int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl);
|
|
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, remap_cfra, GP_GETFRAME_USE_PREV);
|
|
|
|
return gpf;
|
|
}
|
|
|
|
static void gpencil_assign_object_eval(Object *object)
|
|
{
|
|
BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE);
|
|
|
|
bGPdata *gpd_eval = object->runtime->gpd_eval;
|
|
|
|
gpd_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
|
|
|
|
if (object->id.tag & LIB_TAG_COPIED_ON_WRITE) {
|
|
object->data = gpd_eval;
|
|
}
|
|
}
|
|
|
|
static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd)
|
|
{
|
|
/* Create a temporary copy gpd. */
|
|
ID *newid = nullptr;
|
|
BKE_libblock_copy_ex(nullptr, &gpd->id, &newid, LIB_ID_COPY_LOCALIZE);
|
|
bGPdata *gpd_eval = (bGPdata *)newid;
|
|
BLI_listbase_clear(&gpd_eval->layers);
|
|
|
|
if (gpd->mat != nullptr) {
|
|
gpd_eval->mat = static_cast<Material **>(MEM_dupallocN(gpd->mat));
|
|
}
|
|
|
|
BKE_defgroup_copy_list(&gpd_eval->vertex_group_names, &gpd->vertex_group_names);
|
|
|
|
/* Duplicate structure: layers and frames without strokes. */
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd->layers) {
|
|
bGPDlayer *gpl_eval = BKE_gpencil_layer_duplicate(gpl_orig, true, false);
|
|
BLI_addtail(&gpd_eval->layers, gpl_eval);
|
|
gpl_eval->runtime.gpl_orig = gpl_orig;
|
|
/* Update frames orig pointers (helps for faster lookup in copy_frame_to_eval_cb). */
|
|
BKE_gpencil_layer_original_pointers_update(gpl_orig, gpl_eval);
|
|
}
|
|
|
|
return gpd_eval;
|
|
}
|
|
|
|
static void copy_frame_to_eval_ex(bGPDframe *gpf_orig, bGPDframe *gpf_eval)
|
|
{
|
|
/* Free any existing eval stroke data. This happens in case we have a single user on the data
|
|
* block and the strokes have not been deleted. */
|
|
if (!BLI_listbase_is_empty(&gpf_eval->strokes)) {
|
|
BKE_gpencil_free_strokes(gpf_eval);
|
|
}
|
|
/* Copy strokes to eval frame and update internal orig pointers. */
|
|
BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval);
|
|
BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval);
|
|
}
|
|
|
|
static void copy_frame_to_eval_cb(bGPDlayer *gpl,
|
|
bGPDframe *gpf,
|
|
bGPDstroke * /*gps*/,
|
|
void * /*thunk*/)
|
|
{
|
|
/* Early return when callback:
|
|
* - Is not provided with a frame.
|
|
* - When the frame is the layer's active frame (already handled in
|
|
* gpencil_copy_visible_frames_to_eval).
|
|
*/
|
|
if (ELEM(gpf, nullptr, gpl->actframe)) {
|
|
return;
|
|
}
|
|
|
|
copy_frame_to_eval_ex(gpf->runtime.gpf_orig, gpf);
|
|
}
|
|
|
|
static void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
|
{
|
|
/* Remap layers active frame with time modifiers applied. */
|
|
bGPdata *gpd_eval = static_cast<bGPdata *>(ob->data);
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl_eval, &gpd_eval->layers) {
|
|
bGPDframe *gpf_eval = gpl_eval->actframe;
|
|
int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_eval);
|
|
if (gpf_eval == nullptr || gpf_eval->framenum != remap_cfra) {
|
|
gpl_eval->actframe = BKE_gpencil_layer_frame_get(gpl_eval, remap_cfra, GP_GETFRAME_USE_PREV);
|
|
}
|
|
/* Always copy active frame to eval, because the modifiers always evaluate the active frame,
|
|
* even if it's not visible (e.g. the layer is hidden). */
|
|
if (gpl_eval->actframe != nullptr) {
|
|
copy_frame_to_eval_ex(gpl_eval->actframe->runtime.gpf_orig, gpl_eval->actframe);
|
|
}
|
|
}
|
|
|
|
/* Copy visible frames that are not the active one to evaluated version. */
|
|
BKE_gpencil_visible_stroke_advanced_iter(
|
|
nullptr, ob, copy_frame_to_eval_cb, nullptr, nullptr, true, scene->r.cfra);
|
|
}
|
|
|
|
void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
|
{
|
|
bGPdata *gpd_eval = (bGPdata *)ob->data;
|
|
Object *ob_orig = (Object *)DEG_get_original_id(&ob->id);
|
|
bGPdata *gpd_orig = (bGPdata *)ob_orig->data;
|
|
|
|
/* Need check if some layer is parented or transformed. */
|
|
bool do_parent = false;
|
|
bool do_transform = false;
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) {
|
|
if (gpl->parent != nullptr) {
|
|
do_parent = true;
|
|
break;
|
|
}
|
|
|
|
/* Only do layer transformations for non-zero or animated transforms. */
|
|
bool transformed = (!is_zero_v3(gpl->location) || !is_zero_v3(gpl->rotation) ||
|
|
!is_one_v3(gpl->scale));
|
|
float tmp_mat[4][4];
|
|
loc_eul_size_to_mat4(tmp_mat, gpl->location, gpl->rotation, gpl->scale);
|
|
transformed |= !equals_m4m4(gpl->layer_mat, tmp_mat);
|
|
if (transformed) {
|
|
do_transform = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEG_debug_print_eval(depsgraph, __func__, gpd_eval->id.name, gpd_eval);
|
|
|
|
/* Delete any previously created runtime copy. */
|
|
if (ob->runtime->gpd_eval != nullptr) {
|
|
/* Make sure to clear the pointer in case the runtime eval data points to the same data block.
|
|
* This can happen when the gpencil data block was not tagged for a depsgraph update after last
|
|
* call to this function (e.g. a frame change). */
|
|
if (gpd_eval == ob->runtime->gpd_eval) {
|
|
gpd_eval = nullptr;
|
|
}
|
|
BKE_gpencil_eval_delete(ob->runtime->gpd_eval);
|
|
ob->runtime->gpd_eval = nullptr;
|
|
ob->data = gpd_eval;
|
|
}
|
|
|
|
const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_orig));
|
|
const bool is_curve_edit = bool(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_orig));
|
|
const bool do_modifiers = bool((!is_multiedit) && (!is_curve_edit) &&
|
|
(ob_orig->greasepencil_modifiers.first != nullptr) &&
|
|
!GPENCIL_SIMPLIFY_MODIF(scene));
|
|
if ((!do_modifiers) && (!do_parent) && (!do_transform)) {
|
|
BLI_assert(ob->data != nullptr);
|
|
return;
|
|
}
|
|
|
|
/* If datablock has only one user, we can update its eval data directly.
|
|
* Otherwise, we need to have distinct copies for each instance, since applied transformations
|
|
* may differ. */
|
|
if (gpd_orig->id.us > 1) {
|
|
/* Copy of the original datablock's structure (layers and empty frames). */
|
|
ob->runtime->gpd_eval = gpencil_copy_structure_for_eval(gpd_orig);
|
|
/* Overwrite ob->data with gpd_eval here. */
|
|
gpencil_assign_object_eval(ob);
|
|
}
|
|
|
|
BLI_assert(ob->data != nullptr);
|
|
/* Only copy strokes from visible frames to evaluated data. */
|
|
gpencil_copy_visible_frames_to_eval(depsgraph, scene, ob);
|
|
}
|
|
|
|
void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
|
|
{
|
|
bGPdata *gpd = (bGPdata *)ob->data;
|
|
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
|
const bool is_render = bool(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
|
const bool is_curve_edit = bool(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) && !is_render);
|
|
const bool is_multiedit = bool(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && !is_render);
|
|
const bool do_modifiers = bool((!is_multiedit) && (!is_curve_edit) &&
|
|
(ob->greasepencil_modifiers.first != nullptr) &&
|
|
!GPENCIL_SIMPLIFY_MODIF(scene));
|
|
if (!do_modifiers) {
|
|
return;
|
|
}
|
|
|
|
/* Init general modifiers data. */
|
|
BKE_gpencil_cache_data_init(depsgraph, ob);
|
|
|
|
const bool time_remap = BKE_gpencil_has_time_modifiers(ob);
|
|
bool is_first_lineart = true;
|
|
GpencilLineartLimitInfo info = BKE_gpencil_get_lineart_modifier_limits(ob);
|
|
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
|
|
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
|
|
if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) {
|
|
continue;
|
|
}
|
|
|
|
if (md->type == eGpencilModifierType_Lineart) {
|
|
BKE_gpencil_set_lineart_modifier_limits(md, &info, is_first_lineart);
|
|
is_first_lineart = false;
|
|
}
|
|
|
|
/* Apply geometry modifiers (add new geometry). */
|
|
if (mti && mti->generate_strokes) {
|
|
mti->generate_strokes(md, depsgraph, ob);
|
|
}
|
|
|
|
/* Apply deform modifiers and Time remap (only change geometry). */
|
|
if ((time_remap) || (mti && mti->deform_stroke)) {
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl);
|
|
if (gpf == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
if (mti->deform_stroke) {
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
|
mti->deform_stroke(md, depsgraph, ob, gpl, gpf, gps);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear any cache data. */
|
|
BKE_gpencil_cache_data_clear(ob);
|
|
|
|
MOD_lineart_clear_cache(&gpd->runtime.lineart_cache);
|
|
}
|
|
|
|
void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
|
|
{
|
|
if (modbase == nullptr) {
|
|
return;
|
|
}
|
|
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, modbase) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(
|
|
GpencilModifierType(md->type));
|
|
if (mti == nullptr) {
|
|
return;
|
|
}
|
|
|
|
BLO_write_struct_by_name(writer, mti->struct_name, md);
|
|
|
|
if (md->type == eGpencilModifierType_Thick) {
|
|
ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md;
|
|
|
|
if (gpmd->curve_thickness) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_thickness);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Noise) {
|
|
NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md;
|
|
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Hook) {
|
|
HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md;
|
|
|
|
if (gpmd->curfalloff) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curfalloff);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Tint) {
|
|
TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md;
|
|
if (gpmd->colorband) {
|
|
BLO_write_struct(writer, ColorBand, gpmd->colorband);
|
|
}
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Smooth) {
|
|
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Color) {
|
|
ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md;
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Opacity) {
|
|
OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md;
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Dash) {
|
|
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
|
|
BLO_write_struct_array(
|
|
writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments);
|
|
}
|
|
else if (md->type == eGpencilModifierType_Time) {
|
|
TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)md;
|
|
BLO_write_struct_array(
|
|
writer, TimeGpencilModifierSegment, gpmd->segments_len, gpmd->segments);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb, Object *ob)
|
|
{
|
|
BLO_read_list(reader, lb);
|
|
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, lb) {
|
|
md->error = nullptr;
|
|
|
|
/* if modifiers disappear, or for upward compatibility */
|
|
if (nullptr == BKE_gpencil_modifier_get_info(GpencilModifierType(md->type))) {
|
|
md->type = eModifierType_None;
|
|
}
|
|
|
|
/* If linking from a library, clear 'local' library override flag. */
|
|
if (ID_IS_LINKED(ob)) {
|
|
md->flag &= ~eGpencilModifierFlag_OverrideLibrary_Local;
|
|
}
|
|
|
|
if (md->type == eGpencilModifierType_Lattice) {
|
|
LatticeGpencilModifierData *gpmd = (LatticeGpencilModifierData *)md;
|
|
gpmd->cache_data = nullptr;
|
|
}
|
|
else if (md->type == eGpencilModifierType_Hook) {
|
|
HookGpencilModifierData *hmd = (HookGpencilModifierData *)md;
|
|
|
|
BLO_read_data_address(reader, &hmd->curfalloff);
|
|
if (hmd->curfalloff) {
|
|
BKE_curvemapping_blend_read(reader, hmd->curfalloff);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Noise) {
|
|
NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md;
|
|
|
|
BLO_read_data_address(reader, &gpmd->curve_intensity);
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
|
|
/* Initialize the curve. Maybe this could be moved to modifier logic. */
|
|
BKE_curvemapping_init(gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Thick) {
|
|
ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md;
|
|
|
|
BLO_read_data_address(reader, &gpmd->curve_thickness);
|
|
if (gpmd->curve_thickness) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_thickness);
|
|
BKE_curvemapping_init(gpmd->curve_thickness);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Tint) {
|
|
TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->colorband);
|
|
BLO_read_data_address(reader, &gpmd->curve_intensity);
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
|
|
BKE_curvemapping_init(gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Smooth) {
|
|
SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->curve_intensity);
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
|
|
BKE_curvemapping_init(gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Color) {
|
|
ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->curve_intensity);
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
|
|
BKE_curvemapping_init(gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Opacity) {
|
|
OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->curve_intensity);
|
|
if (gpmd->curve_intensity) {
|
|
BKE_curvemapping_blend_read(reader, gpmd->curve_intensity);
|
|
BKE_curvemapping_init(gpmd->curve_intensity);
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Dash) {
|
|
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->segments);
|
|
for (int i = 0; i < gpmd->segments_len; i++) {
|
|
gpmd->segments[i].dmd = gpmd;
|
|
}
|
|
}
|
|
else if (md->type == eGpencilModifierType_Time) {
|
|
TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)md;
|
|
BLO_read_data_address(reader, &gpmd->segments);
|
|
for (int i = 0; i < gpmd->segments_len; i++) {
|
|
gpmd->segments[i].gpmd = gpmd;
|
|
}
|
|
}
|
|
if (md->type == eGpencilModifierType_Shrinkwrap) {
|
|
ShrinkwrapGpencilModifierData *gpmd = (ShrinkwrapGpencilModifierData *)md;
|
|
gpmd->cache_data = nullptr;
|
|
}
|
|
}
|
|
}
|