From 66da2f537ae80ce2b31d1eaf34ad8c03d858938d Mon Sep 17 00:00:00 2001 From: Antonioya Date: Tue, 31 Jul 2018 10:22:19 +0200 Subject: New Grease Pencil object for 2D animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton. --- source/blender/blenkernel/BKE_brush.h | 8 +- source/blender/blenkernel/BKE_context.h | 12 +- source/blender/blenkernel/BKE_gpencil.h | 103 +- source/blender/blenkernel/BKE_gpencil_modifier.h | 256 ++++ source/blender/blenkernel/BKE_icons.h | 7 + source/blender/blenkernel/BKE_lattice.h | 1 - source/blender/blenkernel/BKE_material.h | 4 + source/blender/blenkernel/BKE_object.h | 12 + source/blender/blenkernel/BKE_paint.h | 3 +- source/blender/blenkernel/BKE_shader_fx.h | 180 +++ source/blender/blenkernel/CMakeLists.txt | 6 + source/blender/blenkernel/intern/anim_sys.c | 7 + source/blender/blenkernel/intern/brush.c | 380 +++++- source/blender/blenkernel/intern/colortools.c | 19 + source/blender/blenkernel/intern/context.c | 20 +- source/blender/blenkernel/intern/deform.c | 4 +- source/blender/blenkernel/intern/gpencil.c | 1383 ++++++++++++-------- .../blender/blenkernel/intern/gpencil_modifier.c | 679 ++++++++++ source/blender/blenkernel/intern/icons.c | 44 +- source/blender/blenkernel/intern/library.c | 1 + source/blender/blenkernel/intern/library_query.c | 25 +- source/blender/blenkernel/intern/material.c | 76 +- source/blender/blenkernel/intern/object.c | 194 ++- source/blender/blenkernel/intern/object_deform.c | 16 +- source/blender/blenkernel/intern/object_update.c | 4 + source/blender/blenkernel/intern/paint.c | 13 +- source/blender/blenkernel/intern/scene.c | 81 +- source/blender/blenkernel/intern/shader_fx.c | 245 ++++ 28 files changed, 3146 insertions(+), 637 deletions(-) create mode 100644 source/blender/blenkernel/BKE_gpencil_modifier.h create mode 100644 source/blender/blenkernel/BKE_shader_fx.h create mode 100644 source/blender/blenkernel/intern/gpencil_modifier.c create mode 100644 source/blender/blenkernel/intern/shader_fx.c (limited to 'source/blender/blenkernel') diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index eda1c51bbc2..489746cbfd9 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -28,11 +28,14 @@ */ enum eCurveMappingPreset; +struct bContext; struct Brush; +struct Paint; struct ImBuf; struct ImagePool; struct Main; struct Scene; +struct ToolSettings; struct UnifiedPaintSettings; // enum eCurveMappingPreset; @@ -45,14 +48,17 @@ void BKE_brush_system_exit(void); /* datablock functions */ void BKE_brush_init(struct Brush *brush); struct Brush *BKE_brush_add(struct Main *bmain, const char *name, const eObjectMode ob_mode); +struct Brush *BKE_brush_add_gpencil(struct Main *bmain, struct ToolSettings *ts, const char *name); struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode); void BKE_brush_copy_data(struct Main *bmain, struct Brush *brush_dst, const struct Brush *brush_src, const int flag); struct Brush *BKE_brush_copy(struct Main *bmain, const struct Brush *brush); void BKE_brush_make_local(struct Main *bmain, struct Brush *brush, const bool lib_local); -void BKE_brush_unlink(struct Main *bmain, struct Brush *brush); void BKE_brush_free(struct Brush *brush); void BKE_brush_sculpt_reset(struct Brush *brush); +void BKE_brush_gpencil_presets(struct bContext *C); +struct Brush *BKE_brush_getactive_gpencil(struct ToolSettings *ts); +struct Paint *BKE_brush_get_gpencil_paint(struct ToolSettings *ts); /* image icon function */ struct ImBuf *get_brush_icon(struct Brush *brush); diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 9f57859d318..28dcf9cb127 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -65,9 +65,7 @@ struct bPoseChannel; struct bGPdata; struct bGPDlayer; struct bGPDframe; -struct bGPDpalette; -struct bGPDpalettecolor; -struct bGPDbrush; +struct Brush; struct wmWindow; struct wmWindowManager; struct RenderEngineType; @@ -120,6 +118,10 @@ enum { CTX_MODE_PAINT_TEXTURE, CTX_MODE_PARTICLE, CTX_MODE_OBJECT, + CTX_MODE_GPENCIL_PAINT, + CTX_MODE_GPENCIL_EDIT, + CTX_MODE_GPENCIL_SCULPT, + CTX_MODE_GPENCIL_WEIGHT, CTX_MODE_NUM /* must be last */ }; @@ -313,9 +315,7 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list); struct bGPdata *CTX_data_gpencil_data(const bContext *C); struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C); struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C); -struct bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C); -struct bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C); -struct bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C); +struct Brush *CTX_data_active_gpencil_brush(const bContext *C); int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 3a951b7860d..887a7f4f67b 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -31,25 +31,45 @@ * \author Joshua Leung */ +struct CurveMapping; +struct Depsgraph; +struct GpencilModifierData; struct ToolSettings; struct ListBase; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct bGPDspoint; struct bGPDstroke; +struct Material; struct bGPDpalette; struct bGPDpalettecolor; struct Main; +struct BoundBox; +struct Brush; +struct Object; +struct bDeformGroup; +struct SimplifyGpencilModifierData; +struct InstanceGpencilModifierData; +struct LatticeGpencilModifierData; + +struct MDeformVert; +struct MDeformWeight; /* ------------ Grease-Pencil API ------------------ */ +void BKE_gpencil_free_point_weights(struct MDeformVert *dvert); +void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps); void BKE_gpencil_free_stroke(struct bGPDstroke *gps); bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); void BKE_gpencil_free_frames(struct bGPDlayer *gpl); void BKE_gpencil_free_layers(struct ListBase *list); -void BKE_gpencil_free_brushes(struct ListBase *list); -void BKE_gpencil_free_palettes(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd, bool free_palettes); +bool BKE_gpencil_free_frame_runtime_data(struct bGPDframe *derived_gpf); +void BKE_gpencil_free_derived_frames(struct bGPdata *gpd); +void BKE_gpencil_free(struct bGPdata *gpd, bool free_all); + +void BKE_gpencil_batch_cache_dirty(struct bGPdata *gpd); +void BKE_gpencil_batch_cache_free(struct bGPdata *gpd); void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps); @@ -60,21 +80,36 @@ struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]) struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src); struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); +void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst); +struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src); + void BKE_gpencil_copy_data(struct Main *bmain, struct bGPdata *gpd_dst, const struct bGPdata *gpd_src, const int flag); +struct bGPdata *BKE_gpencil_copy(struct Main *bmain, const struct bGPdata *gpd); struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, const struct bGPdata *gpd, bool internal_copy); void BKE_gpencil_make_local(struct Main *bmain, struct bGPdata *gpd, const bool lib_local); void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); -struct bGPDpalette *BKE_gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive); -struct bGPDpalette *BKE_gpencil_palette_duplicate(const struct bGPDpalette *palette_src); -struct bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive); +/* materials */ +void BKE_gpencil_material_index_remove(struct bGPdata *gpd, int index); +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len); -struct bGPDbrush *BKE_gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive); -struct bGPDbrush *BKE_gpencil_brush_duplicate(const struct bGPDbrush *brush_src); -void BKE_gpencil_brush_init_presets(struct ToolSettings *ts); +/* statistics functions */ +void BKE_gpencil_stats_update(struct bGPdata *gpd); +/* Utilities for creating and populating GP strokes */ +/* - Number of values defining each point in the built-in data + * buffers for primitives (e.g. 2D Monkey) + */ +#define GP_PRIM_DATABUF_SIZE 5 + +void BKE_gpencil_stroke_add_points( + struct bGPDstroke *gps, + const float *array, const int totpoints, + const float mat[4][4]); + +struct bGPDstroke *BKE_gpencil_add_stroke(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness); /* Stroke and Fill - Alpha Visibility Threshold */ #define GPENCIL_ALPHA_OPACITY_THRESH 0.001f @@ -103,20 +138,40 @@ struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd); void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); -struct bGPDbrush *BKE_gpencil_brush_getactive(struct ToolSettings *ts); -void BKE_gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active); -void BKE_gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *brush); - -struct bGPDpalette *BKE_gpencil_palette_getactive(struct bGPdata *gpd); -void BKE_gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active); -void BKE_gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette); -void BKE_gpencil_palette_change_strokes(struct bGPdata *gpd); - -struct bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(struct bGPDpalette *palette); -void BKE_gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active); -void BKE_gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor); -struct bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name); -void BKE_gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname); -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name); +struct Material *BKE_gpencil_get_material_from_brush(struct Brush *brush); +struct Material *BKE_gpencil_material_ensure(struct Main *bmain, struct Object *ob); + +/* object boundbox */ +bool BKE_gpencil_stroke_minmax( + const struct bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]); + +struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob); +void BKE_gpencil_centroid_3D(struct bGPdata *gpd, float r_centroid[3]); + +/* vertex groups */ +float BKE_gpencil_vgroup_use_index(struct MDeformVert *dvert, int index); +void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup); +struct MDeformWeight *BKE_gpencil_vgroup_add_point_weight(struct MDeformVert *dvert, int index, float weight); +bool BKE_gpencil_vgroup_remove_point_weight(struct MDeformVert *dvert, int index); +void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst); + +/* GPencil geometry evaluation */ +void BKE_gpencil_eval_geometry(struct Depsgraph *depsgraph, struct bGPdata *gpd); + +/* stroke geometry utilities */ +void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]); +void BKE_gpencil_simplify_stroke(struct bGPDstroke *gps, float factor); +void BKE_gpencil_simplify_fixed(struct bGPDstroke *gps); + +void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]); + +bool BKE_gpencil_smooth_stroke(struct bGPDstroke *gps, int i, float inf); +bool BKE_gpencil_smooth_stroke_strength(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_smooth_stroke_uv(struct bGPDstroke *gps, int point_index, float influence); + +void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe); +float BKE_gpencil_multiframe_falloff_calc(struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff); #endif /* __BKE_GPENCIL_H__ */ diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h new file mode 100644 index 00000000000..cd6b6540012 --- /dev/null +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -0,0 +1,256 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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: all of this file. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_GPENCIL_MODIFIER_H__ +#define __BKE_GPENCIL_MODIFIER_H__ + +/** \file BKE_greasepencil_modifier.h + * \ingroup bke + */ + +#include "DNA_gpencil_modifier_types.h" /* needed for all enum typdefs */ +#include "BLI_compiler_attrs.h" +#include "BKE_customdata.h" + +struct ID; +struct Depsgraph; +struct DerivedMesh; +struct bContext; /* NOTE: bakeModifier() - called from UI - needs to create new datablocks, hence the need for this */ +struct Mesh; +struct Object; +struct Scene; +struct ViewLayer; +struct ListBase; +struct bArmature; +struct Main; +struct GpencilModifierData; +struct BMEditMesh; +struct DepsNodeHandle; +struct bGPDlayer; +struct bGPDframe; +struct bGPDstroke; +struct ModifierUpdateDepsgraphContext; + +#define GPENCIL_MODIFIER_ACTIVE(_md, _is_render) (((_md->mode & eGpencilModifierMode_Realtime) && (_is_render == false)) || \ + ((_md->mode & eGpencilModifierMode_Render) && (_is_render == true))) +#define GPENCIL_MODIFIER_EDIT(_md, _is_edit) (((_md->mode & eGpencilModifierMode_Editmode) == 0) && (_is_edit)) + +typedef enum { + /* Should not be used, only for None modifier type */ + eGpencilModifierTypeType_None, + + /* grease pencil modifiers */ + eGpencilModifierTypeType_Gpencil, +} GpencilModifierTypeType; + +typedef enum { + eGpencilModifierTypeFlag_SupportsMapping = (1 << 0), + eGpencilModifierTypeFlag_SupportsEditmode = (1 << 1), + + /* For modifiers that support editmode this determines if the + * modifier should be enabled by default in editmode. This should + * only be used by modifiers that are relatively speedy and + * also generally used in editmode, otherwise let the user enable + * it by hand. + */ + eGpencilModifierTypeFlag_EnableInEditmode = (1 << 2), + + /* For modifiers that require original data and so cannot + * be placed after any non-deformative modifier. + */ + eGpencilModifierTypeFlag_RequiresOriginalData = (1 << 3), + + /* max one per type */ + eGpencilModifierTypeFlag_Single = (1 << 4), + + /* can't be added manually by user */ + eGpencilModifierTypeFlag_NoUserAdd = (1 << 5), +} GpencilModifierTypeFlag; + +/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ +typedef void(*GreasePencilObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag); +typedef void(*GreasePencilIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); +typedef void(*GreasePencilTexWalkFunc)(void *userData, struct Object *ob, struct GpencilModifierData *md, const char *propname); + +typedef struct GpencilModifierTypeInfo { + /* The user visible name for this modifier */ + char name[32]; + + /* The DNA struct name for the modifier data type, used to + * write the DNA data out. + */ + char struct_name[32]; + + /* The size of the modifier data type, used by allocation. */ + int struct_size; + + GpencilModifierType type; + GpencilModifierTypeFlag flags; + + + /********************* Non-optional functions *********************/ + + /* Copy instance data for this modifier type. Should copy all user + * level settings to the target modifier. + */ + void (*copyData)(const struct GpencilModifierData *md, struct GpencilModifierData *target); + + /* Callback for GP "stroke" modifiers that operate on the + * shape and parameters of the provided strokes (e.g. Thickness, Noise, etc.) + * + * The gpl parameter contains the GP layer that the strokes come from. + * While access is provided to this data, you should not directly access + * the gpl->frames data from the modifier. Instead, use the gpf parameter + * instead. + * + * The gps parameter contains the GP stroke to operate on. This is usually a copy + * of the original (unmodified and saved to files) stroke data. + */ + void (*deformStroke)(struct GpencilModifierData *md, struct Depsgraph *depsgraph, + struct Object *ob, struct bGPDlayer *gpl, struct bGPDstroke *gps); + + /* Callback for GP "geometry" modifiers that create extra geometry + * in the frame (e.g. Array) + * + * The gpf parameter contains the GP frame/strokes to operate on. This is + * usually a copy of the original (unmodified and saved to files) stroke data. + * Modifiers should only add any generated strokes to this frame (and not one accessed + * via the gpl parameter). + * + * The modifier_index parameter indicates where the modifier is + * in the modifier stack in relation to other modifiers. + */ + void (*generateStrokes)(struct GpencilModifierData *md, struct Depsgraph *depsgraph, + struct Object *ob, struct bGPDlayer *gpl, struct bGPDframe *gpf); + + /* Bake-down GP modifier's effects into the GP datablock. + * + * This gets called when the user clicks the "Apply" button in the UI. + * As such, this callback needs to go through all layers/frames in the + * datablock, mutating the geometry and/or creating new datablocks/objects + */ + void (*bakeModifier)(struct Main *bmain, struct Depsgraph *depsgraph, + struct GpencilModifierData *md, struct Object *ob); + + /********************* Optional functions *********************/ + + /* Initialize new instance data for this modifier type, this function + * should set modifier variables to their default values. + * + * This function is optional. + */ + void (*initData)(struct GpencilModifierData *md); + + /* Free internal modifier data variables, this function should + * not free the md variable itself. + * + * This function is optional. + */ + void (*freeData)(struct GpencilModifierData *md); + + /* Return a boolean value indicating if this modifier is able to be + * calculated based on the modifier data. This is *not* regarding the + * md->flag, that is tested by the system, this is just if the data + * validates (for example, a lattice will return false if the lattice + * object is not defined). + * + * This function is optional (assumes never disabled if not present). + */ + bool (*isDisabled)(struct GpencilModifierData *md, int userRenderParams); + + /* Add the appropriate relations to the dependency graph. + * + * This function is optional. + */ + void (*updateDepsgraph)(struct GpencilModifierData *md, + const struct ModifierUpdateDepsgraphContext *ctx); + + /* Should return true if the modifier needs to be recalculated on time + * changes. + * + * This function is optional (assumes false if not present). + */ + bool (*dependsOnTime)(struct GpencilModifierData *md); + + + /* Should call the given walk function on with a pointer to each Object + * pointer that the modifier data stores. This is used for linking on file + * load and for unlinking objects or forwarding object references. + * + * This function is optional. + */ + void (*foreachObjectLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilObjectWalkFunc walk, void *userData); + + /* Should call the given walk function with a pointer to each ID + * pointer (i.e. each datablock pointer) that the modifier data + * stores. This is used for linking on file load and for + * unlinking datablocks or forwarding datablock references. + * + * This function is optional. If it is not present, foreachObjectLink + * will be used. + */ + void (*foreachIDLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilIDWalkFunc walk, void *userData); + + /* Should call the given walk function for each texture that the + * modifier data stores. This is used for finding all textures in + * the context for the UI. + * + * This function is optional. If it is not present, it will be + * assumed the modifier has no textures. + */ + void (*foreachTexLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilTexWalkFunc walk, void *userData); +} GpencilModifierTypeInfo; + +void BKE_gpencil_instance_modifier_instance_tfm(struct InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]); + +/* Initialize modifier's global data (type info and some common global storages). */ +void BKE_gpencil_modifier_init(void); + +const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type); +struct GpencilModifierData *BKE_gpencil_modifier_new(int type); +void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, const int flag); +void BKE_gpencil_modifier_free(struct GpencilModifierData *md); +bool BKE_gpencil_modifier_unique_name(struct ListBase *modifiers, struct GpencilModifierData *gmd); +bool BKE_gpencil_modifier_dependsOnTime(struct GpencilModifierData *md); +struct GpencilModifierData *BKE_gpencil_modifiers_findByType(struct Object *ob, GpencilModifierType type); +struct GpencilModifierData *BKE_gpencil_modifiers_findByName(struct Object *ob, const char *name); +void BKE_gpencil_modifier_copyData_generic(const struct GpencilModifierData *md_src, struct GpencilModifierData *md_dst); +void BKE_gpencil_modifier_copyData(struct GpencilModifierData *md, struct GpencilModifierData *target); +void BKE_gpencil_modifier_copyData_ex(struct GpencilModifierData *md, struct GpencilModifierData *target, const int flag); +void BKE_gpencil_modifiers_foreachIDLink(struct Object *ob, GreasePencilIDWalkFunc walk, void *userData); +void BKE_gpencil_modifiers_foreachTexLink(struct Object *ob, GreasePencilTexWalkFunc walk, void *userData); + +bool BKE_gpencil_has_geometry_modifiers(struct Object *ob); + +void BKE_gpencil_stroke_modifiers( + struct Depsgraph *depsgraph, struct Object *ob, + struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, bool is_render); +void BKE_gpencil_geometry_modifiers( + struct Depsgraph *depsgraph, struct Object *ob, + struct bGPDlayer *gpl, struct bGPDframe *gpf, bool is_render); + +void BKE_gpencil_lattice_init(struct Object *ob); +void BKE_gpencil_lattice_clear(struct Object *ob); + +#endif /* __BKE_GPENCIL_MODIFIER_H__ */ diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index 22897d2ea80..7a5262e0a14 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -43,7 +43,10 @@ enum { ICON_DATA_PREVIEW, /** 2D triangles: obj is #Icon_Geom */ ICON_DATA_GEOM, + /** Studiolight */ ICON_DATA_STUDIOLIGHT, + /** GPencil Layer color preview (annotations): obj is #bGPDlayer */ + ICON_DATA_GPLAYER, }; struct Icon { @@ -79,6 +82,7 @@ struct ImBuf; struct PreviewImage; struct ID; struct StudioLight; +struct bGPDlayer; enum eIconSizes; @@ -87,6 +91,9 @@ void BKE_icons_init(int first_dyn_id); /* return icon id for library object or create new icon if not found */ int BKE_icon_id_ensure(struct ID *id); +/* return icon id for Grease Pencil layer (color preview) or create new icon if not found */ +int BKE_icon_gplayer_color_ensure(struct bGPDlayer *gpl); + int BKE_icon_preview_ensure(struct ID *id, struct PreviewImage *preview); /* retrieve icon for id */ diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h index c2ac5e98f76..67e6a32edfd 100644 --- a/source/blender/blenkernel/BKE_lattice.h +++ b/source/blender/blenkernel/BKE_lattice.h @@ -54,7 +54,6 @@ void BKE_lattice_free(struct Lattice *lt); void BKE_lattice_make_local(struct Main *bmain, struct Lattice *lt, const bool lib_local); void calc_lat_fudu(int flag, int res, float *r_fu, float *r_du); -struct LatticeDeformData; struct LatticeDeformData *init_latt_deform(struct Object *oblatt, struct Object *ob) ATTR_WARN_UNUSED_RESULT; void calc_latt_deform(struct LatticeDeformData *lattice_deform_data, float co[3], float weight); void end_latt_deform(struct LatticeDeformData *lattice_deform_data); diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index c85017a2216..1ca8928c61d 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -54,11 +54,13 @@ void BKE_material_init(struct Material *ma); void BKE_material_remap_object(struct Object *ob, const unsigned int *remap); void BKE_material_remap_object_calc(struct Object *ob_dst, struct Object *ob_src, short *remap_src_to_dst); struct Material *BKE_material_add(struct Main *bmain, const char *name); +struct Material *BKE_material_add_gpencil(struct Main *bmain, const char *name); void BKE_material_copy_data(struct Main *bmain, struct Material *ma_dst, const struct Material *ma_src, const int flag); struct Material *BKE_material_copy(struct Main *bmain, const struct Material *ma); struct Material *BKE_material_localize(struct Material *ma); struct Material *give_node_material(struct Material *ma); /* returns node material or self */ void BKE_material_make_local(struct Main *bmain, struct Material *ma, const bool lib_local); +void BKE_material_init_gpencil_settings(struct Material *ma); /* UNUSED */ // void automatname(struct Material *); @@ -87,6 +89,8 @@ short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob); bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob); +struct MaterialGPencilStyle *BKE_material_gpencil_settings_get(struct Object *ob, short act); + void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma); void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 79e4f1d448a..7d795c25a04 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -37,8 +37,11 @@ extern "C" { struct Base; struct Depsgraph; +struct GpencilModifierData; struct Scene; +struct ShaderFxData; struct ViewLayer; +struct ID; struct Object; struct BoundBox; struct View3D; @@ -49,6 +52,7 @@ struct Mesh; struct RigidBodyWorld; struct HookModifierData; struct ModifierData; +struct HookGpencilModifierData; #include "DNA_object_enums.h" @@ -69,11 +73,16 @@ void BKE_object_free_derived_mesh_caches(struct Object *ob); void BKE_object_free_caches(struct Object *object); void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd); +void BKE_object_modifier_gpencil_hook_reset(struct Object *ob, struct HookGpencilModifierData *hmd); +bool BKE_object_modifier_gpencil_use_time(struct Object *ob, struct GpencilModifierData *md); + +bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md); bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type); void BKE_object_link_modifiers(struct Scene *scene, struct Object *ob_dst, const struct Object *ob_src); void BKE_object_free_modifiers(struct Object *ob, const int flag); +void BKE_object_free_shaderfx(struct Object *ob, const int flag); void BKE_object_make_proxy(struct Main *bmain, struct Object *ob, struct Object *target, struct Object *gob); void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); @@ -108,6 +117,9 @@ struct Object *BKE_object_add_from( struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, int type, const char *name, struct Object *ob_src) ATTR_NONNULL(1, 2, 3, 6) ATTR_RETURNS_NONNULL; +struct Object *BKE_object_add_for_data( + struct Main *bmain, struct ViewLayer *view_layer, + int type, const char *name, struct ID *data, bool do_id_user) ATTR_RETURNS_NONNULL; void *BKE_object_obdata_add_from_type( struct Main *bmain, int type, const char *name) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 6ade14b275c..c440a634c9f 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -77,7 +77,8 @@ typedef enum ePaintMode { ePaintTextureProjective = 3, ePaintTexture2D = 4, ePaintSculptUV = 5, - ePaintInvalid = 6 + ePaintInvalid = 6, + ePaintGpencil = 7 } ePaintMode; /* overlay invalidation */ diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h new file mode 100644 index 00000000000..11c5983106a --- /dev/null +++ b/source/blender/blenkernel/BKE_shader_fx.h @@ -0,0 +1,180 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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: all of this file. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_SHADER_FX_H__ +#define __BKE_SHADER_FX_H__ + +/** \file BKE_shader_fx.h + * \ingroup bke + */ + +#include "DNA_shader_fx_types.h" /* needed for all enum typdefs */ +#include "BLI_compiler_attrs.h" +#include "BKE_customdata.h" + +struct ID; +struct Depsgraph; +struct DerivedMesh; +struct Mesh; +struct Object; +struct Scene; +struct ViewLayer; +struct ListBase; +struct bArmature; +struct Main; +struct ShaderFxData; +struct DepsNodeHandle; +struct bGPDlayer; +struct bGPDframe; +struct bGPDstroke; +struct ModifierUpdateDepsgraphContext; + +#define SHADER_FX_ACTIVE(_fx, _is_render) (((_fx->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \ + ((_fx->mode & eShaderFxMode_Render) && (_is_render == true))) +#define SHADER_FX_EDIT(_fx, _is_edit) (((_fx->mode & eShaderFxMode_Editmode) == 0) && (_is_edit)) + +typedef enum { + /* Should not be used, only for None type */ + eShaderFxType_NoneType, + + /* grease pencil effects */ + eShaderFxType_GpencilType, +} ShaderFxTypeType; + +typedef enum { + eShaderFxTypeFlag_SupportsEditmode = (1 << 0), + + /* For effects that support editmode this determines if the + * effect should be enabled by default in editmode. + */ + eShaderFxTypeFlag_EnableInEditmode = (1 << 2), + + /* max one per type */ + eShaderFxTypeFlag_Single = (1 << 4), + + /* can't be added manually by user */ + eShaderFxTypeFlag_NoUserAdd = (1 << 5), +} ShaderFxTypeFlag; + +/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ +typedef void(*ShaderFxObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag); +typedef void(*ShaderFxIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); +typedef void(*ShaderFxTexWalkFunc)(void *userData, struct Object *ob, struct ShaderFxData *fx, const char *propname); + +typedef struct ShaderFxTypeInfo { + /* The user visible name for this effect */ + char name[32]; + + /* The DNA struct name for the effect data type, used to + * write the DNA data out. + */ + char struct_name[32]; + + /* The size of the effect data type, used by allocation. */ + int struct_size; + + ShaderFxTypeType type; + ShaderFxTypeFlag flags; + + /* Copy instance data for this effect type. Should copy all user + * level settings to the target effect. + */ + void(*copyData)(const struct ShaderFxData *fx, struct ShaderFxData *target); + + /* Initialize new instance data for this effect type, this function + * should set effect variables to their default values. + * + * This function is optional. + */ + void (*initData)(struct ShaderFxData *fx); + + /* Free internal effect data variables, this function should + * not free the fx variable itself. + * + * This function is optional. + */ + void (*freeData)(struct ShaderFxData *fx); + + /* Return a boolean value indicating if this effect is able to be + * calculated based on the effect data. This is *not* regarding the + * fx->flag, that is tested by the system, this is just if the data + * validates (for example, a lattice will return false if the lattice + * object is not defined). + * + * This function is optional (assumes never disabled if not present). + */ + bool (*isDisabled)(struct ShaderFxData *fx, int userRenderParams); + + /* Add the appropriate relations to the dependency graph. + * + * This function is optional. + */ + void (*updateDepsgraph)(struct ShaderFxData *fx, + const struct ModifierUpdateDepsgraphContext *ctx); + + /* Should return true if the effect needs to be recalculated on time + * changes. + * + * This function is optional (assumes false if not present). + */ + bool (*dependsOnTime)(struct ShaderFxData *fx); + + + /* Should call the given walk function on with a pointer to each Object + * pointer that the effect data stores. This is used for linking on file + * load and for unlinking objects or forwarding object references. + * + * This function is optional. + */ + void (*foreachObjectLink)(struct ShaderFxData *fx, struct Object *ob, + ShaderFxObjectWalkFunc walk, void *userData); + + /* Should call the given walk function with a pointer to each ID + * pointer (i.e. each datablock pointer) that the effect data + * stores. This is used for linking on file load and for + * unlinking datablocks or forwarding datablock references. + * + * This function is optional. If it is not present, foreachObjectLink + * will be used. + */ + void (*foreachIDLink)(struct ShaderFxData *fx, struct Object *ob, + ShaderFxIDWalkFunc walk, void *userData); +} ShaderFxTypeInfo; + +/* Initialize global data (type info and some common global storages). */ +void BKE_shaderfx_init(void); + +const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type); +struct ShaderFxData *BKE_shaderfx_new(int type); +void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag); +void BKE_shaderfx_free(struct ShaderFxData *fx); +bool BKE_shaderfx_unique_name(struct ListBase *shaderfx, struct ShaderFxData *fx); +bool BKE_shaderfx_dependsOnTime(struct ShaderFxData *fx); +struct ShaderFxData *BKE_shaderfx_findByType(struct Object *ob, ShaderFxType type); +struct ShaderFxData *BKE_shaderfx_findByName(struct Object *ob, const char *name); +void BKE_shaderfx_copyData_generic(const struct ShaderFxData *fx_src, struct ShaderFxData *fx_dst); +void BKE_shaderfx_copyData(struct ShaderFxData *fx, struct ShaderFxData *target); +void BKE_shaderfx_copyData_ex(struct ShaderFxData *fx, struct ShaderFxData *target, const int flag); +void BKE_shaderfx_foreachIDLink(struct Object *ob, ShaderFxIDWalkFunc walk, void *userData); + +bool BKE_shaderfx_has_gpencil(struct Object *ob); + +#endif /* __BKE_SHADER_FX_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 01910bffdb0..7169597f100 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -38,6 +38,8 @@ set(INC ../makesrna ../bmesh ../modifiers + ../gpencil_modifiers + ../shader_fx ../nodes ../physics ../render/extern/include @@ -115,6 +117,7 @@ set(SRC intern/font.c intern/freestyle.c intern/gpencil.c + intern/gpencil_modifier.c intern/icons.c intern/icons_rasterize.c intern/idcode.c @@ -180,6 +183,7 @@ set(SRC intern/seqeffects.c intern/seqmodifier.c intern/sequencer.c + intern/shader_fx.c intern/shrinkwrap.c intern/smoke.c intern/softbody.c @@ -259,6 +263,7 @@ set(SRC BKE_freestyle.h BKE_global.h BKE_gpencil.h + BKE_gpencil_modifier.h BKE_icons.h BKE_idcode.h BKE_idprop.h @@ -306,6 +311,7 @@ set(SRC BKE_scene.h BKE_screen.h BKE_sequencer.h + BKE_shader_fx.h BKE_shrinkwrap.h BKE_smoke.h BKE_softbody.h diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fd7497f9ba1..7dfedfe6c06 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -104,6 +104,7 @@ bool id_type_can_have_animdata(const short id_type) case ID_MSK: case ID_GD: case ID_CF: + case ID_PAL: return true; /* no AnimData */ @@ -1150,6 +1151,9 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use /* grease pencil */ ANIMDATA_IDS_CB(bmain->gpencil.first); + /* palettes */ + ANIMDATA_IDS_CB(bmain->palettes.first); + /* cache files */ ANIMDATA_IDS_CB(bmain->cachefiles.first); } @@ -2925,6 +2929,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, Scene /* grease pencil */ EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM); + /* palettes */ + EVAL_ANIM_IDS(main->palettes.first, ADT_RECALC_ANIM); + /* cache files */ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 42cd7968321..598eb9b5b54 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -29,6 +29,7 @@ #include "DNA_brush_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" @@ -36,6 +37,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" +#include "BKE_context.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_library_query.h" @@ -129,6 +131,7 @@ static void brush_defaults(Brush *brush) brush->stencil_dimension[0] = 256; brush->stencil_dimension[1] = 256; + } /* Datablock add/copy/free/make_local */ @@ -164,6 +167,368 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) return brush; } +/* add a new gp-brush */ +Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name) +{ + Brush *brush; + Paint *paint = BKE_brush_get_gpencil_paint(ts); + brush = BKE_brush_add(bmain, name, OB_MODE_GPENCIL_PAINT); + + BKE_paint_brush_set(paint, brush); + id_us_min(&brush->id); + + /* grease pencil basic settings */ + brush->size = 3; + + brush->gpencil_settings = MEM_callocN(sizeof(BrushGpencilSettings), "BrushGpencilSettings"); + + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->flag = 0; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + + /* curves */ + brush->gpencil_settings->curve_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + /* return brush */ + return brush; +} + +Paint *BKE_brush_get_gpencil_paint(ToolSettings *ts) +{ + /* alloc paint session */ + if (ts->gp_paint == NULL) { + ts->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + } + + return &ts->gp_paint->paint; +} + +/* grease pencil cumapping->preset */ +typedef enum eGPCurveMappingPreset { + GPCURVE_PRESET_PENCIL = 0, + GPCURVE_PRESET_INK = 1, + GPCURVE_PRESET_INKNOISE = 2, +} eGPCurveMappingPreset; + +static void brush_gpencil_curvemap_reset(CurveMap *cuma, int preset) +{ + if (cuma->curve) + MEM_freeN(cuma->curve); + + cuma->totpoint = 3; + cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); + + switch (preset) { + case GPCURVE_PRESET_PENCIL: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.75115f; + cuma->curve[1].y = 0.25f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INK: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63448f; + cuma->curve[1].y = 0.375f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INKNOISE: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63134f; + cuma->curve[1].y = 0.3625f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + } + + if (cuma->table) { + MEM_freeN(cuma->table); + cuma->table = NULL; + } +} + +/* create a set of grease pencil presets */ +void BKE_brush_gpencil_presets(bContext *C) +{ +#define SMOOTH_STROKE_RADIUS 40 +#define SMOOTH_STROKE_FACTOR 0.9f + + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = BKE_brush_get_gpencil_paint(ts); + Main *bmain = CTX_data_main(C); + + Brush *brush, *deft; + CurveMapping *custom_curve; + + /* Pencil brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pencil"); + brush->size = 25.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.6f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Pen brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pen"); + deft = brush; /* save default brush */ + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Ink brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Ink"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.6f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INK); + + /* Ink Noise brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Noise"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.7f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 1.0f; + brush->gpencil_settings->draw_smoothlvl = 2; + brush->gpencil_settings->thick_smoothfac = 0.5f; + brush->gpencil_settings->thick_smoothlvl = 2; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INKNOISE); + + /* Block Basic brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Block"); + brush->size = 150.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.7f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.0f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->draw_random_sub = 0; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_BLOCK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Marker brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Marker"); + brush->size = 80.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.374f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = M_PI_4; /* 45 degrees */ + brush->gpencil_settings->draw_angle_factor = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Fill brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Fill Area"); + brush->size = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->fill_leak = 3; + brush->gpencil_settings->fill_threshold = 0.1f; + brush->gpencil_settings->fill_simplylvl = 1; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_FILL; + + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + brush->gpencil_settings->draw_strength = 1.0f; + + /* Soft Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Soft"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + + /* Hard Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Hard"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; + + /* Stroke Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Stroke"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; + + /* set defaut brush */ + BKE_paint_brush_set(paint, deft); + +} + +/* get the active gp-brush for editing */ +Brush *BKE_brush_getactive_gpencil(ToolSettings *ts) +{ + /* error checking */ + if (ELEM(NULL, ts, ts->gp_paint)) { + return NULL; + } + Paint *paint = &ts->gp_paint->paint; + + return paint->brush; +} + struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) { Brush *brush; @@ -197,6 +562,12 @@ void BKE_brush_copy_data(Main *UNUSED(bmain), Brush *brush_dst, const Brush *bru } brush_dst->curve = curvemapping_copy(brush_src->curve); + if (brush_src->gpencil_settings != NULL) { + brush_dst->gpencil_settings = MEM_dupallocN(brush_src->gpencil_settings); + brush_dst->gpencil_settings->curve_sensitivity = curvemapping_copy(brush_src->gpencil_settings->curve_sensitivity); + brush_dst->gpencil_settings->curve_strength = curvemapping_copy(brush_src->gpencil_settings->curve_strength); + brush_dst->gpencil_settings->curve_jitter = curvemapping_copy(brush_src->gpencil_settings->curve_jitter); + } /* enable fake user by default */ id_fake_user_set(&brush_dst->id); @@ -215,11 +586,18 @@ void BKE_brush_free(Brush *brush) if (brush->icon_imbuf) { IMB_freeImBuf(brush->icon_imbuf); } - curvemapping_free(brush->curve); + if (brush->gpencil_settings != NULL) { + curvemapping_free(brush->gpencil_settings->curve_sensitivity); + curvemapping_free(brush->gpencil_settings->curve_strength); + curvemapping_free(brush->gpencil_settings->curve_jitter); + MEM_SAFE_FREE(brush->gpencil_settings); + } + MEM_SAFE_FREE(brush->gradient); + BKE_previewimg_free(&(brush->preview)); } diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index ff4795afe87..d18572a57f6 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -282,6 +282,7 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) case CURVE_PRESET_MID9: cuma->totpoint = 9; break; case CURVE_PRESET_ROUND: cuma->totpoint = 4; break; case CURVE_PRESET_ROOT: cuma->totpoint = 4; break; + case CURVE_PRESET_GAUSS: cuma->totpoint = 7; break; } cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points"); @@ -352,6 +353,24 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) cuma->curve[3].x = 1; cuma->curve[3].y = 0; break; + case CURVE_PRESET_GAUSS: + cuma->curve[0].x = 0; + cuma->curve[0].y = 0.025f; + cuma->curve[1].x = 0.16f; + cuma->curve[1].y = 0.135f; + cuma->curve[2].x = 0.298f; + cuma->curve[2].y = 0.36f; + + cuma->curve[3].x = 0.50f; + cuma->curve[3].y = 1.0f; + + cuma->curve[4].x = 0.70f; + cuma->curve[4].y = 0.36f; + cuma->curve[5].x = 0.84f; + cuma->curve[5].y = 0.135f; + cuma->curve[6].x = 1.0f; + cuma->curve[6].y = 0.025f; + break; } /* mirror curve in x direction to have positive slope diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 3dfe9732062..84ca143dc55 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1014,6 +1014,10 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM else if (object_mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX; else if (object_mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE; else if (object_mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE; + else if (object_mode & OB_MODE_GPENCIL_PAINT) return CTX_MODE_GPENCIL_PAINT; + else if (object_mode & OB_MODE_GPENCIL_EDIT) return CTX_MODE_GPENCIL_EDIT; + else if (object_mode & OB_MODE_GPENCIL_SCULPT) return CTX_MODE_GPENCIL_SCULPT; + else if (object_mode & OB_MODE_GPENCIL_WEIGHT) return CTX_MODE_GPENCIL_WEIGHT; } } @@ -1044,6 +1048,10 @@ static const char *data_mode_strings[] = { "imagepaint", "particlemode", "objectmode", + "greasepencil_paint", + "greasepencil_edit", + "greasepencil_sculpt", + "greasepencil_weight", NULL }; BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1, "Must have a string for each context mode") @@ -1212,17 +1220,7 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C) return ctx_data_pointer_get(C, "active_gpencil_layer"); } -bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palette"); -} - -bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palettecolor"); -} - -bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C) +Brush *CTX_data_active_gpencil_brush(const bContext *C) { return ctx_data_pointer_get(C, "active_gpencil_brush"); } diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index d08e3643ca7..ddf9840a32e 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -74,7 +74,9 @@ bDeformGroup *BKE_defgroup_new(Object *ob, const char *name) BLI_addtail(&ob->defbase, defgroup); defgroup_unique_name(defgroup, ob); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + if (ob->type != OB_GPENCIL) { + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } return defgroup; } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index e89508fd6c0..de3f891f9f9 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -39,26 +39,83 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_math_color.h" #include "BLI_string_utils.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" #include "BLT_translation.h" +#include "DNA_anim_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_material_types.h" #include "DNA_gpencil_types.h" #include "DNA_userdef_types.h" #include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "BKE_context.h" +#include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_colortools.h" +#include "BKE_icons.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_material.h" +#include "DEG_depsgraph.h" /* ************************************************** */ -/* GENERAL STUFF */ +/* Draw Engine */ -/* --------- Memory Management ------------ */ +void(*BKE_gpencil_batch_cache_dirty_cb)(bGPdata *gpd) = NULL; +void(*BKE_gpencil_batch_cache_free_cb)(bGPdata *gpd) = NULL; + +void BKE_gpencil_batch_cache_dirty(bGPdata *gpd) +{ + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + BKE_gpencil_batch_cache_dirty_cb(gpd); + } +} + +void BKE_gpencil_batch_cache_free(bGPdata *gpd) +{ + if (gpd) { + BKE_gpencil_batch_cache_free_cb(gpd); + } +} + +/* ************************************************** */ +/* Memory Management */ + +/* clean vertex groups weights */ +void BKE_gpencil_free_point_weights(MDeformVert *dvert) +{ + if (dvert == NULL) { + return; + } + MEM_SAFE_FREE(dvert->dw); +} + +void BKE_gpencil_free_stroke_weights(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + if (gps->dvert == NULL) { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } +} /* free stroke, doesn't unlink from any listbase */ void BKE_gpencil_free_stroke(bGPDstroke *gps) @@ -66,10 +123,14 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps) if (gps == NULL) { return; } - /* free stroke memory arrays, then stroke itself */ - if (gps->points) + if (gps->points) { MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } if (gps->triangles) MEM_freeN(gps->triangles); @@ -92,6 +153,26 @@ bool BKE_gpencil_free_strokes(bGPDframe *gpf) return changed; } +/* Free strokes and colors belonging to a gp-frame */ +bool BKE_gpencil_free_frame_runtime_data(bGPDframe *derived_gpf) +{ + bGPDstroke *gps_next; + if (!derived_gpf) { + return false; + } + + /* free strokes */ + for (bGPDstroke *gps = derived_gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + BKE_gpencil_free_stroke(gps); + } + BLI_listbase_clear(&derived_gpf->strokes); + + MEM_SAFE_FREE(derived_gpf); + + return true; +} + /* Free all of a gp-layer's frames */ void BKE_gpencil_free_frames(bGPDlayer *gpl) { @@ -111,101 +192,101 @@ void BKE_gpencil_free_frames(bGPDlayer *gpl) gpl->actframe = NULL; } -/* Free all of a gp-colors */ -static void free_gpencil_colors(bGPDpalette *palette) -{ - /* error checking */ - if (palette == NULL) { - return; - } - /* free colors */ - BLI_freelistN(&palette->colors); -} -/* Free all of the gp-palettes and colors */ -void BKE_gpencil_free_palettes(ListBase *list) +/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ +void BKE_gpencil_free_layers(ListBase *list) { - bGPDpalette *palette_next; + bGPDlayer *gpl_next; /* error checking */ - if (list == NULL) { - return; - } + if (list == NULL) return; - /* delete palettes */ - for (bGPDpalette *palette = list->first; palette; palette = palette_next) { - palette_next = palette->next; - /* free palette colors */ - free_gpencil_colors(palette); + /* delete layers */ + for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { + gpl_next = gpl->next; - MEM_freeN(palette); + /* free layers and their data */ + BKE_gpencil_free_frames(gpl); + BLI_freelinkN(list, gpl); } - BLI_listbase_clear(list); } -/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ -void BKE_gpencil_free_brushes(ListBase *list) +/* clear all runtime derived data */ +static void BKE_gpencil_clear_derived(bGPDlayer *gpl) { - bGPDbrush *brush_next; + GHashIterator gh_iter; - /* error checking */ - if (list == NULL) { + if (gpl->runtime.derived_data == NULL) { return; } - /* delete brushes */ - for (bGPDbrush *brush = list->first; brush; brush = brush_next) { - brush_next = brush->next; - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); + GHASH_ITER(gh_iter, gpl->runtime.derived_data) { + bGPDframe *gpf = (bGPDframe *)BLI_ghashIterator_getValue(&gh_iter); + if (gpf) { + BKE_gpencil_free_frame_runtime_data(gpf); } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); - } - - MEM_freeN(brush); } - BLI_listbase_clear(list); } -/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ -void BKE_gpencil_free_layers(ListBase *list) +/* Free all of the gp-layers temp data*/ +static void BKE_gpencil_free_layers_temp_data(ListBase *list) { bGPDlayer *gpl_next; /* error checking */ if (list == NULL) return; - /* delete layers */ for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { gpl_next = gpl->next; + BKE_gpencil_clear_derived(gpl); - /* free layers and their data */ - BKE_gpencil_free_frames(gpl); - BLI_freelinkN(list, gpl); + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + } +} + +/* Free temp gpf derived frames */ +void BKE_gpencil_free_derived_frames(bGPdata *gpd) +{ + /* error checking */ + if (gpd == NULL) return; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + BKE_gpencil_clear_derived(gpl); + + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } } } /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd, bool free_palettes) +void BKE_gpencil_free(bGPdata *gpd, bool free_all) { + /* clear animation data */ BKE_animdata_free(&gpd->id, false); /* free layers */ + if (free_all) { + BKE_gpencil_free_layers_temp_data(&gpd->layers); + } BKE_gpencil_free_layers(&gpd->layers); - /* free palettes */ - if (free_palettes) { - BKE_gpencil_free_palettes(&gpd->palettes); + /* materials */ + MEM_SAFE_FREE(gpd->mat); + + /* free all data */ + if (free_all) { + /* clear cache */ + BKE_gpencil_batch_cache_free(gpd); } } -/* -------- Container Creation ---------- */ +/* ************************************************** */ +/* Container Creation */ /* add a new gp-frame to the given layer */ bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) @@ -329,28 +410,31 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti /* add to datablock */ BLI_addtail(&gpd->layers, gpl); - /* set basic settings */ - copy_v4_v4(gpl->color, U.gpencil_new_layer_col); - /* Since GPv2 thickness must be 0 */ - gpl->thickness = 0; - - gpl->opacity = 1.0f; + /* annotation vs GP Object behaviour is slightly different */ + if (gpd->flag & GP_DATA_ANNOTATIONS) { + /* set default color of new strokes for this layer */ + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); + gpl->opacity = 1.0f; - /* onion-skinning settings */ - if (gpd->flag & GP_DATA_SHOW_ONIONSKINS) - gpl->flag |= GP_LAYER_ONIONSKIN; + /* set default thickness of new strokes for this layer */ + gpl->thickness = 3; - gpl->flag |= (GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL); - - ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ - ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ - - /* high quality fill by default */ - gpl->flag |= GP_LAYER_HQ_FILL; + /* onion-skinning settings */ + gpl->onion_flag |= GP_LAYER_ONIONSKIN; + } + else { + /* thickness parameter represents "thickness change", not absolute thickness */ + gpl->thickness = 0; + gpl->opacity = 1.0f; + } /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); - BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + BLI_uniquename(&gpd->layers, gpl, + (gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(gpl->info)); /* make this one the active one */ if (setactive) @@ -360,292 +444,153 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti return gpl; } -/* add a new gp-palette and make it the active */ -bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) -{ - bGPDpalette *palette; - - /* check that list is ok */ - if (gpd == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); - - /* add to datablock */ - BLI_addtail(&gpd->palettes, palette); - - /* set basic settings */ - /* auto-name */ - BLI_strncpy(palette->info, name, sizeof(palette->info)); - BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), - sizeof(palette->info)); - - /* make this one the active one */ - /* NOTE: Always make this active if there's nothing else yet (T50123) */ - if ((setactive) || (gpd->palettes.first == gpd->palettes.last)) { - BKE_gpencil_palette_setactive(gpd, palette); - } - - /* return palette */ - return palette; -} - -/* create a set of default drawing brushes with predefined presets */ -void BKE_gpencil_brush_init_presets(ToolSettings *ts) +/* add a new gp-datablock */ +bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) { - bGPDbrush *brush; - /* Basic brush */ - brush = BKE_gpencil_brush_addnew(ts, "Basic", true); - brush->thickness = 3.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 0; - brush->draw_random_sub = 0.0f; - - /* Pencil brush */ - brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.7f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Ink brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.6f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + bGPdata *gpd; - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; + /* allocate memory for a new block */ + gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0); - /* Ink Noise brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false); - brush->thickness = 6.0f; - brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.611f; - brush->flag |= GP_BRUSH_USE_PRESSURE; + /* initial settings */ + gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + /* general flags */ + gpd->flag |= GP_DATA_VIEWALIGN; - brush->draw_random_press = 1.0f; + /* GP object specific settings */ + ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f); - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + gpd->xray_mode = GP_XRAY_3DSPACE; + gpd->runtime.batch_cache_data = NULL; + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + /* onion-skinning settings (datablock level) */ + gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); + gpd->onion_flag |= GP_ONION_FADE; + gpd->onion_mode = GP_ONION_MODE_RELATIVE; + gpd->onion_factor = 0.5f; + ARRAY_SET_ITEMS(gpd->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ + ARRAY_SET_ITEMS(gpd->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ + gpd->gstep = 1; + gpd->gstep_next = 1; - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; + return gpd; +} - /* Marker brush */ - brush = BKE_gpencil_brush_addnew(ts, "Marker", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 2.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; +/* ************************************************** */ +/* Primitive Creation */ +/* Utilities for easier bulk-creation of geometry */ - brush->draw_random_press = 0.0f; +/** + * Populate stroke with point data from data buffers + * + * \param array Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values + * \param mat 4x4 transform matrix to transform points into the right coordinate space + */ +void BKE_gpencil_stroke_add_points(bGPDstroke *gps, const float *array, const int totpoints, const float mat[4][4]) +{ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + const int x = GP_PRIM_DATABUF_SIZE * i; - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + pt->x = array[x]; + pt->y = array[x + 1]; + pt->z = array[x + 2]; + mul_m4_v3(mat, &pt->x); - brush->draw_angle = M_PI_4; /* 45 degrees */ - brush->draw_angle_factor = 1.0f; + pt->pressure = array[x + 3]; + pt->strength = array[x + 4]; + } +} - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; +/* Create a new stroke, with pre-allocated data buffers */ +bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness) +{ + /* allocate memory for a new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - /* Crayon brush */ - brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 3.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; + gps->thickness = thickness * 25; + gps->inittime = 0; - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.140f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + /* enable recalculation flag by default */ + gps->flag = GP_STROKE_RECALC_CACHES | GP_STROKE_3DSPACE; - brush->draw_random_press = 0.0f; + gps->totpoints = totpoints; + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; + gps->mat_nr = mat_idx; - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 2; - brush->draw_random_sub = 0.5f; + /* add to frame */ + BLI_addtail(&gpf->strokes, gps); + return gps; } -/* add a new gp-brush and make it the active */ -bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) -{ - bGPDbrush *brush; - - /* check that list is ok */ - if (ts == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush"); - /* add to datablock */ - BLI_addtail(&ts->gp_brushes, brush); - - /* set basic settings */ - brush->thickness = 3; - brush->draw_smoothlvl = 1; - brush->flag |= GP_BRUSH_USE_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* curves */ - brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - - /* auto-name */ - BLI_strncpy(brush->info, name, sizeof(brush->info)); - BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); - - /* make this one the active one */ - if (setactive) { - BKE_gpencil_brush_setactive(ts, brush); - } - - /* return brush */ - return brush; -} +/* ************************************************** */ +/* Data Duplication */ -/* add a new gp-palettecolor and make it the active */ -bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) +/* make a copy of a given gpencil weights */ +void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst) { - bGPDpalettecolor *palcolor; - - /* check that list is ok */ - if (palette == NULL) { - return NULL; + if (gps_src == NULL) { + return; } + BLI_assert(gps_src->totpoints == gps_dst->totpoints); - /* allocate memory and add to end of list */ - palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); - - /* add to datablock */ - BLI_addtail(&palette->colors, palcolor); - - /* set basic settings */ - palcolor->flag |= PC_COLOR_HQ_FILL; - copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); - ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); + if ((gps_src->dvert == NULL) || (gps_dst->dvert == NULL)){ + return; + } - /* auto-name */ - BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); - BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), - sizeof(palcolor->info)); + for (int i = 0; i < gps_src->totpoints; i++) { + MDeformVert *dvert_src = &gps_src->dvert[i]; + MDeformVert *dvert_dst = &gps_dst->dvert[i]; + if (dvert_src->totweight > 0) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + else { + dvert_dst->dw = NULL; + } - /* make this one the active one */ - if (setactive) { - BKE_gpencil_palettecolor_setactive(palette, palcolor); } - - /* return palette color */ - return palcolor; } -/* add a new gp-datablock */ -bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) +/* make a copy of a given gpencil stroke */ +bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src) { - bGPdata *gpd; + bGPDstroke *gps_dst = NULL; - /* allocate memory for a new block */ - gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0); + gps_dst = MEM_dupallocN(gps_src); + gps_dst->prev = gps_dst->next = NULL; - /* initial settings */ - gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); + gps_dst->points = MEM_dupallocN(gps_src->points); - /* for now, stick to view is also enabled by default - * since this is more useful... + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); + + /* Don't clear triangles, so that modifier evaluation can just use + * this without extra work first. Most places that need to force + * this data to get recalculated will destroy the data anyway though. */ - gpd->flag |= GP_DATA_VIEWALIGN; + gps_dst->triangles = MEM_dupallocN(gps_dst->triangles); + /* gps_dst->flag |= GP_STROKE_RECALC_CACHES; */ - return gpd; + /* return new stroke */ + return gps_dst; } -/* -------- Data Duplication ---------- */ - /* make a copy of a given gpencil frame */ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { - bGPDstroke *gps_dst; + bGPDstroke *gps_dst = NULL; bGPDframe *gpf_dst; /* error checking */ @@ -660,11 +605,8 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) /* copy strokes */ BLI_listbase_clear(&gpf_dst->strokes); for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_CACHES; + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); BLI_addtail(&gpf_dst->strokes, gps_dst); } @@ -672,55 +614,24 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) return gpf_dst; } -/* make a copy of a given gpencil brush */ -bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) +/* make a copy of strokes between gpencil frames */ +void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_dst) { - bGPDbrush *brush_dst; - + bGPDstroke *gps_dst = NULL; /* error checking */ - if (brush_src == NULL) { - return NULL; - } - - /* make a copy of source brush */ - brush_dst = MEM_dupallocN(brush_src); - brush_dst->prev = brush_dst->next = NULL; - /* make a copy of curves */ - brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity); - brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength); - brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter); - - /* return new brush */ - return brush_dst; -} - -/* make a copy of a given gpencil palette */ -bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) -{ - bGPDpalette *palette_dst; - const bGPDpalettecolor *palcolor_src; - bGPDpalettecolor *palcolord_dst; - - /* error checking */ - if (palette_src == NULL) { - return NULL; + if ((gpf_src == NULL) || (gpf_dst == NULL)) { + return; } - /* make a copy of source palette */ - palette_dst = MEM_dupallocN(palette_src); - palette_dst->prev = palette_dst->next = NULL; - - /* copy colors */ - BLI_listbase_clear(&palette_dst->colors); - for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) { - /* make a copy of source */ - palcolord_dst = MEM_dupallocN(palcolor_src); - BLI_addtail(&palette_dst->colors, palcolord_dst); + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + BLI_addtail(&gpf_dst->strokes, gps_dst); } - - /* return new palette */ - return palette_dst; } + /* make a copy of a given gpencil layer */ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { @@ -736,6 +647,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /* make a copy of source layer */ gpl_dst = MEM_dupallocN(gpl_src); gpl_dst->prev = gpl_dst->next = NULL; + gpl_dst->runtime.derived_data = NULL; /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); @@ -755,7 +667,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /** * Only copy internal data of GreasePencil ID from source to already allocated/initialized destination. - * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. + * You probably never want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. * * WARNING! This function will not handle ID user count! * @@ -763,6 +675,14 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) */ void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) { + /* cache data is not duplicated */ + gpd_dst->runtime.batch_cache_data = NULL; + + /* duplicate material array */ + if (gpd_src->mat) { + gpd_dst->mat = MEM_dupallocN(gpd_src->mat); + } + /* copy layers */ BLI_listbase_clear(&gpd_dst->layers); for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { @@ -771,45 +691,46 @@ void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata BLI_addtail(&gpd_dst->layers, gpl_dst); } - /* copy palettes */ - BLI_listbase_clear(&gpd_dst->palettes); - for (const bGPDpalette *palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) { - bGPDpalette *palette_dst = BKE_gpencil_palette_duplicate(palette_src); /* TODO here too could add unused flags... */ - BLI_addtail(&gpd_dst->palettes, palette_dst); - } +} + +/* Standard API to make a copy of GP datablock, separate from copying its data */ +bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd) +{ + bGPdata *gpd_copy; + BKE_id_copy_ex(bmain, &gpd->id, (ID **)&gpd_copy, 0, false); + return gpd_copy; } /* make a copy of a given gpencil datablock */ +// XXX: Should this be deprecated? bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy) { + bGPdata *gpd_dst; + /* Yuck and super-uber-hyper yuck!!! * Should be replaceable with a no-main copy (LIB_ID_COPY_NO_MAIN etc.), but not sure about it, * so for now keep old code for that one. */ - if (internal_copy) { - const bGPDlayer *gpl_src; - bGPDlayer *gpl_dst; - bGPdata *gpd_dst; + /* error checking */ + if (gpd_src == NULL) { + return NULL; + } + + if (internal_copy) { /* make a straight copy for undo buffers used during stroke drawing */ gpd_dst = MEM_dupallocN(gpd_src); - - /* copy layers */ - BLI_listbase_clear(&gpd_dst->layers); - for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { - /* make a copy of source layer and its data */ - gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); - BLI_addtail(&gpd_dst->layers, gpl_dst); - } - - /* return new */ - return gpd_dst; } else { BLI_assert(bmain != NULL); - bGPdata *gpd_copy; - BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_copy, 0, false); - return gpd_copy; + BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_dst, 0, false); + gpd_dst->runtime.batch_cache_data = NULL; } + + /* Copy internal data (layers, etc.) */ + BKE_gpencil_copy_data(bmain, gpd_dst, gpd_src, 0); + + /* return new */ + return gpd_dst; } void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) @@ -817,7 +738,8 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) BKE_id_make_local_generic(bmain, &gpd->id, true, lib_local); } -/* -------- GP-Stroke API --------- */ +/* ************************************************** */ +/* GP Stroke API */ /* ensure selection status of stroke is in sync with its points */ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) @@ -842,7 +764,8 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) } } -/* -------- GP-Frame API ---------- */ +/* ************************************************** */ +/* GP Frame API */ /* delete the last stroke of the given frame */ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) @@ -855,7 +778,13 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) return; /* free the stroke and its data */ - MEM_freeN(gps->points); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); @@ -866,7 +795,8 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) } } -/* -------- GP-Layer API ---------- */ +/* ************************************************** */ +/* GP Layer API */ /* Check if the given layer is able to be edited or not */ bool gpencil_layer_is_editable(const bGPDlayer *gpl) @@ -1048,8 +978,6 @@ bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) */ if (gpl->actframe == gpf) gpl->actframe = gpf->prev; - else - gpl->actframe = NULL; /* free the frame and its data */ changed = BKE_gpencil_free_strokes(gpf); @@ -1103,255 +1031,602 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) /* free layer */ BKE_gpencil_free_frames(gpl); + + /* free icon providing preview of icon color */ + BKE_icon_delete(gpl->runtime.icon_id); + + /* free derived data */ + BKE_gpencil_clear_derived(gpl); + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + BLI_freelinkN(&gpd->layers, gpl); } -/* ************************************************** */ -/* get the active gp-brush for editing */ -bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts) +Material *BKE_gpencil_get_material_from_brush(Brush *brush) { - bGPDbrush *brush; + Material *ma = NULL; - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first)) { - return NULL; + if ((brush != NULL) && (brush->gpencil_settings != NULL) && + (brush->gpencil_settings->material != NULL)) + { + ma = brush->gpencil_settings->material; } - /* loop over brushes until found (assume only one active) */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - if (brush->flag & GP_BRUSH_ACTIVE) { - return brush; + return ma; +} + +/* Get active color, and add all default settings if we don't find anything */ +Material *BKE_gpencil_material_ensure(Main *bmain, Object *ob) +{ + Material *ma = NULL; + + /* sanity checks */ + if (ELEM(NULL, bmain, ob)) + return NULL; + + ma = give_current_material(ob, ob->actcol); + if (ma == NULL) { + if (ob->totcol == 0) { + BKE_object_material_slot_add(bmain, ob); } + ma = BKE_material_add_gpencil(bmain, DATA_("Material")); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + } + else if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); } - /* no active brush found */ - return NULL; + return ma; } -/* set the active gp-brush */ -void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +/* ************************************************** */ +/* GP Object - Boundbox Support */ + +/** + * Get min/max coordinate bounds for single stroke + * \return Returns whether we found any selected points + */ +bool BKE_gpencil_stroke_minmax( + const bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]) { - bGPDbrush *brush; + const bGPDspoint *pt; + int i; + bool changed = false; - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first, active)) { - return; - } + if (ELEM(NULL, gps, r_min, r_max)) + return false; - /* loop over brushes deactivating all */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - brush->flag &= ~GP_BRUSH_ACTIVE; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) { + minmax_v3v3_v3(r_min, r_max, &pt->x); + changed = true; + } } - - /* set as active one */ - active->flag |= GP_BRUSH_ACTIVE; + return changed; } -/* delete the active gp-brush */ -void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) +/* get min/max bounds of all strokes in GP datablock */ +static void gpencil_minmax(bGPdata *gpd, float r_min[3], float r_max[3]) { - /* error checking */ - if (ELEM(NULL, ts, brush)) { + INIT_MINMAX(r_min, r_max); + + if (gpd == NULL) return; - } - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf = gpl->actframe; + + if (gpf != NULL) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); + } + } } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); +} + +/* compute center of bounding box */ +void BKE_gpencil_centroid_3D(bGPdata *gpd, float r_centroid[3]) +{ + float min[3], max[3], tot[3]; + + gpencil_minmax(gpd, min, max); + + add_v3_v3v3(tot, min, max); + mul_v3_v3fl(r_centroid, tot, 0.5f); +} + + +/* create bounding box values */ +static void boundbox_gpencil(Object *ob) +{ + BoundBox *bb; + bGPdata *gpd; + float min[3], max[3]; + + if (ob->bb == NULL) { + ob->bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); } - /* free */ - BLI_freelinkN(&ts->gp_brushes, brush); + bb = ob->bb; + gpd = ob->data; + + gpencil_minmax(gpd, min, max); + BKE_boundbox_init_from_minmax(bb, min, max); + + bb->flag &= ~BOUNDBOX_DIRTY; } -/* ************************************************** */ -/* get the active gp-palette for editing */ -bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) +/* get bounding box */ +BoundBox *BKE_gpencil_boundbox_get(Object *ob) { - bGPDpalette *palette; + bGPdata *gpd; - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first)) { + if (ELEM(NULL, ob, ob->data)) return NULL; - } - /* loop over palettes until found (assume only one active) */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - if (palette->flag & PL_PALETTE_ACTIVE) - return palette; + gpd = ob->data; + if ((ob->bb) && ((ob->bb->flag & BOUNDBOX_DIRTY) == 0) && + ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) + { + return ob->bb; } - /* no active palette found */ - return NULL; + boundbox_gpencil(ob); + + return ob->bb; } -/* set the active gp-palette */ -void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) -{ - bGPDpalette *palette; +/* ************************************************** */ +/* Apply Transforms */ - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first, active)) { +void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) +{ + if (gpd == NULL) return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* FIXME: For now, we just skip parented layers. + * Otherwise, we have to update each frame to find + * the current parent position/effects. + */ + if (gpl->parent) { + continue; + } + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) { + mul_m4_v3(mat, &pt->x); + } + + /* TODO: Do we need to do this? distortion may mean we need to re-triangulate */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } + } } - /* loop over palettes deactivating all */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - palette->flag &= ~PL_PALETTE_ACTIVE; +} + +/* ************************************************** */ +/* GP Object - Vertex Groups */ + +/* remove a vertex group */ +void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) +{ + bGPdata *gpd = ob->data; + MDeformVert *dvert = NULL; + MDeformWeight *gpw = NULL; + const int def_nr = BLI_findindex(&ob->defbase, defgroup); + + /* Remove points data */ + if (gpd) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + for (int i2 = 0; i2 < dvert->totweight; i2++) { + gpw = &dvert->dw[i2]; + if (gpw->def_nr == def_nr) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + /* if index is greater, must be moved one back */ + if (gpw->def_nr > def_nr) { + gpw->def_nr--; + } + } + } + } + } + } } - /* set as active one */ - active->flag |= PL_PALETTE_ACTIVE; - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); + /* Remove the group */ + BLI_freelinkN(&ob->defbase, defgroup); } -/* delete the active gp-palette */ -void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) +/* add a new weight */ +MDeformWeight *BKE_gpencil_vgroup_add_point_weight(MDeformVert *dvert, int index, float weight) { - /* error checking */ - if (ELEM(NULL, gpd, palette)) { - return; + MDeformWeight *new_gpw = NULL; + MDeformWeight *tmp_gpw; + + /* need to verify if was used before to update */ + for (int i = 0; i < dvert->totweight; i++) { + tmp_gpw = &dvert->dw[i]; + if (tmp_gpw->def_nr == index) { + tmp_gpw->weight = weight; + return tmp_gpw; + } + } + + dvert->totweight++; + if (dvert->totweight == 1) { + dvert->dw = MEM_callocN(sizeof(MDeformWeight), "gp_weight"); + } + else { + dvert->dw = MEM_reallocN(dvert->dw, sizeof(MDeformWeight) * dvert->totweight); } + new_gpw = &dvert->dw[dvert->totweight - 1]; + new_gpw->def_nr = index; + new_gpw->weight = weight; - /* free colors */ - free_gpencil_colors(palette); - BLI_freelinkN(&gpd->palettes, palette); - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); + return new_gpw; } -/* Set all strokes to recalc the palette color */ -void BKE_gpencil_palette_change_strokes(bGPdata *gpd) +/* return the weight if use index or -1*/ +float BKE_gpencil_vgroup_use_index(MDeformVert *dvert, int index) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; + MDeformWeight *gpw; + for (int i = 0; i < dvert->totweight; i++) { + gpw = &dvert->dw[i]; + if (gpw->def_nr == index) { + return gpw->weight; + } + } + return -1.0f; +} - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - gps->flag |= GP_STROKE_RECALC_COLOR; - } +/* add a new weight */ +bool BKE_gpencil_vgroup_remove_point_weight(MDeformVert *dvert, int index) +{ + int e = 0; + + if (BKE_gpencil_vgroup_use_index(dvert, index) < 0.0f) { + return false; + } + + /* if the array get empty, exit */ + if (dvert->totweight == 1) { + dvert->totweight = 0; + MEM_SAFE_FREE(dvert->dw); + return true; + } + + /* realloc weights */ + MDeformWeight *tmp = MEM_dupallocN(dvert->dw); + MEM_SAFE_FREE(dvert->dw); + dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight - 1, "gp_weights"); + + for (int x = 0; x < dvert->totweight; x++) { + MDeformWeight *gpw = &tmp[e]; + MDeformWeight *final_gpw = &dvert->dw[e]; + if (gpw->def_nr != index) { + final_gpw->def_nr = gpw->def_nr; + final_gpw->weight = gpw->weight; + e++; } } + MEM_SAFE_FREE(tmp); + dvert->totweight--; + + return true; } -/* get the active gp-palettecolor for editing */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) +/* ************************************************** */ + +/** + * Apply smooth to stroke point + * \param gps Stroke to smooth + * \param i Point index + * \param inf Amount of smoothing to apply + */ +bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) { - bGPDpalettecolor *palcolor; + bGPDspoint *pt = &gps->points[i]; + // float pressure = 0.0f; + float sco[3] = { 0.0f }; - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first)) { - return NULL; + /* Do nothing if not enough points to smooth out */ + if (gps->totpoints <= 2) { + return false; + } + + /* Only affect endpoints by a fraction of the normal strength, + * to prevent the stroke from shrinking too much + */ + if ((i == 0) || (i == gps->totpoints - 1)) { + inf *= 0.1f; } - /* loop over colors until found (assume only one active) */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - if (palcolor->flag & PC_COLOR_ACTIVE) { - return palcolor; + /* Compute smoothed coordinate by taking the ones nearby */ + /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ + { + // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) + const int steps = 2; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; + + /* add the point itself */ + madd_v3_v3fl(sco, &pt->x, average_fac); + + /* n-steps before/after current point */ + // XXX: review how the endpoints are treated by this algorithm + // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = i - step; + int after = i + step; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + madd_v3_v3fl(sco, &pt1->x, average_fac); + madd_v3_v3fl(sco, &pt2->x, average_fac); + } } - /* no active color found */ - return NULL; + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v3_v3v3(&pt->x, &pt->x, sco, inf); + + return true; } -/* get the gp-palettecolor looking for name */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) + +/** + * Apply smooth for strength to stroke point */ +bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence) { - /* error checking */ - if (ELEM(NULL, palette, name)) { - return NULL; + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; } - return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info)); + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the strength + * at the distance of point b + */ + const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - influence) * ptb->strength + influence * optimal; + + return true; } -/* Change color name in all strokes */ -void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +/** + * Apply smooth for thickness to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; + bGPDspoint *ptb = &gps->points[point_index]; - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, oldname, newname)) - return; + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (STREQ(gps->colorname, oldname)) { - BLI_strncpy(gps->colorname, newname, sizeof(gps->colorname)); - } + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->pressure, pta->pressure, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->pressure = interpf(optimal, ptb->pressure, influence); + + return true; +} + +/** +* Apply smooth for UV rotation to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence) +{ + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence); + CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2); + + return true; +} + +/** + * Get range of selected frames in layer. + * Always the active frame is considered as selected, so if no more selected the range + * will be equal to the current active frame. + * \param gpl Layer + * \param r_initframe Number of first selected frame + * \param r_endframe Number of last selected frame + */ +void BKE_gpencil_get_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe) +{ + *r_initframe = gpl->actframe->framenum; + *r_endframe = gpl->actframe->framenum; + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (gpf->framenum < *r_initframe) { + *r_initframe = gpf->framenum; + } + if (gpf->framenum > *r_endframe) { + *r_endframe = gpf->framenum; } } } +} + +/** + * Get Falloff factor base on frame range + * \param gpf Frame + * \param actnum Number of active frame in layer + * \param f_init Number of first selected frame + * \param f_end Number of last selected frame + * \param cur_falloff Curve with falloff factors + */ +float BKE_gpencil_multiframe_falloff_calc(bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff) +{ + float fnum = 0.5f; /* default mid curve */ + float value; + + /* frames to the right of the active frame */ + if (gpf->framenum < actnum) { + fnum = (float)(gpf->framenum - f_init) / (actnum - f_init); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum); + } + /* frames to the left of the active frame */ + else if (gpf->framenum > actnum) { + fnum = (float)(gpf->framenum - actnum) / (f_end - actnum); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f); + } + else { + value = 1.0f; + } + return value; } -/* Delete all strokes of the color */ -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +/* remove strokes using a material */ +void BKE_gpencil_material_index_remove(bGPdata *gpd, int index) { - bGPDlayer *gpl; - bGPDframe *gpf; bGPDstroke *gps, *gpsn; - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, name)) - return; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - if (STREQ(gps->colorname, name)) { - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->mat_nr == index) { + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + } + else { + /* reassign strokes */ + if (gps->mat_nr > index) { + gps->mat_nr--; + } + } } } } - } - } -/* set the active gp-palettecolor */ -void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len) { - bGPDpalettecolor *palcolor; - - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first, active)) { - return; + const short remap_len_short = (short)remap_len; + +#define MAT_NR_REMAP(n) \ + if (n < remap_len_short) { \ + BLI_assert(n >= 0 && remap[n] < remap_len_short); \ + n = remap[n]; \ + } ((void)0) + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* reassign strokes */ + MAT_NR_REMAP(gps->mat_nr); + } + } } - /* loop over colors deactivating all */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_ACTIVE; - } +#undef MAT_NR_REMAP - /* set as active one */ - active->flag |= PC_COLOR_ACTIVE; } -/* delete the active gp-palettecolor */ -void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) +/* statistics functions */ +void BKE_gpencil_stats_update(bGPdata *gpd) { - /* error checking */ - if (ELEM(NULL, palette, palcolor)) { - return; + gpd->totlayer = 0; + gpd->totframe = 0; + gpd->totstroke = 0; + gpd->totpoint = 0; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpd->totlayer++; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + gpd->totframe++; + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + gpd->totstroke++; + gpd->totpoint += gps->totpoints; + } + } } - /* free */ - BLI_freelinkN(&palette->colors, palcolor); } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c new file mode 100644 index 00000000000..2ba738902a0 --- /dev/null +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -0,0 +1,679 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/gpencil_modifier.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_lattice.h" +#include "BKE_gpencil_modifier.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); +} + +/* Get points of stroke always flat to view not affected by camera view or view position */ +static void gpencil_stroke_project_2d(const bGPDspoint *points, int totpoints, vec2f *points2d) +{ + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; + + /* local X axis (p0 -> p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + const bGPDspoint *pt = &points[i]; + float loc[3]; + + /* Get local space using first point as origin */ + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + vec2f *point = &points2d[i]; + point->x = dot_v3v3(loc, locx); + point->y = dot_v3v3(loc, locy); + } + +} + +/* 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 + */ +static void gpencil_rdp_stroke(bGPDstroke *gps, vec2f *points2d, float epsilon) +{ + vec2f *old_points2d = points2d; + int totpoints = gps->totpoints; + char *marked = NULL; + char work; + + int start = 1; + int end = gps->totpoints - 2; + + 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; + float v1[2]; + /* divided to get more control */ + float max_dist = epsilon / 10.0f; + + /* find the next marked point */ + while (marked[le] == 0) { + le++; + } + + /* perpendicular vector to ls-le */ + v1[1] = old_points2d[le].x - old_points2d[ls].x; + v1[0] = old_points2d[ls].y - old_points2d[le].y; + + for (int i = ls + 1; i < le; i++) { + float mul; + float dist; + float v2[2]; + + v2[0] = old_points2d[i].x - old_points2d[ls].x; + v2[1] = old_points2d[i].y - old_points2d[ls].y; + + if (v2[0] == 0 && v2[1] == 0) { + continue; + } + + mul = (float)(v1[0] * v2[0] + v1[1] * v2[1]) / (float)(v2[0] * v2[0] + v2[1] * v2[1]); + + dist = mul * mul * (v2[0] * v2[0] + v2[1] * v2[1]); + + 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 */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = MEM_dupallocN(gps->dvert); + + /* resize gps */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((marked[i]) || (i == 0) || (i == totpoints - 1)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); + MEM_SAFE_FREE(marked); +} + +/* Simplify stroke using Ramer-Douglas-Peucker algorithm */ +void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float factor) +{ + /* first create temp data and convert points to 2D */ + vec2f *points2d = MEM_mallocN(sizeof(vec2f) * gps->totpoints, "GP Stroke temp 2d points"); + + gpencil_stroke_project_2d(gps->points, gps->totpoints, points2d); + + gpencil_rdp_stroke(gps, points2d, factor); + + MEM_SAFE_FREE(points2d); +} + +/* Simplify alternate vertex of stroke except extrems */ +void BKE_gpencil_simplify_fixed(bGPDstroke *gps) +{ + if (gps->totpoints < 5) { + return; + } + + /* save points */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *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); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + 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; +} + +/* apply stroke modifiers */ +void BKE_gpencil_stroke_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *UNUSED(gpf), bGPDstroke *gps, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + 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)) { + continue; + } + + if (mti && mti->deformStroke) { + mti->deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +/* apply stroke geometry modifiers */ +void BKE_gpencil_geometry_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + 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)) { + continue; + } + + if (mti->generateStrokes) { + mti->generateStrokes(md, depsgraph, ob, gpl, gpf); + } + } + } +} + +/* *************************************************** */ + +void BKE_gpencil_eval_geometry(Depsgraph *depsgraph, + bGPdata *gpd) +{ + DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd); + int ctime = (int)DEG_get_ctime(depsgraph); + + /* update active frame */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + } + + /* TODO: Move "derived_gpf" logic here from DRW_gpencil_populate_datablock()? + * This would be better than inventing our own logic for this stuff... + */ + + /* TODO: Move the following code to "BKE_gpencil_eval_done()" (marked as an exit node) + * later when there's more happening here. For now, let's just keep this in here to avoid + * needing to have one more node slowing down evaluation... + */ + 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 + */ + for (bGPDlayer *gpl = gpd_orig->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(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_StaticOverride_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 alredy 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)); +} + +/* helper function for per-instance positioning */ +void BKE_gpencil_instance_modifier_instance_tfm(InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]) +{ + float offset[3], rot[3], scale[3]; + int ri = mmd->rnd[0]; + float factor; + + offset[0] = mmd->offset[0] * elem_idx[0]; + offset[1] = mmd->offset[1] * elem_idx[1]; + offset[2] = mmd->offset[2] * elem_idx[2]; + + /* rotation */ + if (mmd->flag & GP_INSTANCE_RANDOM_ROT) { + factor = mmd->rnd_rot * mmd->rnd[ri]; + mul_v3_v3fl(rot, mmd->rot, factor); + add_v3_v3(rot, mmd->rot); + } + else { + copy_v3_v3(rot, mmd->rot); + } + + /* scale */ + if (mmd->flag & GP_INSTANCE_RANDOM_SIZE) { + factor = mmd->rnd_size * mmd->rnd[ri]; + mul_v3_v3fl(scale, mmd->scale, factor); + add_v3_v3(scale, mmd->scale); + } + else { + copy_v3_v3(scale, mmd->scale); + } + + /* advance random index */ + mmd->rnd[0]++; + if (mmd->rnd[0] > 19) { + mmd->rnd[0] = 1; + } + + /* calculate matrix */ + loc_eul_size_to_mat4(r_mat, offset, rot, scale); +} diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 1c2575dfa52..3a4bf53e22d 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -37,6 +37,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" @@ -45,7 +47,6 @@ #include "DNA_screen_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" -#include "DNA_brush_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -127,6 +128,9 @@ static void icon_free_data(int icon_id, Icon *icon) else if (icon->obj_type == ICON_DATA_PREVIEW) { ((PreviewImage *)(icon->obj))->icon_id = 0; } + else if (icon->obj_type == ICON_DATA_GPLAYER) { + ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; + } else if (icon->obj_type == ICON_DATA_GEOM) { ((struct Icon_Geom *)(icon->obj))->icon_id = 0; } @@ -598,6 +602,44 @@ int BKE_icon_id_ensure(struct ID *id) return icon_id_ensure_create_icon(id); } + +static int icon_gplayer_color_ensure_create_icon(bGPDlayer *gpl) +{ + BLI_assert(BLI_thread_is_main()); + + /* NOTE: The color previews for GP Layers don't really need + * to be "rendered" to image per se (as it will just be a plain + * colored rectangle), we need to define icon data here so that + * we can store a pointer to the layer data in icon->obj. + */ + Icon *icon = icon_create(gpl->runtime.icon_id, ICON_DATA_GPLAYER, gpl); + icon->flag = ICON_FLAG_MANAGED; + + return gpl->runtime.icon_id; +} + +int BKE_icon_gplayer_color_ensure(bGPDlayer *gpl) +{ + /* Never handle icons in non-main thread! */ + BLI_assert(BLI_thread_is_main()); + + if (!gpl || G.background) { + return 0; + } + + if (gpl->runtime.icon_id) + return gpl->runtime.icon_id; + + gpl->runtime.icon_id = get_next_free_id(); + + if (!gpl->runtime.icon_id) { + printf("%s: Internal error - not enough IDs\n", __func__); + return 0; + } + + return icon_gplayer_color_ensure_create_icon(gpl); +} + /** * Return icon id of given preview, or create new icon if not found. */ diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 351214ed72b..1d70d9db1e9 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1073,6 +1073,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[INDEX_ID_IP] = &(main->ipo); lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ lb[INDEX_ID_KE] = &(main->key); + lb[INDEX_ID_PAL] = &(main->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */ lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ lb[INDEX_ID_NT] = &(main->nodetree); lb[INDEX_ID_IM] = &(main->image); diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 93fdd3349bf..473dd787a69 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -417,7 +417,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call SEQ_END } - CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); for (CollectionObject *cob = scene->master_collection->gobject.first; cob; cob = cob->next) { CALLBACK_INVOKE(cob->ob, IDWALK_CB_USER); @@ -478,6 +477,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (toolsett->uvsculpt) { library_foreach_paint(&data, &toolsett->uvsculpt->paint); } + if (toolsett->gp_paint) { + library_foreach_paint(&data, &toolsett->gp_paint->paint); + } } if (scene->rigidbody_world) { @@ -641,6 +643,10 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (material->texpaintslot != NULL) { CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); } + if (material->gp_style != NULL) { + CALLBACK_INVOKE(material->gp_style->sima, IDWALK_CB_USER); + CALLBACK_INVOKE(material->gp_style->ima, IDWALK_CB_USER); + } break; } @@ -758,6 +764,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); + if (brush->gpencil_settings) { + CALLBACK_INVOKE(brush->gpencil_settings->material, IDWALK_CB_USER); + } library_foreach_mtex(&data, &brush->mtex); library_foreach_mtex(&data, &brush->mask_mtex); break; @@ -941,10 +950,15 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call case ID_GD: { bGPdata *gpencil = (bGPdata *) id; + /* materials */ + for (i = 0; i < gpencil->totcol; i++) { + CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); + } - for (bGPDlayer *gp_layer = gpencil->layers.first; gp_layer; gp_layer = gp_layer->next) { - CALLBACK_INVOKE(gp_layer->parent, IDWALK_CB_NOP); + for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; gplayer = gplayer->next) { + CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); } + break; } @@ -1072,7 +1086,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return true; #endif case ID_BR: - return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE); + return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE, ID_MA); case ID_PA: return ELEM(id_type_used, ID_OB, ID_GR, ID_TE); case ID_MC: @@ -1083,6 +1097,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_LP: return ELEM(id_type_used, ID_IM); + case ID_GD: + return ELEM(id_type_used, ID_MA); case ID_WS: return ELEM(id_type_used, ID_SCR, ID_SCE); case ID_IM: @@ -1091,7 +1107,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_SO: case ID_AR: case ID_AC: - case ID_GD: case ID_WM: case ID_PAL: case ID_PC: diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 28d75811185..03ec26c07d0 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -43,6 +43,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_customdata_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ID.h" #include "DNA_meta_types.h" #include "DNA_node_types.h" @@ -58,6 +59,7 @@ #include "BKE_animsys.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_library.h" @@ -103,10 +105,30 @@ void BKE_material_free(Material *ma) MEM_SAFE_FREE(ma->texpaintslot); + MEM_SAFE_FREE(ma->gp_style); + BKE_icon_id_delete((ID *)ma); BKE_previewimg_free(&ma->preview); } +void BKE_material_init_gpencil_settings(Material *ma) +{ + if ((ma) && (ma->gp_style == NULL)) { + ma->gp_style = MEM_callocN(sizeof(MaterialGPencilStyle), "Grease Pencil Material Settings"); + + MaterialGPencilStyle *gp_style = ma->gp_style; + /* set basic settings */ + gp_style->stroke_rgba[3] = 1.0f; + gp_style->pattern_gridsize = 0.1f; + gp_style->gradient_radius = 0.5f; + ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f); + ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f); + ARRAY_SET_ITEMS(gp_style->texture_scale, 1.0f, 1.0f); + gp_style->texture_opacity = 1.0f; + gp_style->texture_pixsize = 100.0f; + } +} + void BKE_material_init(Material *ma) { BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ma, id)); @@ -124,6 +146,7 @@ void BKE_material_init(Material *ma) ma->preview = NULL; ma->alpha_threshold = 0.5f; + } Material *BKE_material_add(Main *bmain, const char *name) @@ -137,6 +160,19 @@ Material *BKE_material_add(Main *bmain, const char *name) return ma; } +Material *BKE_material_add_gpencil(Main *bmain, const char *name) +{ + Material *ma; + + ma = BKE_material_add(bmain, name); + + /* grease pencil settings */ + BKE_material_init_gpencil_settings(ma); + + return ma; +} + + /** * Only copy internal data of Material ID from source to already allocated/initialized destination. * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. @@ -164,6 +200,10 @@ void BKE_material_copy_data(Main *bmain, Material *ma_dst, const Material *ma_sr ma_dst->texpaintslot = MEM_dupallocN(ma_src->texpaintslot); } + if (ma_src->gp_style != NULL) { + ma_dst->gp_style = MEM_dupallocN(ma_src->gp_style); + } + BLI_listbase_clear(&ma_dst->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -199,6 +239,7 @@ Material *BKE_material_localize(Material *ma) man->texpaintslot = NULL; man->preview = NULL; + /* man->gp_style = NULL; */ /* XXX: We probably don't want to clear here, or else we may get problems with COW later? */ BLI_listbase_clear(&man->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -218,6 +259,7 @@ Material ***give_matarar(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -231,6 +273,10 @@ Material ***give_matarar(Object *ob) mb = ob->data; return &(mb->mat); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->mat); + } return NULL; } @@ -239,6 +285,7 @@ short *give_totcolp(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -252,6 +299,10 @@ short *give_totcolp(Object *ob) mb = ob->data; return &(mb->totcol); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->totcol); + } return NULL; } @@ -286,6 +337,8 @@ short *give_totcolp_id(ID *id) return &(((Curve *)id)->totcol); case ID_MB: return &(((MetaBall *)id)->totcol); + case ID_GD: + return &(((bGPdata *)id)->totcol); default: break; } @@ -307,6 +360,9 @@ static void material_data_index_remove_id(ID *id, short index) case ID_MB: /* meta-elems don't have materials atm */ break; + case ID_GD: + BKE_gpencil_material_index_remove((bGPdata *)id, index); + break; default: break; } @@ -487,6 +543,21 @@ Material *give_current_material(Object *ob, short act) return ma; } +MaterialGPencilStyle *BKE_material_gpencil_settings_get(Object *ob, short act) +{ + Material *ma = give_current_material(ob, act); + if (ma != NULL) { + if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); + } + + return ma->gp_style; + } + else { + return NULL; + } +} + Material *give_node_material(Material *ma) { if (ma && ma->use_nodes && ma->nodetree) { @@ -727,6 +798,9 @@ void BKE_material_remap_object(Object *ob, const unsigned int *remap) else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_curve_material_remap(ob->data, remap, ob->totcol); } + if (ob->type == OB_GPENCIL) { + BKE_gpencil_material_remap(ob->data, remap, ob->totcol); + } else { /* add support for this object data! */ BLI_assert(matar == NULL); @@ -924,7 +998,7 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) } /* check indices from mesh */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_GPENCIL)) { material_data_index_remove_id((ID *)ob->data, actcol - 1); if (ob->runtime.curve_cache) { BKE_displist_free(&ob->runtime.curve_cache->disp); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 21b5bb89f19..c88de006eba 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -41,6 +41,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -53,6 +54,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_smoke_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -86,6 +88,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_fcurve.h" +#include "BKE_gpencil_modifier.h" #include "BKE_icons.h" #include "BKE_key.h" #include "BKE_lamp.h" @@ -110,12 +113,14 @@ #include "BKE_rigidbody.h" #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "BKE_shader_fx.h" #include "BKE_speaker.h" #include "BKE_softbody.h" #include "BKE_subsurf.h" #include "BKE_material.h" #include "BKE_camera.h" #include "BKE_image.h" +#include "BKE_gpencil.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -185,11 +190,15 @@ void BKE_object_free_curve_cache(Object *ob) void BKE_object_free_modifiers(Object *ob, const int flag) { ModifierData *md; + GpencilModifierData *gp_md; while ((md = BLI_pophead(&ob->modifiers))) { modifier_free_ex(md, flag); } + while ((gp_md = BLI_pophead(&ob->greasepencil_modifiers))) { + BKE_gpencil_modifier_free_ex(gp_md, flag); + } /* particle modifiers were freed, so free the particlesystems as well */ BKE_object_free_particlesystems(ob); @@ -200,6 +209,15 @@ void BKE_object_free_modifiers(Object *ob, const int flag) BKE_object_free_derived_caches(ob); } +void BKE_object_free_shaderfx(Object *ob, const int flag) +{ + ShaderFxData *fx; + + while ((fx = BLI_pophead(&ob->shader_fx))) { + BKE_shaderfx_free_ex(fx, flag); + } +} + void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) { /* reset functionality */ @@ -222,6 +240,29 @@ void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) } } +void BKE_object_modifier_gpencil_hook_reset(Object *ob, HookGpencilModifierData *hmd) +{ + if (hmd->object == NULL) { + return; + } + /* reset functionality */ + bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); + + if (hmd->subtarget[0] && pchan) { + float imat[4][4], mat[4][4]; + + /* calculate the world-space matrix for the pose-channel target first, then carry on as usual */ + mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); + + invert_m4_m4(imat, mat); + mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); + } + else { + invert_m4_m4(hmd->object->imat, hmd->object->obmat); + mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); + } +} + bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti; @@ -428,6 +469,7 @@ void BKE_object_free(Object *ob) /* BKE__free shall never touch to ID->us. Never ever. */ BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); + BKE_object_free_shaderfx(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); MEM_SAFE_FREE(ob->mat); MEM_SAFE_FREE(ob->matbits); @@ -653,6 +695,7 @@ static const char *get_obdata_defname(int type) case OB_ARMATURE: return DATA_("Armature"); case OB_SPEAKER: return DATA_("Speaker"); case OB_EMPTY: return DATA_("Empty"); + case OB_GPENCIL: return DATA_("GPencil"); default: printf("get_obdata_defname: Internal error, bad type: %d\n", type); return DATA_("Empty"); @@ -677,6 +720,7 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) case OB_ARMATURE: return BKE_armature_add(bmain, name); case OB_SPEAKER: return BKE_speaker_add(bmain, name); case OB_LIGHTPROBE:return BKE_lightprobe_add(bmain, name); + case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name); case OB_EMPTY: return NULL; default: printf("%s: Internal error, bad type: %d\n", __func__, type); @@ -810,7 +854,7 @@ Object *BKE_object_add( /** * Add a new object, using another one as a reference * - * /param ob_src object to use to determine the collections of the new object. + * \param ob_src object to use to determine the collections of the new object. */ Object *BKE_object_add_from( Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -828,6 +872,41 @@ Object *BKE_object_add_from( return ob; } +/** + * Add a new object, but assign the given datablock as the ob->data + * for the newly created object. + * + * \param data The datablock to assign as ob->data for the new object. + * This is assumed to be of the correct type. + * \param do_id_user If true, id_us_plus() will be called on data when + * assigning it to the object. + */ +Object *BKE_object_add_for_data( + Main *bmain, ViewLayer *view_layer, + int type, const char *name, ID *data, bool do_id_user) +{ + Object *ob; + Base *base; + LayerCollection *layer_collection; + + /* same as object_add_common, except we don't create new ob->data */ + ob = BKE_object_add_only_object(bmain, type, name); + ob->data = data; + if (do_id_user) id_us_plus(data); + + BKE_view_layer_base_deselect_all(view_layer); + DEG_id_tag_update_ex(bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select(view_layer, base); + + return ob; +} + + void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag) { SoftBody *sb = ob_src->soft; @@ -1153,6 +1232,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, const int flag) { ModifierData *md; + GpencilModifierData *gmd; + ShaderFxData *fx; /* Do not copy runtime data. */ BKE_object_runtime_reset(ob_dst); @@ -1179,6 +1260,24 @@ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, con BLI_addtail(&ob_dst->modifiers, nmd); } + BLI_listbase_clear(&ob_dst->greasepencil_modifiers); + + for (gmd = ob_src->greasepencil_modifiers.first; gmd; gmd = gmd->next) { + GpencilModifierData *nmd = BKE_gpencil_modifier_new(gmd->type); + BLI_strncpy(nmd->name, gmd->name, sizeof(nmd->name)); + BKE_gpencil_modifier_copyData_ex(gmd, nmd, flag_subdata); + BLI_addtail(&ob_dst->greasepencil_modifiers, nmd); + } + + BLI_listbase_clear(&ob_dst->shader_fx); + + for (fx = ob_src->shader_fx.first; fx; fx = fx->next) { + ShaderFxData *nfx = BKE_shaderfx_new(fx->type); + BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); + BKE_shaderfx_copyData_ex(fx, nfx, flag_subdata); + BLI_addtail(&ob_dst->shader_fx, nfx); + } + if (ob_src->pose) { copy_object_pose(ob_dst, ob_src, flag_subdata); /* backwards compat... non-armatures can get poses in older files? */ @@ -1210,6 +1309,10 @@ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, con BLI_listbase_clear((ListBase *)&ob_dst->drawdata); BLI_listbase_clear(&ob_dst->pc_ids); + /* grease pencil: clean derived data */ + if (ob_dst->type == OB_GPENCIL) + BKE_gpencil_free_derived_frames(ob_dst->data); + ob_dst->avs = ob_src->avs; ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); @@ -1469,6 +1572,11 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size) ob->empty_drawsize *= size; break; } + case OB_GPENCIL: + { + ob->empty_drawsize *= size; + break; + } case OB_FONT: { Curve *cu = ob->data; @@ -2452,7 +2560,7 @@ void BKE_object_minmax(Object *ob, float min_r[3], float max_r[3], const bool us float size[3]; copy_v3_v3(size, ob->size); - if (ob->type == OB_EMPTY) { + if ((ob->type == OB_EMPTY) || (ob->type == OB_GPENCIL)) { mul_v3_fl(size, ob->empty_drawsize); } @@ -3694,6 +3802,88 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) return false; } +bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) +{ + if (BKE_gpencil_modifier_dependsOnTime(md)) { + return true; + } + + /* Check whether modifier is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md->name); + + /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ + if (adt->action) { + for (fcu = (FCurve *)adt->action->curves.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + /* This here allows modifier properties to get driven and still update properly + * + */ + for (fcu = (FCurve *)adt->drivers.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + return false; +} + +bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) +{ + if (BKE_shaderfx_dependsOnTime(fx)) { + return true; + } + + /* Check whether effect is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx->name); + + /* action - check for F-Curves with paths containing string[' */ + if (adt->action) { + for (fcu = (FCurve *)adt->action->curves.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + /* This here allows properties to get driven and still update properly + * + */ + for (fcu = (FCurve *)adt->drivers.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + return false; +} + /* set "ignore cache" flag for all caches on this object */ static void object_cacheIgnoreClear(Object *ob, int state) { diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 5c9e53aaa56..a6b0e57e55c 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -53,6 +53,7 @@ #include "BKE_object.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_gpencil.h" /** \name Misc helpers * \{ */ @@ -402,12 +403,17 @@ static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) */ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) { - if (BKE_object_is_in_editmode_vgroup(ob)) - object_defgroup_remove_edit_mode(ob, defgroup); - else - object_defgroup_remove_object_mode(ob, defgroup); + if ((ob) && (ob->type == OB_GPENCIL)) { + BKE_gpencil_vgroup_remove(ob, defgroup); + } + else { + if (BKE_object_is_in_editmode_vgroup(ob)) + object_defgroup_remove_edit_mode(ob, defgroup); + else + object_defgroup_remove_object_mode(ob, defgroup); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } } /** diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 3e72de3909f..3641df26496 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -62,6 +62,7 @@ #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_scene.h" +#include "BKE_gpencil.h" #include "MEM_guardedalloc.h" @@ -324,6 +325,9 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph, case OB_MBALL: BKE_mball_batch_cache_dirty(ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; + case OB_GPENCIL: + BKE_gpencil_batch_cache_dirty(ob->data); + break; } } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 07aa21f44ff..cb26f7e9f3e 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -41,13 +41,19 @@ #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "DNA_space_types.h" +#include "DNA_gpencil_types.h" #include "DNA_workspace_types.h" #include "BLI_bitmap.h" +#include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utils.h" #include "BLI_math_vector.h" #include "BLI_listbase.h" +#include "BLT_translation.h" + +#include "BKE_animsys.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_deform.h" @@ -55,6 +61,7 @@ #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -151,6 +158,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->imapaint.paint; case ePaintSculptUV: return &ts->uvsculpt->paint; + case ePaintGpencil: + return &ts->gp_paint->paint; case ePaintInvalid: return NULL; default: @@ -176,6 +185,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->wpaint->paint; case OB_MODE_TEXTURE_PAINT: return &ts->imapaint.paint; + case OB_MODE_GPENCIL_PAINT: + return &ts->gp_paint->paint; case OB_MODE_EDIT: if (ts->use_uv_sculpt) return &ts->uvsculpt->paint; @@ -430,13 +441,11 @@ PaletteColor *BKE_palette_color_add(Palette *palette) return color; } - bool BKE_palette_is_empty(const struct Palette *palette) { return BLI_listbase_is_empty(&palette->colors); } - /* are we in vertex paint or weight pain face select mode? */ bool BKE_paint_select_face_test(Object *ob) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index b50dc37af81..7085b515ec1 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -173,6 +173,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->uvsculpt = MEM_dupallocN(ts->uvsculpt); BKE_paint_copy(&ts->uvsculpt->paint, &ts->uvsculpt->paint, flag); } + if (ts->gp_paint) { + ts->gp_paint = MEM_dupallocN(ts->gp_paint); + BKE_paint_copy(&ts->gp_paint->paint, &ts->gp_paint->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->imapaint.paintcursor = NULL; @@ -180,15 +184,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->particle.scene = NULL; ts->particle.object = NULL; - /* duplicate Grease Pencil Drawing Brushes */ - BLI_listbase_clear(&ts->gp_brushes); - for (bGPDbrush *brush = toolsettings->gp_brushes.first; brush; brush = brush->next) { - bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); - BLI_addtail(&ts->gp_brushes, newbrush); - } - /* duplicate Grease Pencil interpolation curve */ ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo); + /* duplicate Grease Pencil multiframe fallof */ + ts->gp_sculpt.cur_falloff = curvemapping_copy(ts->gp_sculpt.cur_falloff); return ts; } @@ -213,16 +212,20 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->uvsculpt->paint); MEM_freeN(toolsettings->uvsculpt); } + if (toolsettings->gp_paint) { + BKE_paint_free(&toolsettings->gp_paint->paint); + MEM_freeN(toolsettings->gp_paint); + } BKE_paint_free(&toolsettings->imapaint.paint); - /* free Grease Pencil Drawing Brushes */ - BKE_gpencil_free_brushes(&toolsettings->gp_brushes); - BLI_freelistN(&toolsettings->gp_brushes); - /* free Grease Pencil interpolation curve */ if (toolsettings->gp_interpolate.custom_ipo) { curvemapping_free(toolsettings->gp_interpolate.custom_ipo); } + /* free Grease Pencil multiframe falloff curve */ + if (toolsettings->gp_sculpt.cur_falloff) { + curvemapping_free(toolsettings->gp_sculpt.cur_falloff); + } MEM_freeN(toolsettings); } @@ -428,9 +431,9 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) } /* NOTE: part of SCE_COPY_LINK_DATA and SCE_COPY_FULL operations - * are done outside of blenkernel with ED_objects_single_users! */ + * are done outside of blenkernel with ED_object_single_users! */ - /* camera */ + /* camera */ if (ELEM(type, SCE_COPY_LINK_DATA, SCE_COPY_FULL)) { ID_NEW_REMAP(sce_copy->camera); } @@ -683,6 +686,19 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->imapaint.normal_angle = 80; sce->toolsettings->imapaint.seam_bleed = 2; + /* alloc grease pencil drawing brushes */ + sce->toolsettings->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + + /* grease pencil multiframe falloff curve */ + sce->toolsettings->gp_sculpt.cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + CurveMapping *gp_falloff_curve = sce->toolsettings->gp_sculpt.cur_falloff; + curvemapping_set_defaults(gp_falloff_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(gp_falloff_curve); + curvemap_reset(gp_falloff_curve->cm, + &gp_falloff_curve->clipr, + CURVE_PRESET_GAUSS, + CURVEMAP_SLOPE_POSITIVE); + sce->physics_settings.gravity[0] = 0.0f; sce->physics_settings.gravity[1] = 0.0f; sce->physics_settings.gravity[2] = -9.81f; @@ -760,46 +776,65 @@ void BKE_scene_init(Scene *sce) { GP_BrushEdit_Settings *gset = &sce->toolsettings->gp_sculpt; GP_EditBrush_Data *gp_brush; + float curcolor_add[3], curcolor_sub[3]; + ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f); + ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; gp_brush->size = 50; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST]; gp_brush->size = 50; gp_brush->strength = 0.3f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH]; gp_brush->size = 50; gp_brush->strength = 0.5f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMIZE]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); } /* GP Stroke Placement */ @@ -808,6 +843,10 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->gpencil_seq_align = GP_PROJECT_VIEWSPACE; sce->toolsettings->gpencil_ima_align = GP_PROJECT_VIEWSPACE; + /* Annotations */ + sce->toolsettings->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + sce->toolsettings->annotate_thickness = 3; + sce->orientation_index_custom = -1; /* Master Collection */ diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c new file mode 100644 index 00000000000..c028c2184fd --- /dev/null +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -0,0 +1,245 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/shader_fx.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_shader_fx.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "FX_shader_types.h" + +static ShaderFxTypeInfo *shader_fx_types[NUM_SHADER_FX_TYPES] = { NULL }; + +/* *************************************************** */ +/* Methods - Evaluation Loops, etc. */ + +/* check if exist grease pencil effects */ +bool BKE_shaderfx_has_gpencil(Object *ob) +{ + ShaderFxData *fx; + for (fx = ob->shader_fx.first; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + if (fxi->type == eShaderFxType_GpencilType) { + return true; + } + } + return false; +} + +void BKE_shaderfx_init(void) +{ + /* Initialize shaders */ + shaderfx_type_init(shader_fx_types); /* FX_shader_util.c */ +} + +ShaderFxData *BKE_shaderfx_new(int type) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(type); + ShaderFxData *fx = MEM_callocN(fxi->struct_size, fxi->struct_name); + + /* note, this name must be made unique later */ + BLI_strncpy(fx->name, DATA_(fxi->name), sizeof(fx->name)); + + fx->type = type; + fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render | eShaderFxMode_Expanded; + fx->flag = eShaderFxFlag_StaticOverride_Local; + + if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) + fx->mode |= eShaderFxMode_Editmode; + + if (fxi->initData) fxi->initData(fx); + + return fx; +} + +static void shaderfx_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_shaderfx_free_ex(ShaderFxData *fx, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(fx, NULL, shaderfx_free_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(fx, NULL, (ShaderFxObjectWalkFunc)shaderfx_free_data_id_us_cb, NULL); + } + } + + if (fxi->freeData) fxi->freeData(fx); + if (fx->error) MEM_freeN(fx->error); + + MEM_freeN(fx); +} + +void BKE_shaderfx_free(ShaderFxData *fx) +{ + BKE_shaderfx_free_ex(fx, 0); +} + +/* check unique name */ +bool BKE_shaderfx_unique_name(ListBase *shaders, ShaderFxData *fx) +{ + if (shaders && fx) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + return BLI_uniquename(shaders, fx, DATA_(fxi->name), '.', offsetof(ShaderFxData, name), sizeof(fx->name)); + } + return false; +} + +bool BKE_shaderfx_dependsOnTime(ShaderFxData *fx) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + return fxi->dependsOnTime && fxi->dependsOnTime(fx); +} + +const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type) +{ + /* type unsigned, no need to check < 0 */ + if (type < NUM_SHADER_FX_TYPES && shader_fx_types[type]->name[0] != '\0') { + return shader_fx_types[type]; + } + else { + return NULL; + } +} + +void BKE_shaderfx_copyData_generic(const ShaderFxData *fx_src, ShaderFxData *fx_dst) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx_src->type); + + /* fx_dst may have alredy be fully initialized with some extra allocated data, + * we need to free it now to avoid memleak. */ + if (fxi->freeData) { + fxi->freeData(fx_dst); + } + + const size_t data_size = sizeof(ShaderFxData); + const char *fx_src_data = ((const char *)fx_src) + data_size; + char *fx_dst_data = ((char *)fx_dst) + data_size; + BLI_assert(data_size <= (size_t)fxi->struct_size); + memcpy(fx_dst_data, fx_src_data, (size_t)fxi->struct_size - data_size); +} + +static void shaderfx_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_shaderfx_copyData_ex(ShaderFxData *fx, ShaderFxData *target, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + target->mode = fx->mode; + target->flag = fx->flag; + + if (fxi->copyData) { + fxi->copyData(fx, target); + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(target, NULL, shaderfx_copy_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(target, NULL, (ShaderFxObjectWalkFunc)shaderfx_copy_data_id_us_cb, NULL); + } + } +} + +void BKE_shaderfx_copyData(ShaderFxData *fx, ShaderFxData *target) +{ + BKE_shaderfx_copyData_ex(fx, target, 0); +} + +ShaderFxData *BKE_shaderfx_findByType(Object *ob, ShaderFxType type) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) + if (fx->type == type) + break; + + return fx; +} + +void BKE_shaderfx_foreachIDLink(Object *ob, ShaderFxIDWalkFunc walk, void *userData) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if (fxi->foreachIDLink) fxi->foreachIDLink(fx, ob, walk, userData); + else if (fxi->foreachObjectLink) { + /* each Object can masquerade as an ID, so this should be OK */ + ShaderFxObjectWalkFunc fp = (ShaderFxObjectWalkFunc)walk; + fxi->foreachObjectLink(fx, ob, fp, userData); + } + } +} + +ShaderFxData *BKE_shaderfx_findByName(Object *ob, const char *name) +{ + return BLI_findstring(&(ob->shader_fx), name, offsetof(ShaderFxData, name)); +} -- cgit v1.2.3