When use the subdivide modifier the number of points was not correct and can produce segment faults. Also, the points were selected by default and this was wrong.
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_modifierType_getInfo(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_modifierType_getInfo(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_modifierType_getInfo(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_modifierType_getInfo(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_modifierType_getInfo(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_modifierType_getInfo(gmd->type);
|
|
return BLI_uniquename(modifiers,
|
|
gmd,
|
|
DATA_(gmti->name),
|
|
'.',
|
|
offsetof(GpencilModifierData, name),
|
|
sizeof(gmd->name));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BKE_gpencil_modifier_dependsOnTime(GpencilModifierData *md)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
|
|
|
return mti->dependsOnTime && mti->dependsOnTime(md);
|
|
}
|
|
|
|
const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(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_modifierType_getInfo(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_modifierType_getInfo(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_findByType(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_foreachIDLink(Object *ob, GreasePencilIDWalkFunc walk, void *userData)
|
|
{
|
|
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(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_foreachTexLink(Object *ob, GreasePencilTexWalkFunc walk, void *userData)
|
|
{
|
|
GpencilModifierData *md = ob->greasepencil_modifiers.first;
|
|
|
|
for (; md; md = md->next) {
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
|
|
|
|
if (mti->foreachTexLink) {
|
|
mti->foreachTexLink(md, ob, walk, userData);
|
|
}
|
|
}
|
|
}
|
|
|
|
GpencilModifierData *BKE_gpencil_modifiers_findByName(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_modifierType_getInfo(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);
|
|
}
|