916 lines
27 KiB
C
916 lines
27 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2017, Blender Foundation
|
|
* This is a new part of Blender
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_math_geom.h"
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_string_utils.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "DNA_gpencil_modifier_types.h"
|
|
#include "DNA_gpencil_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BKE_gpencil.h"
|
|
#include "BKE_gpencil_geom.h"
|
|
#include "BKE_gpencil_modifier.h"
|
|
#include "BKE_lattice.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_lib_query.h"
|
|
#include "BKE_material.h"
|
|
#include "BKE_object.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "MOD_gpencil_modifiertypes.h"
|
|
|
|
static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL};
|
|
|
|
/* *************************************************** */
|
|
/* Geometry Utilities */
|
|
|
|
/* calculate stroke normal using some points */
|
|
void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
|
|
{
|
|
if (gps->totpoints < 3) {
|
|
zero_v3(r_normal);
|
|
return;
|
|
}
|
|
|
|
bGPDspoint *points = gps->points;
|
|
int totpoints = gps->totpoints;
|
|
|
|
const bGPDspoint *pt0 = &points[0];
|
|
const bGPDspoint *pt1 = &points[1];
|
|
const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
|
|
|
|
float vec1[3];
|
|
float vec2[3];
|
|
|
|
/* initial vector (p0 -> p1) */
|
|
sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
|
|
|
|
/* point vector at 3/4 */
|
|
sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
|
|
|
|
/* vector orthogonal to polygon plane */
|
|
cross_v3_v3v3(r_normal, vec1, vec2);
|
|
|
|
/* Normalize vector */
|
|
normalize_v3(r_normal);
|
|
}
|
|
|
|
/* Stroke Simplify ------------------------------------- */
|
|
|
|
/* Reduce a series of points to a simplified version, but
|
|
* maintains the general shape of the series
|
|
*
|
|
* Ramer - Douglas - Peucker algorithm
|
|
* by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
|
|
*/
|
|
void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
|
|
{
|
|
bGPDspoint *old_points = MEM_dupallocN(gps->points);
|
|
int totpoints = gps->totpoints;
|
|
char *marked = NULL;
|
|
char work;
|
|
|
|
int start = 0;
|
|
int end = gps->totpoints - 1;
|
|
|
|
marked = MEM_callocN(totpoints, "GP marked array");
|
|
marked[start] = 1;
|
|
marked[end] = 1;
|
|
|
|
work = 1;
|
|
int totmarked = 0;
|
|
/* while still reducing */
|
|
while (work) {
|
|
int ls, le;
|
|
work = 0;
|
|
|
|
ls = start;
|
|
le = start + 1;
|
|
|
|
/* while not over interval */
|
|
while (ls < end) {
|
|
int max_i = 0;
|
|
/* divided to get more control */
|
|
float max_dist = epsilon / 10.0f;
|
|
|
|
/* find the next marked point */
|
|
while (marked[le] == 0) {
|
|
le++;
|
|
}
|
|
|
|
for (int i = ls + 1; i < le; i++) {
|
|
float point_on_line[3];
|
|
float dist;
|
|
|
|
closest_to_line_segment_v3(
|
|
point_on_line, &old_points[i].x, &old_points[ls].x, &old_points[le].x);
|
|
|
|
dist = len_v3v3(point_on_line, &old_points[i].x);
|
|
|
|
if (dist > max_dist) {
|
|
max_dist = dist;
|
|
max_i = i;
|
|
}
|
|
}
|
|
|
|
if (max_i != 0) {
|
|
work = 1;
|
|
marked[max_i] = 1;
|
|
totmarked++;
|
|
}
|
|
|
|
ls = le;
|
|
le = ls + 1;
|
|
}
|
|
}
|
|
|
|
/* adding points marked */
|
|
MDeformVert *old_dvert = NULL;
|
|
MDeformVert *dvert_src = NULL;
|
|
|
|
if (gps->dvert != NULL) {
|
|
old_dvert = MEM_dupallocN(gps->dvert);
|
|
}
|
|
/* resize gps */
|
|
int j = 0;
|
|
for (int i = 0; i < totpoints; i++) {
|
|
bGPDspoint *pt_src = &old_points[i];
|
|
bGPDspoint *pt = &gps->points[j];
|
|
|
|
if ((marked[i]) || (i == 0) || (i == totpoints - 1)) {
|
|
memcpy(pt, pt_src, sizeof(bGPDspoint));
|
|
if (gps->dvert != NULL) {
|
|
dvert_src = &old_dvert[i];
|
|
MDeformVert *dvert = &gps->dvert[j];
|
|
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
|
if (dvert_src->dw) {
|
|
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
|
|
}
|
|
}
|
|
j++;
|
|
}
|
|
else {
|
|
if (gps->dvert != NULL) {
|
|
dvert_src = &old_dvert[i];
|
|
BKE_gpencil_free_point_weights(dvert_src);
|
|
}
|
|
}
|
|
}
|
|
|
|
gps->totpoints = j;
|
|
|
|
/* Calc geometry data. */
|
|
BKE_gpencil_stroke_geometry_update(gps);
|
|
|
|
MEM_SAFE_FREE(old_points);
|
|
MEM_SAFE_FREE(old_dvert);
|
|
MEM_SAFE_FREE(marked);
|
|
}
|
|
|
|
/* Simplify alternate vertex of stroke except extremes */
|
|
void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
|
|
{
|
|
if (gps->totpoints < 5) {
|
|
return;
|
|
}
|
|
|
|
/* save points */
|
|
bGPDspoint *old_points = MEM_dupallocN(gps->points);
|
|
MDeformVert *old_dvert = NULL;
|
|
MDeformVert *dvert_src = NULL;
|
|
|
|
if (gps->dvert != NULL) {
|
|
old_dvert = MEM_dupallocN(gps->dvert);
|
|
}
|
|
|
|
/* resize gps */
|
|
int newtot = (gps->totpoints - 2) / 2;
|
|
if (((gps->totpoints - 2) % 2) > 0) {
|
|
newtot++;
|
|
}
|
|
newtot += 2;
|
|
|
|
gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
|
|
if (gps->dvert != NULL) {
|
|
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
|
|
}
|
|
|
|
int j = 0;
|
|
for (int i = 0; i < gps->totpoints; i++) {
|
|
bGPDspoint *pt_src = &old_points[i];
|
|
bGPDspoint *pt = &gps->points[j];
|
|
|
|
if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) {
|
|
memcpy(pt, pt_src, sizeof(bGPDspoint));
|
|
if (gps->dvert != NULL) {
|
|
dvert_src = &old_dvert[i];
|
|
MDeformVert *dvert = &gps->dvert[j];
|
|
memcpy(dvert, dvert_src, sizeof(MDeformVert));
|
|
if (dvert_src->dw) {
|
|
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
|
|
}
|
|
}
|
|
j++;
|
|
}
|
|
else {
|
|
if (gps->dvert != NULL) {
|
|
dvert_src = &old_dvert[i];
|
|
BKE_gpencil_free_point_weights(dvert_src);
|
|
}
|
|
}
|
|
}
|
|
|
|
gps->totpoints = j;
|
|
/* Calc geometry data. */
|
|
BKE_gpencil_stroke_geometry_update(gps);
|
|
|
|
MEM_SAFE_FREE(old_points);
|
|
MEM_SAFE_FREE(old_dvert);
|
|
}
|
|
|
|
/* *************************************************** */
|
|
/* Modifier Utilities */
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
/* init lattice deform data */
|
|
void BKE_gpencil_lattice_init(Object *ob)
|
|
{
|
|
GpencilModifierData *md;
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
if (md->type == eGpencilModifierType_Lattice) {
|
|
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
|
Object *latob = NULL;
|
|
|
|
latob = mmd->object;
|
|
if ((!latob) || (latob->type != OB_LATTICE)) {
|
|
return;
|
|
}
|
|
if (mmd->cache_data) {
|
|
end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
|
|
}
|
|
|
|
/* init deform data */
|
|
mmd->cache_data = (struct LatticeDeformData *)init_latt_deform(latob, ob);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clear lattice deform data */
|
|
void BKE_gpencil_lattice_clear(Object *ob)
|
|
{
|
|
GpencilModifierData *md;
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
if (md->type == eGpencilModifierType_Lattice) {
|
|
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
|
|
if ((mmd) && (mmd->cache_data)) {
|
|
end_latt_deform((struct LatticeDeformData *)mmd->cache_data);
|
|
mmd->cache_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* *************************************************** */
|
|
/* Modifier Methods - Evaluation Loops, etc. */
|
|
|
|
/* check if exist geometry modifiers */
|
|
bool BKE_gpencil_has_geometry_modifiers(Object *ob)
|
|
{
|
|
GpencilModifierData *md;
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if (mti && mti->generateStrokes) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* check if exist time modifiers */
|
|
bool BKE_gpencil_has_time_modifiers(Object *ob)
|
|
{
|
|
GpencilModifierData *md;
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if (mti && mti->remapTime) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Check if exist transform stroke modifiers (to rotate sculpt or edit). */
|
|
bool BKE_gpencil_has_transform_modifiers(Object *ob)
|
|
{
|
|
GpencilModifierData *md;
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
/* Only if enabled in edit mode. */
|
|
if (!GPENCIL_MODIFIER_EDIT(md, true) && GPENCIL_MODIFIER_ACTIVE(md, false)) {
|
|
if ((md->type == eGpencilModifierType_Armature) || (md->type == eGpencilModifierType_Hook) ||
|
|
(md->type == eGpencilModifierType_Lattice) ||
|
|
(md->type == eGpencilModifierType_Offset)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* apply time modifiers */
|
|
static int gpencil_time_modifier(
|
|
Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render)
|
|
{
|
|
GpencilModifierData *md;
|
|
bGPdata *gpd = ob->data;
|
|
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
|
|
int nfra = cfra;
|
|
|
|
for (md = ob->greasepencil_modifiers.first; md; md = md->next) {
|
|
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) {
|
|
continue;
|
|
}
|
|
|
|
if (mti->remapTime) {
|
|
nfra = mti->remapTime(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(void)
|
|
{
|
|
/* Initialize modifier types */
|
|
gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifier_new(int type)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(type);
|
|
GpencilModifierData *md = MEM_callocN(mti->struct_size, mti->struct_name);
|
|
|
|
/* note, this name must be made unique later */
|
|
BLI_strncpy(md->name, DATA_(mti->name), sizeof(md->name));
|
|
|
|
md->type = type;
|
|
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render |
|
|
eGpencilModifierMode_Expanded;
|
|
md->flag = eGpencilModifierFlag_OverrideLibrary_Local;
|
|
|
|
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) {
|
|
md->mode |= eGpencilModifierMode_Editmode;
|
|
}
|
|
|
|
if (mti->initData) {
|
|
mti->initData(md);
|
|
}
|
|
|
|
return md;
|
|
}
|
|
|
|
static void modifier_free_data_id_us_cb(void *UNUSED(userData),
|
|
Object *UNUSED(ob),
|
|
ID **idpoin,
|
|
int cb_flag)
|
|
{
|
|
ID *id = *idpoin;
|
|
if (id != NULL && (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(md->type);
|
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
if (mti->foreachIDLink) {
|
|
mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL);
|
|
}
|
|
else if (mti->foreachObjectLink) {
|
|
mti->foreachObjectLink(
|
|
md, NULL, (GreasePencilObjectWalkFunc)modifier_free_data_id_us_cb, NULL);
|
|
}
|
|
}
|
|
|
|
if (mti->freeData) {
|
|
mti->freeData(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);
|
|
}
|
|
|
|
/* check unique name */
|
|
bool BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *gmd)
|
|
{
|
|
if (modifiers && gmd) {
|
|
const GpencilModifierTypeInfo *gmti = BKE_gpencil_modifier_get_info(gmd->type);
|
|
return BLI_uniquename(modifiers,
|
|
gmd,
|
|
DATA_(gmti->name),
|
|
'.',
|
|
offsetof(GpencilModifierData, name),
|
|
sizeof(gmd->name));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BKE_gpencil_modifier_depends_ontime(GpencilModifierData *md)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
return mti->dependsOnTime && mti->dependsOnTime(md);
|
|
}
|
|
|
|
const GpencilModifierTypeInfo *BKE_gpencil_modifier_get_info(GpencilModifierType type)
|
|
{
|
|
/* type unsigned, no need to check < 0 */
|
|
if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && modifier_gpencil_types[type]->name[0] != '\0') {
|
|
return modifier_gpencil_types[type];
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifier_copydata_generic(const GpencilModifierData *md_src,
|
|
GpencilModifierData *md_dst)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(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->freeData) {
|
|
mti->freeData(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 *UNUSED(userData),
|
|
Object *UNUSED(ob),
|
|
ID **idpoin,
|
|
int cb_flag)
|
|
{
|
|
ID *id = *idpoin;
|
|
if (id != NULL && (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(md->type);
|
|
|
|
target->mode = md->mode;
|
|
target->flag = md->flag;
|
|
|
|
if (mti->copyData) {
|
|
mti->copyData(md, target);
|
|
}
|
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
if (mti->foreachIDLink) {
|
|
mti->foreachIDLink(target, NULL, gpencil_modifier_copy_data_id_us_cb, NULL);
|
|
}
|
|
else if (mti->foreachObjectLink) {
|
|
mti->foreachObjectLink(
|
|
target, NULL, (GreasePencilObjectWalkFunc)gpencil_modifier_copy_data_id_us_cb, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = ob->greasepencil_modifiers.first;
|
|
|
|
for (; md; md = md->next) {
|
|
if (md->type == type) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return md;
|
|
}
|
|
|
|
void BKE_gpencil_modifiers_foreach_ID_link(Object *ob, GreasePencilIDWalkFunc walk, void *userData)
|
|
{
|
|
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if (mti->foreachIDLink) {
|
|
mti->foreachIDLink(md, ob, walk, userData);
|
|
}
|
|
else if (mti->foreachObjectLink) {
|
|
/* each Object can masquerade as an ID, so this should be OK */
|
|
GreasePencilObjectWalkFunc fp = (GreasePencilObjectWalkFunc)walk;
|
|
mti->foreachObjectLink(md, ob, fp, userData);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_gpencil_modifiers_foreach_tex_link(Object *ob, GreasePencilTexWalkFunc walk, void *userData)
|
|
{
|
|
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if (mti->foreachTexLink) {
|
|
mti->foreachTexLink(md, ob, walk, userData);
|
|
}
|
|
}
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifiers_findny_name(Object *ob, const char *name)
|
|
{
|
|
return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name));
|
|
}
|
|
|
|
void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
|
|
{
|
|
bGPDspoint *temp_points;
|
|
MDeformVert *temp_dverts = NULL;
|
|
MDeformVert *dvert = NULL;
|
|
MDeformVert *dvert_final = NULL;
|
|
MDeformVert *dvert_next = NULL;
|
|
int totnewpoints, oldtotpoints;
|
|
int i2;
|
|
|
|
for (int s = 0; s < level; s++) {
|
|
totnewpoints = gps->totpoints - 1;
|
|
/* duplicate points in a temp area */
|
|
temp_points = MEM_dupallocN(gps->points);
|
|
oldtotpoints = gps->totpoints;
|
|
|
|
/* resize the points arrays */
|
|
gps->totpoints += totnewpoints;
|
|
gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
|
|
if (gps->dvert != NULL) {
|
|
temp_dverts = MEM_dupallocN(gps->dvert);
|
|
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
|
|
}
|
|
|
|
/* move points from last to first to new place */
|
|
i2 = gps->totpoints - 1;
|
|
for (int i = oldtotpoints - 1; i > 0; i--) {
|
|
bGPDspoint *pt = &temp_points[i];
|
|
bGPDspoint *pt_final = &gps->points[i2];
|
|
|
|
copy_v3_v3(&pt_final->x, &pt->x);
|
|
pt_final->pressure = pt->pressure;
|
|
pt_final->strength = pt->strength;
|
|
pt_final->time = pt->time;
|
|
pt_final->flag = pt->flag;
|
|
pt_final->runtime.pt_orig = pt->runtime.pt_orig;
|
|
pt_final->runtime.idx_orig = pt->runtime.idx_orig;
|
|
copy_v4_v4(pt_final->vert_color, pt->vert_color);
|
|
|
|
if (gps->dvert != NULL) {
|
|
dvert = &temp_dverts[i];
|
|
dvert_final = &gps->dvert[i2];
|
|
dvert_final->totweight = dvert->totweight;
|
|
dvert_final->dw = dvert->dw;
|
|
}
|
|
i2 -= 2;
|
|
}
|
|
/* interpolate mid points */
|
|
i2 = 1;
|
|
for (int i = 0; i < oldtotpoints - 1; i++) {
|
|
bGPDspoint *pt = &temp_points[i];
|
|
bGPDspoint *next = &temp_points[i + 1];
|
|
bGPDspoint *pt_final = &gps->points[i2];
|
|
|
|
/* add a half way point */
|
|
interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
|
|
pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
|
|
pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
|
|
CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
|
|
pt_final->time = interpf(pt->time, next->time, 0.5f);
|
|
pt_final->runtime.pt_orig = NULL;
|
|
pt_final->flag = 0;
|
|
interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
|
|
|
|
if (gps->dvert != NULL) {
|
|
dvert = &temp_dverts[i];
|
|
dvert_next = &temp_dverts[i + 1];
|
|
dvert_final = &gps->dvert[i2];
|
|
|
|
dvert_final->totweight = dvert->totweight;
|
|
dvert_final->dw = MEM_dupallocN(dvert->dw);
|
|
|
|
/* interpolate weight values */
|
|
for (int d = 0; d < dvert->totweight; d++) {
|
|
MDeformWeight *dw_a = &dvert->dw[d];
|
|
if (dvert_next->totweight > d) {
|
|
MDeformWeight *dw_b = &dvert_next->dw[d];
|
|
MDeformWeight *dw_final = &dvert_final->dw[d];
|
|
dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
|
|
}
|
|
}
|
|
}
|
|
|
|
i2 += 2;
|
|
}
|
|
|
|
MEM_SAFE_FREE(temp_points);
|
|
MEM_SAFE_FREE(temp_dverts);
|
|
|
|
/* move points to smooth stroke (not simple type )*/
|
|
if (type != GP_SUBDIV_SIMPLE) {
|
|
/* duplicate points in a temp area with the new subdivide data */
|
|
temp_points = MEM_dupallocN(gps->points);
|
|
|
|
/* extreme points are not changed */
|
|
for (int i = 0; i < gps->totpoints - 2; i++) {
|
|
bGPDspoint *pt = &temp_points[i];
|
|
bGPDspoint *next = &temp_points[i + 1];
|
|
bGPDspoint *pt_final = &gps->points[i + 1];
|
|
|
|
/* move point */
|
|
interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
|
|
}
|
|
/* free temp memory */
|
|
MEM_SAFE_FREE(temp_points);
|
|
}
|
|
}
|
|
|
|
/* Calc geometry data. */
|
|
BKE_gpencil_stroke_geometry_update(gps);
|
|
}
|
|
|
|
/* Remap frame (Time modifier) */
|
|
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 = gpencil_time_modifier(depsgraph, scene, ob, gpl, cfra_eval, is_render);
|
|
}
|
|
|
|
return remap_cfra;
|
|
}
|
|
|
|
/* Get the current frame retimed with time modifiers. */
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* Helper: Copy active frame from original datablock to evaluated datablock for modifiers. */
|
|
static void gpencil_copy_activeframe_to_eval(
|
|
Depsgraph *depsgraph, Scene *scene, Object *ob, bGPdata *gpd_orig, bGPdata *gpd_eval)
|
|
{
|
|
|
|
bGPDlayer *gpl_eval = gpd_eval->layers.first;
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd_orig->layers) {
|
|
|
|
if (gpl_eval != NULL) {
|
|
int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_orig);
|
|
|
|
bGPDframe *gpf_orig = BKE_gpencil_layer_frame_get(
|
|
gpl_orig, remap_cfra, GP_GETFRAME_USE_PREV);
|
|
|
|
if (gpf_orig != NULL) {
|
|
int gpf_index = BLI_findindex(&gpl_orig->frames, gpf_orig);
|
|
bGPDframe *gpf_eval = BLI_findlink(&gpl_eval->frames, gpf_index);
|
|
|
|
if (gpf_eval != NULL) {
|
|
/* Delete old strokes. */
|
|
BKE_gpencil_free_strokes(gpf_eval);
|
|
/* Copy again strokes. */
|
|
BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval);
|
|
|
|
gpf_eval->runtime.gpf_orig = (bGPDframe *)gpf_orig;
|
|
BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval);
|
|
}
|
|
}
|
|
|
|
gpl_eval = gpl_eval->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bGPdata *gpencil_copy_for_eval(bGPdata *gpd)
|
|
{
|
|
int flags = LIB_ID_COPY_LOCALIZE;
|
|
|
|
bGPdata *result;
|
|
BKE_id_copy_ex(NULL, &gpd->id, (ID **)&result, flags);
|
|
return result;
|
|
}
|
|
|
|
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. */
|
|
bool do_parent = false;
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) {
|
|
if (gpl->parent != NULL) {
|
|
do_parent = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval);
|
|
const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
|
|
(!GPENCIL_SIMPLIFY_MODIF(scene)));
|
|
if ((!do_modifiers) && (!do_parent)) {
|
|
return;
|
|
}
|
|
DEG_debug_print_eval(depsgraph, __func__, gpd_eval->id.name, gpd_eval);
|
|
|
|
/* If only one user, don't need a new copy, just update data of the frame. */
|
|
if (gpd_orig->id.us == 1) {
|
|
ob->runtime.gpd_eval = NULL;
|
|
gpencil_copy_activeframe_to_eval(depsgraph, scene, ob, ob_orig->data, gpd_eval);
|
|
return;
|
|
}
|
|
|
|
/* Copy full Datablock to evaluated version. */
|
|
ob->runtime.gpd_orig = gpd_orig;
|
|
if (ob->runtime.gpd_eval != NULL) {
|
|
BKE_gpencil_eval_delete(ob->runtime.gpd_eval);
|
|
ob->runtime.gpd_eval = NULL;
|
|
ob->data = ob->runtime.gpd_orig;
|
|
}
|
|
ob->runtime.gpd_eval = gpencil_copy_for_eval(ob->runtime.gpd_orig);
|
|
gpencil_assign_object_eval(ob);
|
|
BKE_gpencil_update_orig_pointers(ob_orig, (Object *)ob);
|
|
}
|
|
|
|
/* Calculate gpencil modifiers */
|
|
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_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
|
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
|
|
const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
|
|
(!GPENCIL_SIMPLIFY_MODIF(scene)));
|
|
if (!do_modifiers) {
|
|
return;
|
|
}
|
|
|
|
/* Init general modifiers data. */
|
|
BKE_gpencil_lattice_init(ob);
|
|
|
|
const bool time_remap = BKE_gpencil_has_time_modifiers(ob);
|
|
|
|
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
|
|
|
|
if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) {
|
|
continue;
|
|
}
|
|
|
|
/* Apply geometry modifiers (add new geometry). */
|
|
if (mti && mti->generateStrokes) {
|
|
mti->generateStrokes(md, depsgraph, ob);
|
|
}
|
|
|
|
/* Apply deform modifiers and Time remap (only change geometry). */
|
|
if ((time_remap) || (mti && mti->deformStroke)) {
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl);
|
|
if (gpf == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (mti->deformStroke) {
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
|
mti->deformStroke(md, depsgraph, ob, gpl, gpf, gps);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear any lattice data. */
|
|
BKE_gpencil_lattice_clear(ob);
|
|
}
|