From 29f3af95272590d26f610ae828b2eeee89c82a00 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 9 Mar 2020 16:27:24 +0100 Subject: GPencil: Refactor of Draw Engine, Vertex Paint and all internal functions This commit is a full refactor of the grease pencil modules including Draw Engine, Modifiers, VFX, depsgraph update, improvements in operators and conversion of Sculpt and Weight paint tools to real brushes. Also, a huge code cleanup has been done at all levels. Thanks to @fclem for his work and yo @pepeland and @mendio for the testing and help in the development. Differential Revision: https://developer.blender.org/D6293 --- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_brush.h | 13 +- source/blender/blenkernel/BKE_context.h | 3 +- source/blender/blenkernel/BKE_gpencil.h | 159 +- source/blender/blenkernel/BKE_gpencil_modifier.h | 39 +- source/blender/blenkernel/BKE_paint.h | 17 +- source/blender/blenkernel/intern/brush.c | 1077 ++++++--- source/blender/blenkernel/intern/context.c | 27 +- source/blender/blenkernel/intern/gpencil.c | 1061 +++++++-- .../blender/blenkernel/intern/gpencil_modifier.c | 358 ++- source/blender/blenkernel/intern/lib_query.c | 9 + source/blender/blenkernel/intern/material.c | 5 +- source/blender/blenkernel/intern/object.c | 15 +- source/blender/blenkernel/intern/object_update.c | 11 +- source/blender/blenkernel/intern/paint.c | 270 +++ source/blender/blenkernel/intern/paint_toolslots.c | 9 + source/blender/blenkernel/intern/scene.c | 90 +- source/blender/blenlib/intern/BLI_memblock.c | 11 +- source/blender/blenloader/intern/readfile.c | 223 +- source/blender/blenloader/intern/versioning_270.c | 97 +- source/blender/blenloader/intern/versioning_280.c | 447 +++- .../blenloader/intern/versioning_defaults.c | 55 +- source/blender/blenloader/intern/writefile.c | 61 +- .../depsgraph/intern/builder/deg_builder_nodes.cc | 2 +- .../intern/builder/deg_builder_relations.cc | 9 + .../intern/eval/deg_eval_copy_on_write.cc | 4 + source/blender/draw/CMakeLists.txt | 50 +- source/blender/draw/DRW_engine.h | 1 - .../draw/engines/gpencil/gpencil_antialiasing.c | 169 ++ .../draw/engines/gpencil/gpencil_cache_utils.c | 568 ++--- .../draw/engines/gpencil/gpencil_draw_cache_impl.c | 1021 --------- .../draw/engines/gpencil/gpencil_draw_data.c | 502 +++++ .../draw/engines/gpencil/gpencil_draw_utils.c | 2071 ----------------- .../blender/draw/engines/gpencil/gpencil_engine.c | 1725 +++++++------- .../blender/draw/engines/gpencil/gpencil_engine.h | 792 +++---- .../blender/draw/engines/gpencil/gpencil_render.c | 365 +-- .../blender/draw/engines/gpencil/gpencil_shader.c | 311 +++ .../draw/engines/gpencil/gpencil_shader_fx.c | 1415 +++++------- .../gpencil/shaders/fx/gpencil_fx_blur_frag.glsl | 85 - .../shaders/fx/gpencil_fx_colorize_frag.glsl | 82 - .../gpencil/shaders/fx/gpencil_fx_flip_frag.glsl | 37 - .../shaders/fx/gpencil_fx_glow_prepare_frag.glsl | 68 - .../shaders/fx/gpencil_fx_glow_resolve_frag.glsl | 46 - .../gpencil/shaders/fx/gpencil_fx_light_frag.glsl | 70 - .../gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl | 51 - .../shaders/fx/gpencil_fx_rim_prepare_frag.glsl | 65 - .../shaders/fx/gpencil_fx_rim_resolve_frag.glsl | 98 - .../shaders/fx/gpencil_fx_shadow_prepare_frag.glsl | 98 - .../shaders/fx/gpencil_fx_shadow_resolve_frag.glsl | 32 - .../gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl | 74 - .../gpencil/shaders/fx/gpencil_fx_wave_frag.glsl | 44 - .../gpencil/shaders/gpencil_antialiasing_frag.glsl | 64 + .../gpencil/shaders/gpencil_antialiasing_vert.glsl | 21 + .../gpencil/shaders/gpencil_background_frag.glsl | 12 - .../gpencil/shaders/gpencil_blend_frag.glsl | 157 -- .../gpencil/shaders/gpencil_common_lib.glsl | 593 +++++ .../gpencil/shaders/gpencil_depth_merge_frag.glsl | 17 + .../gpencil/shaders/gpencil_depth_merge_vert.glsl | 14 + .../gpencil/shaders/gpencil_edit_point_frag.glsl | 17 - .../gpencil/shaders/gpencil_edit_point_geom.glsl | 53 - .../gpencil/shaders/gpencil_edit_point_vert.glsl | 19 - .../engines/gpencil/shaders/gpencil_fill_frag.glsl | 234 -- .../engines/gpencil/shaders/gpencil_fill_vert.glsl | 16 - .../draw/engines/gpencil/shaders/gpencil_frag.glsl | 126 ++ .../gpencil/shaders/gpencil_layer_blend_frag.glsl | 31 + .../gpencil/shaders/gpencil_mask_invert_frag.glsl | 11 + .../gpencil/shaders/gpencil_paper_frag.glsl | 9 - .../gpencil/shaders/gpencil_point_frag.glsl | 126 -- .../gpencil/shaders/gpencil_point_geom.glsl | 142 -- .../gpencil/shaders/gpencil_point_vert.glsl | 66 - .../gpencil/shaders/gpencil_simple_mix_frag.glsl | 15 - .../gpencil/shaders/gpencil_stroke_frag.glsl | 110 - .../gpencil/shaders/gpencil_stroke_geom.glsl | 264 --- .../gpencil/shaders/gpencil_stroke_vert.glsl | 63 - .../draw/engines/gpencil/shaders/gpencil_vert.glsl | 5 + .../engines/gpencil/shaders/gpencil_vfx_frag.glsl | 354 +++ .../gpencil/shaders/gpencil_zdepth_mix_frag.glsl | 76 - .../blender/draw/engines/overlay/overlay_engine.c | 24 +- .../blender/draw/engines/overlay/overlay_extra.c | 85 +- .../blender/draw/engines/overlay/overlay_gpencil.c | 391 ++++ .../draw/engines/overlay/overlay_motion_path.c | 4 +- .../blender/draw/engines/overlay/overlay_outline.c | 158 +- .../blender/draw/engines/overlay/overlay_private.h | 19 +- .../blender/draw/engines/overlay/overlay_shader.c | 103 + .../draw/engines/overlay/overlay_wireframe.c | 6 +- .../overlay/shaders/edit_gpencil_canvas_vert.glsl | 35 + .../overlay/shaders/edit_gpencil_guide_vert.glsl | 14 + .../engines/overlay/shaders/edit_gpencil_vert.glsl | 103 + .../overlay/shaders/outline_prepass_frag.glsl | 38 +- .../overlay/shaders/outline_prepass_vert.glsl | 14 +- .../engines/overlay/shaders/wireframe_vert.glsl | 6 +- .../draw/engines/workbench/workbench_render.c | 60 +- source/blender/draw/intern/DRW_render.h | 22 +- source/blender/draw/intern/draw_cache.c | 27 + source/blender/draw/intern/draw_cache.h | 17 + .../blender/draw/intern/draw_cache_impl_gpencil.c | 745 +++++++ source/blender/draw/intern/draw_common.c | 2 + source/blender/draw/intern/draw_common.h | 2 + source/blender/draw/intern/draw_manager.c | 34 +- source/blender/draw/intern/draw_manager.h | 18 +- source/blender/draw/intern/draw_manager_data.c | 65 +- source/blender/draw/intern/draw_manager_exec.c | 91 +- .../draw/intern/shaders/common_globals_lib.glsl | 2 + .../draw/intern/shaders/common_smaa_lib.glsl | 11 +- .../draw/intern/shaders/common_view_lib.glsl | 13 +- .../editors/animation/anim_channels_defines.c | 10 +- .../blender/editors/animation/anim_channels_edit.c | 2 +- source/blender/editors/armature/armature_naming.c | 2 +- source/blender/editors/gpencil/CMakeLists.txt | 6 +- source/blender/editors/gpencil/annotate_draw.c | 37 +- source/blender/editors/gpencil/annotate_paint.c | 21 +- source/blender/editors/gpencil/drawgpencil.c | 304 +-- .../blender/editors/gpencil/editaction_gpencil.c | 58 +- .../blender/editors/gpencil/gpencil_add_monkey.c | 93 +- .../blender/editors/gpencil/gpencil_add_stroke.c | 8 +- source/blender/editors/gpencil/gpencil_armature.c | 4 +- source/blender/editors/gpencil/gpencil_brush.c | 2343 -------------------- source/blender/editors/gpencil/gpencil_convert.c | 104 +- source/blender/editors/gpencil/gpencil_data.c | 571 ++++- source/blender/editors/gpencil/gpencil_edit.c | 493 ++-- source/blender/editors/gpencil/gpencil_fill.c | 170 +- source/blender/editors/gpencil/gpencil_intern.h | 68 +- .../blender/editors/gpencil/gpencil_interpolate.c | 145 +- source/blender/editors/gpencil/gpencil_merge.c | 126 +- source/blender/editors/gpencil/gpencil_ops.c | 316 ++- .../editors/gpencil/gpencil_ops_versioning.c | 17 +- source/blender/editors/gpencil/gpencil_paint.c | 1374 ++++-------- source/blender/editors/gpencil/gpencil_primitive.c | 119 +- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 2150 ++++++++++++++++++ source/blender/editors/gpencil/gpencil_select.c | 202 +- source/blender/editors/gpencil/gpencil_undo.c | 4 +- source/blender/editors/gpencil/gpencil_utils.c | 483 ++-- source/blender/editors/gpencil/gpencil_uv.c | 587 +++++ .../blender/editors/gpencil/gpencil_vertex_ops.c | 899 ++++++++ .../blender/editors/gpencil/gpencil_vertex_paint.c | 1414 ++++++++++++ .../blender/editors/gpencil/gpencil_weight_paint.c | 901 ++++++++ source/blender/editors/include/ED_gpencil.h | 56 +- source/blender/editors/interface/interface.c | 6 +- .../interface/interface_eyedropper_gpencil_color.c | 114 +- .../blender/editors/interface/interface_handlers.c | 41 +- source/blender/editors/interface/interface_icons.c | 53 +- .../blender/editors/interface/interface_layout.c | 2 +- .../editors/interface/interface_templates.c | 48 + source/blender/editors/object/object_add.c | 4 +- source/blender/editors/object/object_edit.c | 3 +- .../editors/object/object_gpencil_modifier.c | 7 +- source/blender/editors/object/object_modes.c | 5 +- source/blender/editors/object/object_transform.c | 10 +- source/blender/editors/screen/area.c | 72 + source/blender/editors/screen/screen_context.c | 10 +- source/blender/editors/sculpt_paint/paint_ops.c | 322 +++ source/blender/editors/space_action/action_edit.c | 2 +- .../blender/editors/space_action/action_select.c | 4 +- .../blender/editors/space_outliner/outliner_draw.c | 5 +- .../editors/space_outliner/outliner_select.c | 2 +- source/blender/editors/space_view3d/space_view3d.c | 6 + .../editors/space_view3d/view3d_gizmo_ruler.c | 13 +- .../blender/editors/space_view3d/view3d_select.c | 3 +- .../blender/editors/transform/transform_convert.c | 10 +- .../editors/transform/transform_convert_gpencil.c | 14 +- .../blender/editors/transform/transform_generics.c | 7 +- .../blender/editors/transform/transform_gizmo_3d.c | 6 +- source/blender/editors/undo/ed_undo.c | 3 +- source/blender/gpencil_modifiers/CMakeLists.txt | 1 + .../gpencil_modifiers/MOD_gpencil_modifiertypes.h | 1 + .../gpencil_modifiers/intern/MOD_gpencil_util.c | 42 +- .../gpencil_modifiers/intern/MOD_gpencil_util.h | 7 - .../gpencil_modifiers/intern/MOD_gpencilarmature.c | 9 +- .../gpencil_modifiers/intern/MOD_gpencilarray.c | 304 ++- .../gpencil_modifiers/intern/MOD_gpencilbuild.c | 36 +- .../gpencil_modifiers/intern/MOD_gpencilcolor.c | 120 +- .../gpencil_modifiers/intern/MOD_gpencilhook.c | 9 +- .../gpencil_modifiers/intern/MOD_gpencillattice.c | 9 +- .../gpencil_modifiers/intern/MOD_gpencilmirror.c | 30 +- .../gpencil_modifiers/intern/MOD_gpencilmultiply.c | 148 +- .../gpencil_modifiers/intern/MOD_gpencilnoise.c | 227 +- .../gpencil_modifiers/intern/MOD_gpenciloffset.c | 9 +- .../gpencil_modifiers/intern/MOD_gpencilopacity.c | 134 +- .../gpencil_modifiers/intern/MOD_gpencilsimplify.c | 15 +- .../gpencil_modifiers/intern/MOD_gpencilsmooth.c | 52 +- .../gpencil_modifiers/intern/MOD_gpencilsubdiv.c | 11 +- .../gpencil_modifiers/intern/MOD_gpencilthick.c | 79 +- .../gpencil_modifiers/intern/MOD_gpenciltint.c | 116 +- .../intern/MOD_gpencilvertexcolor.c | 314 +++ source/blender/gpu/GPU_framebuffer.h | 2 + source/blender/gpu/GPU_shader_interface.h | 1 + source/blender/gpu/GPU_texture.h | 1 + source/blender/gpu/GPU_vertex_format.h | 2 + source/blender/gpu/intern/gpu_framebuffer.c | 15 + source/blender/gpu/intern/gpu_shader_interface.c | 1 + source/blender/gpu/intern/gpu_vertex_format.c | 41 + source/blender/imbuf/IMB_imbuf.h | 3 + source/blender/imbuf/intern/imageprocess.c | 17 + source/blender/makesdna/DNA_brush_types.h | 171 +- .../blender/makesdna/DNA_gpencil_modifier_types.h | 106 +- source/blender/makesdna/DNA_gpencil_types.h | 165 +- source/blender/makesdna/DNA_material_types.h | 65 +- source/blender/makesdna/DNA_object_enums.h | 6 +- source/blender/makesdna/DNA_object_types.h | 22 +- source/blender/makesdna/DNA_scene_types.h | 146 +- source/blender/makesdna/DNA_shader_fx_types.h | 37 +- source/blender/makesdna/DNA_userdef_types.h | 5 +- source/blender/makesdna/DNA_view3d_types.h | 9 +- source/blender/makesdna/intern/dna_rename_defs.h | 4 + source/blender/makesrna/RNA_access.h | 1 + source/blender/makesrna/RNA_enum_types.h | 5 +- source/blender/makesrna/intern/rna_brush.c | 266 ++- source/blender/makesrna/intern/rna_context.c | 1 + source/blender/makesrna/intern/rna_gpencil.c | 416 +++- .../blender/makesrna/intern/rna_gpencil_modifier.c | 493 ++-- source/blender/makesrna/intern/rna_material.c | 146 +- source/blender/makesrna/intern/rna_object.c | 29 +- source/blender/makesrna/intern/rna_scene.c | 136 +- source/blender/makesrna/intern/rna_sculpt_paint.c | 313 +-- source/blender/makesrna/intern/rna_shader_fx.c | 129 +- source/blender/makesrna/intern/rna_space.c | 41 +- source/blender/makesrna/intern/rna_ui_api.c | 6 +- source/blender/makesrna/intern/rna_userdef.c | 19 - source/blender/shader_fx/CMakeLists.txt | 1 - source/blender/shader_fx/FX_shader_types.h | 1 - source/blender/shader_fx/intern/FX_shader_blur.c | 9 +- source/blender/shader_fx/intern/FX_shader_flip.c | 2 +- source/blender/shader_fx/intern/FX_shader_glow.c | 8 +- source/blender/shader_fx/intern/FX_shader_pixel.c | 2 +- source/blender/shader_fx/intern/FX_shader_rim.c | 2 +- source/blender/shader_fx/intern/FX_shader_shadow.c | 2 +- source/blender/shader_fx/intern/FX_shader_util.c | 1 - source/blender/shader_fx/intern/FX_shader_wave.c | 2 +- .../blender/windowmanager/intern/wm_keymap_utils.c | 3 + .../blender/windowmanager/intern/wm_toolsystem.c | 58 +- 230 files changed, 21192 insertions(+), 15889 deletions(-) create mode 100644 source/blender/draw/engines/gpencil/gpencil_antialiasing.c delete mode 100644 source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_draw_data.c delete mode 100644 source/blender/draw/engines/gpencil/gpencil_draw_utils.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_shader.c delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl delete mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl create mode 100644 source/blender/draw/engines/overlay/overlay_gpencil.c create mode 100644 source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl create mode 100644 source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl create mode 100644 source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl create mode 100644 source/blender/draw/intern/draw_cache_impl_gpencil.c delete mode 100644 source/blender/editors/gpencil/gpencil_brush.c create mode 100644 source/blender/editors/gpencil/gpencil_sculpt_paint.c create mode 100644 source/blender/editors/gpencil/gpencil_uv.c create mode 100644 source/blender/editors/gpencil/gpencil_vertex_ops.c create mode 100644 source/blender/editors/gpencil/gpencil_vertex_paint.c create mode 100644 source/blender/editors/gpencil/gpencil_weight_paint.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilvertexcolor.c (limited to 'source/blender') diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 9e4453d21fe..233c385c247 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ * \note Use #STRINGIFY() rather than defining with quotes. */ #define BLENDER_VERSION 283 -#define BLENDER_SUBVERSION 6 +#define BLENDER_SUBVERSION 7 /** Several breakages with 280, e.g. collections vs layers. */ #define BLENDER_MINVERSION 280 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 6644a3f0231..a97263a6523 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -46,13 +46,22 @@ void BKE_brush_system_exit(void); /* datablock functions */ 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_add_gpencil(struct Main *bmain, + struct ToolSettings *ts, + const char *name, + eObjectMode mode); +bool BKE_brush_delete(struct Main *bmain, struct Brush *brush); void BKE_brush_init_gpencil_settings(struct Brush *brush); struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode); struct Brush *BKE_brush_copy(struct Main *bmain, const struct Brush *brush); void BKE_brush_sculpt_reset(struct Brush *brush); -void BKE_brush_gpencil_presets(struct Main *bmain, struct ToolSettings *ts); + +void BKE_brush_gpencil_paint_presets(struct Main *bmain, struct ToolSettings *ts); +void BKE_brush_gpencil_vertex_presets(struct Main *bmain, struct ToolSettings *ts); +void BKE_brush_gpencil_sculpt_presets(struct Main *bmain, struct ToolSettings *ts); +void BKE_brush_gpencil_weight_presets(struct Main *bmain, struct ToolSettings *ts); +void BKE_gpencil_brush_preset_set(struct Main *bmain, struct Brush *brush, const short type); /* 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 88a27b67963..9e2a124491c 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -115,8 +115,9 @@ typedef enum eContextObjectMode { CTX_MODE_EDIT_GPENCIL, CTX_MODE_SCULPT_GPENCIL, CTX_MODE_WEIGHT_GPENCIL, + CTX_MODE_VERTEX_GPENCIL, } eContextObjectMode; -#define CTX_MODE_NUM (CTX_MODE_WEIGHT_GPENCIL + 1) +#define CTX_MODE_NUM (CTX_MODE_VERTEX_GPENCIL + 1) /* Context */ diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 1a186d2d682..9d382775df7 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -32,19 +32,22 @@ struct BoundBox; struct Brush; struct CurveMapping; struct Depsgraph; +struct GHash; struct ListBase; struct Main; struct Material; struct Object; struct Scene; +struct SpaceImage; struct ToolSettings; struct bDeformGroup; struct bGPDframe; struct bGPDlayer; +struct bGPDlayer_Mask; struct bGPDspoint; struct bGPDstroke; struct bGPdata; - +struct MaterialGPencilStyle; struct MDeformVert; #define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) @@ -54,18 +57,33 @@ struct MDeformVert; #define GPENCIL_SIMPLIFY_FILL(scene, playing) \ ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL))) -#define GPENCIL_SIMPLIFY_MODIF(scene, playing) \ - ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ - (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER))) +#define GPENCIL_SIMPLIFY_MODIF(scene) \ + ((GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER))) #define GPENCIL_SIMPLIFY_FX(scene, playing) \ ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX))) -#define GPENCIL_SIMPLIFY_BLEND(scene, playing) \ - ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ - (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_BLEND))) -#define GPENCIL_SIMPLIFY_TINT(scene, playing) \ - ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ - (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT))) +#define GPENCIL_SIMPLIFY_TINT(scene) \ + ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT)) +#define GPENCIL_SIMPLIFY_AA(scene) \ + ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA)) + +/* Vertex Color macros. */ +#define GPENCIL_USE_VERTEX_COLOR(toolsettings) \ + ((toolsettings->gp_paint->mode == GPPAINT_FLAG_USE_VERTEXCOLOR)) +#define GPENCIL_USE_VERTEX_COLOR_STROKE(toolsettings, brush) \ + ((GPENCIL_USE_VERTEX_COLOR(toolsettings) && \ + ((brush->gpencil_settings->vertex_mode == GPPAINT_MODE_STROKE) || \ + (brush->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH)))) +#define GPENCIL_USE_VERTEX_COLOR_FILL(toolsettings, brush) \ + ((GPENCIL_USE_VERTEX_COLOR(toolsettings) && \ + ((brush->gpencil_settings->vertex_mode == GPPAINT_MODE_FILL) || \ + (brush->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH)))) +#define GPENCIL_TINT_VERTEX_COLOR_STROKE(brush) \ + ((brush->gpencil_settings->vertex_mode == GPPAINT_MODE_STROKE) || \ + (brush->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH)) +#define GPENCIL_TINT_VERTEX_COLOR_FILL(brush) \ + ((brush->gpencil_settings->vertex_mode == GPPAINT_MODE_FILL) || \ + (brush->gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH)) /* ------------ Grease-Pencil API ------------------ */ @@ -75,8 +93,9 @@ 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); -bool BKE_gpencil_free_frame_runtime_data(struct bGPDframe *gpf_eval); void BKE_gpencil_free(struct bGPdata *gpd, bool free_all); +void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval); +void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl); void BKE_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd); void BKE_gpencil_batch_cache_free(struct bGPdata *gpd); @@ -91,10 +110,11 @@ 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); +struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, const bool dup_points); void BKE_gpencil_copy_data(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); @@ -109,6 +129,11 @@ bool BKE_gpencil_material_index_used(struct bGPdata *gpd, int index); void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len); +bool BKE_gpencil_merge_materials_table_get(struct Object *ob, + const float hue_threshold, + const float sat_threshold, + const float val_threshold, + struct GHash *r_mat_table); /* statistics functions */ void BKE_gpencil_stats_update(struct bGPdata *gpd); @@ -124,12 +149,11 @@ void BKE_gpencil_stroke_add_points(struct bGPDstroke *gps, const int totpoints, const float mat[4][4]); -struct bGPDstroke *BKE_gpencil_add_stroke(struct bGPDframe *gpf, - int mat_idx, - int totpoints, - short thickness); +struct bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness); +struct bGPDstroke *BKE_gpencil_stroke_add( + struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head); -struct bGPDstroke *BKE_gpencil_add_stroke_existing_style(struct bGPDframe *gpf, +struct bGPDstroke *BKE_gpencil_stroke_add_existing_style(struct bGPDframe *gpf, struct bGPDstroke *existing, int mat_idx, int totpoints, @@ -139,7 +163,7 @@ struct bGPDstroke *BKE_gpencil_add_stroke_existing_style(struct bGPDframe *gpf, #define GPENCIL_ALPHA_OPACITY_THRESH 0.001f #define GPENCIL_STRENGTH_MIN 0.003f -bool gpencil_layer_is_editable(const struct bGPDlayer *gpl); +bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl); /* How gpencil_layer_getframe() should behave when there * is no existing GP-Frame on the frame requested. @@ -154,17 +178,25 @@ typedef enum eGP_GetFrame_Mode { GP_GETFRAME_ADD_COPY = 2, } eGP_GetFrame_Mode; -struct bGPDframe *BKE_gpencil_layer_getframe(struct bGPDlayer *gpl, - int cframe, - eGP_GetFrame_Mode addnew); -struct bGPDframe *BKE_gpencil_layer_find_frame(struct bGPDlayer *gpl, int cframe); -bool BKE_gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); +struct bGPDframe *BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, + int cframe, + eGP_GetFrame_Mode addnew); +struct bGPDframe *BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe); +bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf); -struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd); -void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); +struct bGPDlayer *BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name); +struct bGPDlayer *BKE_gpencil_layer_active_get(struct bGPdata *gpd); +void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active); void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); void BKE_gpencil_layer_autolock_set(struct bGPdata *gpd, const bool unlock); +struct bGPDlayer_Mask *BKE_gpencil_layer_mask_add(struct bGPDlayer *gpl, const char *name); +void BKE_gpencil_layer_mask_remove(struct bGPDlayer *gpl, struct bGPDlayer_Mask *mask); +void BKE_gpencil_layer_mask_remove_ref(struct bGPdata *gpd, const char *name); +struct bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(struct bGPDlayer *gpl, const char *name); +void BKE_gpencil_layer_mask_sort(struct bGPdata *gpd, struct bGPDlayer *gpl); +void BKE_gpencil_layer_mask_sort_all(struct bGPdata *gpd); + /* Brush */ struct Material *BKE_gpencil_brush_material_get(struct Brush *brush); void BKE_gpencil_brush_material_set(struct Brush *brush, struct Material *material); @@ -183,9 +215,9 @@ struct Material *BKE_gpencil_object_material_new(struct Main *bmain, const char *name, int *r_index); -int BKE_gpencil_object_material_get_index(struct Object *ob, struct Material *ma); +int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma); -struct Material *BKE_gpencil_object_material_get_from_brush(struct Object *ob, +struct Material *BKE_gpencil_object_material_from_brush_get(struct Object *ob, struct Brush *brush); int BKE_gpencil_object_material_get_index_from_brush(struct Object *ob, struct Brush *brush); @@ -206,22 +238,23 @@ bool BKE_gpencil_stroke_select_check(const struct bGPDstroke *gps); struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob); void BKE_gpencil_centroid_3d(struct bGPdata *gpd, float r_centroid[3]); +void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps); /* vertex groups */ void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps); void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup); 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); +/* Set active frame by layer. */ +void BKE_gpencil_frame_active_set(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_subdivide(struct bGPDstroke *gps, int level, int flag); -bool BKE_gpencil_trim_stroke(struct bGPDstroke *gps); -void BKE_gpencil_merge_distance_stroke(struct bGPDframe *gpf, +void BKE_gpencil_stroke_simplify_adaptive(struct bGPDstroke *gps, float factor); +void BKE_gpencil_stroke_simplify_fixed(struct bGPDstroke *gps); +void BKE_gpencil_stroke_subdivide(struct bGPDstroke *gps, int level, int type); +bool BKE_gpencil_stroke_trim(struct bGPDstroke *gps); +void BKE_gpencil_stroke_merge_distance(struct bGPDframe *gpf, struct bGPDstroke *gps, const float threshold, const bool use_unselected); @@ -237,31 +270,33 @@ void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points, float (*points2d)[2], const float scale, int *r_direction); -void BKE_gpencil_triangulate_stroke_fill(struct bGPdata *gpd, struct bGPDstroke *gps); +void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps); +void BKE_gpencil_stroke_geometry_update(struct bGPDstroke *gps); +void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps); void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]); -bool BKE_gpencil_sample_stroke(struct bGPDstroke *gps, const float dist, const bool select); -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); -bool BKE_gpencil_close_stroke(struct bGPDstroke *gps); +bool BKE_gpencil_stroke_sample(struct bGPDstroke *gps, const float dist, const bool select); +bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf); +bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_stroke_close(struct bGPDstroke *gps); void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag); -bool BKE_gpencil_stretch_stroke(struct bGPDstroke *gps, const float dist, const float tip_length); -bool BKE_gpencil_trim_stroke_points(struct bGPDstroke *gps, +bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length); +bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); -bool BKE_gpencil_split_stroke(struct bGPDframe *gpf, +bool BKE_gpencil_stroke_split(struct bGPDframe *gpf, struct bGPDstroke *gps, const int before_index, struct bGPDstroke **remaining_gps); -bool BKE_gpencil_shrink_stroke(struct bGPDstroke *gps, const float dist); +bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist); float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d); -void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe); +void BKE_gpencil_frame_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); @@ -273,9 +308,41 @@ void BKE_gpencil_convert_curve(struct Main *bmain, const bool use_collections, const bool only_stroke); +void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene); + +bool BKE_gpencil_from_image(struct SpaceImage *sima, + struct bGPDframe *gpf, + const float size, + const bool mask); + +/* Iterator */ +/* frame & stroke are NULL if it is a layer callback. */ +typedef void (*gpIterCb)(struct bGPDlayer *layer, + struct bGPDframe *frame, + struct bGPDstroke *stroke, + void *thunk); + +void BKE_gpencil_visible_stroke_iter(struct Object *ob, + gpIterCb layer_cb, + gpIterCb stroke_cb, + void *thunk, + bool do_onion, + int cfra); + extern void (*BKE_gpencil_batch_cache_dirty_tag_cb)(struct bGPdata *gpd); extern void (*BKE_gpencil_batch_cache_free_cb)(struct bGPdata *gpd); +void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig, + const struct bGPDframe *gpf_eval); +void BKE_gpencil_update_orig_pointers(const struct Object *ob_orig, const struct Object *ob_eval); + +void BKE_gpencil_parent_matrix_get(const struct Depsgraph *depsgraph, + struct Object *obact, + struct bGPDlayer *gpl, + float diff_mat[4][4]); + +void BKE_gpencil_update_layer_parent(const struct Depsgraph *depsgraph, struct Object *ob); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index 3a1e729e4de..b48a6284567 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -141,20 +141,10 @@ typedef struct GpencilModifierTypeInfo { /** * 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); + struct Object *ob); /** * Bake-down GP modifier's effects into the GP data-block. @@ -297,24 +287,6 @@ bool BKE_gpencil_has_geometry_modifiers(struct Object *ob); bool BKE_gpencil_has_time_modifiers(struct Object *ob); bool BKE_gpencil_has_transform_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); -int BKE_gpencil_time_modifier(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - struct bGPDlayer *gpl, - int cfra, - bool is_render); - void BKE_gpencil_lattice_init(struct Object *ob); void BKE_gpencil_lattice_clear(struct Object *ob); @@ -322,6 +294,15 @@ void BKE_gpencil_modifiers_calc(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); +void BKE_gpencil_prepare_eval_data(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob); + +struct bGPDframe *BKE_gpencil_frame_retime_get(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bGPDlayer *gpl); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 5283672bdde..46fb254a387 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -34,6 +34,7 @@ struct Brush; struct CurveMapping; struct Depsgraph; struct EnumPropertyItem; +struct GHash; struct GridPaintMask; struct ImagePool; struct MLoop; @@ -55,6 +56,7 @@ struct SubdivCCG; struct SubdivCCG; struct Tex; struct ToolSettings; +struct tPaletteColorHSV; struct UnifiedPaintSettings; struct View3D; struct ViewLayer; @@ -82,9 +84,13 @@ typedef enum ePaintMode { PAINT_MODE_TEXTURE_2D = 4, PAINT_MODE_SCULPT_UV = 5, PAINT_MODE_GPENCIL = 6, + /* Grease Pencil Vertex Paint */ + PAINT_MODE_VERTEX_GPENCIL = 7, + PAINT_MODE_SCULPT_GPENCIL = 8, + PAINT_MODE_WEIGHT_GPENCIL = 9, /** Keep last. */ - PAINT_MODE_INVALID = 7, + PAINT_MODE_INVALID = 10, } ePaintMode; #define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PAINT_MODE_SCULPT_UV) @@ -143,6 +149,15 @@ bool BKE_palette_is_empty(const struct Palette *palette); void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color); void BKE_palette_clear(struct Palette *palette); +void BKE_palette_sort_hsv(struct tPaletteColorHSV *color_array, const int totcol); +void BKE_palette_sort_svh(struct tPaletteColorHSV *color_array, const int totcol); +void BKE_palette_sort_vhs(struct tPaletteColorHSV *color_array, const int totcol); +void BKE_palette_sort_luminance(struct tPaletteColorHSV *color_array, const int totcol); +bool BKE_palette_from_hash(struct Main *bmain, + struct GHash *color_table, + const char *name, + const bool linear); + /* paint curves */ struct PaintCurve *BKE_paint_curve_add(struct Main *bmain, const char *name); struct PaintCurve *BKE_paint_curve_copy(struct Main *bmain, const struct PaintCurve *pc); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 4a92f439d74..1716439c3fd 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -271,12 +271,10 @@ void BKE_brush_init_gpencil_settings(Brush *brush) 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; - brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; /* curves */ brush->gpencil_settings->curve_sensitivity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); @@ -285,11 +283,32 @@ void BKE_brush_init_gpencil_settings(Brush *brush) } /* add a new gp-brush */ -Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name) +Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name, eObjectMode mode) { + Paint *paint = NULL; Brush *brush; - Paint *paint = &ts->gp_paint->paint; - brush = BKE_brush_add(bmain, name, OB_MODE_PAINT_GPENCIL); + switch (mode) { + case OB_MODE_PAINT_GPENCIL: { + paint = &ts->gp_paint->paint; + break; + } + case OB_MODE_SCULPT_GPENCIL: { + paint = &ts->gp_sculptpaint->paint; + break; + } + case OB_MODE_WEIGHT_GPENCIL: { + paint = &ts->gp_weightpaint->paint; + break; + } + case OB_MODE_VERTEX_GPENCIL: { + paint = &ts->gp_vertexpaint->paint; + break; + } + default: + paint = &ts->gp_paint->paint; + } + + brush = BKE_brush_add(bmain, name, mode); BKE_paint_brush_set(paint, brush); id_us_min(&brush->id); @@ -303,6 +322,22 @@ Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name) return brush; } +/* Delete a Brush. */ +bool BKE_brush_delete(Main *bmain, Brush *brush) +{ + if (brush->id.tag & LIB_TAG_INDIRECT) { + return false; + } + else if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 && + ID_EXTRA_USERS(brush) == 0) { + return false; + } + + BKE_id_delete(bmain, brush); + + return true; +} + /* grease pencil cumapping->preset */ typedef enum eGPCurveMappingPreset { GPCURVE_PRESET_PENCIL = 0, @@ -363,442 +398,760 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) } } -/* create a set of grease pencil presets. */ -void BKE_brush_gpencil_presets(Main *bmain, ToolSettings *ts) +void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) { #define SMOOTH_STROKE_RADIUS 40 #define SMOOTH_STROKE_FACTOR 0.9f +#define ACTIVE_SMOOTH 0.35f - Paint *paint = &ts->gp_paint->paint; + CurveMapping *custom_curve = NULL; - Brush *brush, *deft; - CurveMapping *custom_curve; + /* Set general defaults at brush level. */ + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; - /* Airbrush brush. */ - brush = BLI_findstring(&bmain->brushes, "Airbrush", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Airbrush"); + brush->rgb[0] = 0.498f; + brush->rgb[1] = 1.0f; + brush->rgb[2] = 0.498f; + + brush->secondary_rgb[0] = 1.0f; + brush->secondary_rgb[1] = 1.0f; + brush->secondary_rgb[2] = 1.0f; + + brush->curve_preset = BRUSH_CURVE_SMOOTH; + + if (brush->gpencil_settings == NULL) { + return; } - brush->size = 300.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + /* Set preset type. */ + brush->gpencil_settings->preset_type = type; - brush->gpencil_settings->draw_strength = 0.4f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + /* Set vertex mix factor. */ + brush->gpencil_settings->vertex_mode = GPPAINT_MODE_STROKE; + brush->gpencil_settings->vertex_factor = 1.0f; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.98f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 0.211f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + switch (type) { + case GP_BRUSH_PRESET_AIRBRUSH: { + brush->size = 300.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 0.4f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->gpencil_tool = GPAINT_TOOL_DRAW; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_AIRBRUSH; + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 0.211f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_AIRBRUSH; - /* Create and link Black Dots material to brush. - * This material is required because the brush uses the material to define how the stroke is - * drawn. */ - Material *ma = BLI_findstring(&bmain->materials, "Black Dots", offsetof(ID, name) + 2); - if (ma == NULL) { - ma = BKE_gpencil_material_add(bmain, "Black Dots"); - } - brush->gpencil_settings->material = ma; - /* Pin the matterial to the brush. */ - brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; + /* Create and link Black Dots material to brush. + * This material is required because the brush uses the material to define how the stroke is + * drawn. */ + Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); + } + brush->gpencil_settings->material = ma; + /* Pin the matterial to the brush. */ + brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED; - /* Ink Pen brush. */ - brush = BLI_findstring(&bmain->brushes, "Ink Pen", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Ink Pen"); - } - brush->size = 60.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_INK_PEN: { - brush->gpencil_settings->draw_strength = 1.0f; + brush->size = 60.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.7f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.1f; - 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.0f; - brush->gpencil_settings->simplify_f = 0.002f; + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 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_sensitivity = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.002f; - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); + 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->icon_id = GP_BRUSH_ICON_INK; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + /* Curve. */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INK); - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; + brush->gpencil_tool = GPAINT_TOOL_DRAW; - /* Ink Pen Rough brush. */ - brush = BLI_findstring(&bmain->brushes, "Ink Pen Rough", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Ink Pen Rough"); - } + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_INK_PEN_ROUGH: { + brush->size = 60.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); + + brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.0f; + brush->gpencil_settings->draw_smoothlvl = 2; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.000f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 1.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; + + /* Curve. */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); + + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_MARKER_BOLD: { + brush->size = 150.0f; + brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 0.3f; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.002f; + + brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; + 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; + + /* Curve. */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); + + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_MARKER_CHISEL: { + brush->size = 80.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = DEG2RAD(20.0f); + brush->gpencil_settings->draw_angle_factor = 1.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.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->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.002f; + + brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; + 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->icon_id = GP_BRUSH_ICON_CHISEL; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_PEN: { + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.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->draw_subdivide = 1; + brush->gpencil_settings->simplify_f = 0.002f; + + 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->icon_id = GP_BRUSH_ICON_PEN; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_PENCIL_SOFT: { + brush->size = 80.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 0.4f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 0.8f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.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->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.000f; + + 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->icon_id = GP_BRUSH_ICON_PENCIL; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_PENCIL: { + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 0.6f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->input_samples = 10; + brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.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->draw_subdivide = 0; + brush->gpencil_settings->simplify_f = 0.002f; + + 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->icon_id = GP_BRUSH_ICON_PENCIL; + brush->gpencil_tool = GPAINT_TOOL_DRAW; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_FILL_AREA: { + brush->size = 20.0f; + + brush->gpencil_settings->fill_leak = 3; + brush->gpencil_settings->fill_threshold = 0.1f; + brush->gpencil_settings->fill_simplylvl = 1; + brush->gpencil_settings->fill_factor = 1; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->hardeness = 1.0f; + copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); + brush->gpencil_settings->draw_smoothfac = 0.1f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 1; + + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; + brush->gpencil_tool = GPAINT_TOOL_FILL; + brush->gpencil_settings->vertex_mode = GPPAINT_MODE_FILL; + + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_ERASER_SOFT: { + brush->size = 30.0f; + brush->gpencil_settings->draw_strength = 0.5f; + brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush->gpencil_tool = GPAINT_TOOL_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + brush->gpencil_settings->era_strength_f = 100.0f; + brush->gpencil_settings->era_thickness_f = 10.0f; - brush->size = 60.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + break; + } + case GP_BRUSH_PRESET_ERASER_HARD: { + brush->size = 30.0f; + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + brush->gpencil_settings->era_strength_f = 100.0f; + brush->gpencil_settings->era_thickness_f = 50.0f; - brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + brush->gpencil_tool = GPAINT_TOOL_ERASE; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.5f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.0f; - brush->gpencil_settings->draw_smoothlvl = 2; - brush->gpencil_settings->thick_smoothfac = 0.0f; - brush->gpencil_settings->thick_smoothlvl = 2; - brush->gpencil_settings->draw_subdivide = 0; - brush->gpencil_settings->draw_random_sub = 0.0f; - brush->gpencil_settings->simplify_f = 0.000f; - - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; - brush->gpencil_settings->draw_random_press = 1.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_sensitivity = 1.0f; + break; + } + case GP_BRUSH_PRESET_ERASER_POINT: { + brush->size = 30.0f; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_INKNOISE); + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + brush->gpencil_tool = GPAINT_TOOL_ERASE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + break; + } + case GP_BRUSH_PRESET_ERASER_STROKE: { + brush->size = 30.0f; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; + brush->gpencil_tool = GPAINT_TOOL_ERASE; - /* Marker Bold brush. */ - brush = BLI_findstring(&bmain->brushes, "Marker Bold", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Marker Bold"); - } - brush->size = 150.0f; - brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; + break; + } + case GP_BRUSH_PRESET_TINT: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_TINT; + brush->gpencil_tool = GPAINT_TOOL_TINT; - brush->gpencil_settings->draw_strength = 0.3f; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.6f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; - brush->gpencil_settings->draw_smoothfac = 0.1f; - 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.0f; - brush->gpencil_settings->simplify_f = 0.002f; - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; - 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_sensitivity = 1.0f; + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_VERTEX_DRAW: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_DRAW; + brush->gpencil_vertex_tool = GPVERTEX_TOOL_DRAW; - /* Curve. */ - custom_curve = brush->gpencil_settings->curve_sensitivity; - BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); - BKE_curvemapping_initialize(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_MARKER); + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_VERTEX_BLUR: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_BLUR; + brush->gpencil_vertex_tool = GPVERTEX_TOOL_BLUR; - /* Marker Chisel brush. */ - brush = BLI_findstring(&bmain->brushes, "Marker Chisel", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Marker Chisel"); - } - brush->size = 80.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.5f; - brush->gpencil_settings->draw_angle = DEG2RAD(20.0f); - brush->gpencil_settings->draw_angle_factor = 1.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_VERTEX_AVERAGE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_AVERAGE; + brush->gpencil_vertex_tool = GPVERTEX_TOOL_AVERAGE; - 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->simplify_f = 0.002f; - - brush->gpencil_settings->flag &= ~GP_BRUSH_GROUP_RANDOM; - 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_sensitivity = 1.0f; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_VERTEX_SMEAR: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_SMEAR; + brush->gpencil_vertex_tool = GPVERTEX_TOOL_SMEAR; - /* Pen brush. */ - brush = BLI_findstring(&bmain->brushes, "Pen", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Pen"); - } - brush->size = 30.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.3f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.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 = 1; - brush->gpencil_settings->draw_subdivide = 1; - brush->gpencil_settings->draw_random_sub = 0.0f; - brush->gpencil_settings->simplify_f = 0.002f; - - 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_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_VERTEX_REPLACE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_REPLACE; + brush->gpencil_vertex_tool = GPVERTEX_TOOL_REPLACE; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - /* Pencil Soft brush. */ - brush = BLI_findstring(&bmain->brushes, "Pencil Soft", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Pencil Soft"); - } + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->size = 80.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + zero_v3(brush->secondary_rgb); + break; + } + case GP_BRUSH_PRESET_SMOOTH_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_SMOOTH; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_SMOOTH; - brush->gpencil_settings->draw_strength = 0.4f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.64f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 0.8f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + brush->gpencil_settings->draw_strength = 0.3f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - 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.0f; - brush->gpencil_settings->simplify_f = 0.000f; - - 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_sensitivity = 1.0f; + break; + } + case GP_BRUSH_PRESET_STRENGTH_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_STRENGTH; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_STRENGTH; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->gpencil_settings->draw_strength = 0.3f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - /* Pencil brush. */ - brush = BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Pencil"); - } - deft = brush; /* save default brush. */ + break; + } + case GP_BRUSH_PRESET_THICKNESS_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_THICKNESS; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_THICKNESS; - brush->size = 25.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->draw_strength = 0.6f; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->draw_strength = 0.5f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = 0.55f; - brush->gpencil_settings->draw_angle = 0.0f; - brush->gpencil_settings->draw_angle_factor = 0.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; + break; + } + case GP_BRUSH_PRESET_GRAB_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_GRAB; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_GRAB; + brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; - 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.0f; - brush->gpencil_settings->simplify_f = 0.002f; + brush->size = 25.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_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 0.3f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; - brush->gpencil_tool = GPAINT_TOOL_DRAW; + break; + } + case GP_BRUSH_PRESET_PUSH_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_PUSH; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PUSH; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - /* Fill brush. */ - brush = BLI_findstring(&bmain->brushes, "Fill Area", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Fill Area"); - } - brush->size = 20.0f; - brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->draw_strength = 0.3f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - brush->gpencil_settings->fill_leak = 3; - brush->gpencil_settings->fill_threshold = 0.1f; - brush->gpencil_settings->fill_simplylvl = 1; - brush->gpencil_settings->fill_factor = 1; + break; + } + case GP_BRUSH_PRESET_TWIST_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_TWIST; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_TWIST; - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->gradient_f = 1.0f; - brush->gpencil_settings->gradient_s[0] = 1.0f; - brush->gpencil_settings->gradient_s[1] = 1.0f; - brush->gpencil_settings->draw_smoothfac = 0.1f; - 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->size = 50.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 0.3f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; - brush->gpencil_tool = GPAINT_TOOL_FILL; + break; + } + case GP_BRUSH_PRESET_PINCH_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_PINCH; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_PINCH; - brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; - brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + brush->size = 50.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - /* Soft Eraser brush. */ - brush = BLI_findstring(&bmain->brushes, "Eraser Soft", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Soft"); + brush->gpencil_settings->draw_strength = 0.5f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; + + break; + } + case GP_BRUSH_PRESET_RANDOMIZE_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_RANDOMIZE; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_RANDOMIZE; + + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 0.5f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; + + break; + } + case GP_BRUSH_PRESET_CLONE_STROKE: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_CLONE; + brush->gpencil_sculpt_tool = GPSCULPT_TOOL_CLONE; + brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->size = 25.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; + + break; + } + case GP_BRUSH_PRESET_DRAW_WEIGHT: { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_GPBRUSH_WEIGHT; + brush->gpencil_weight_tool = GPWEIGHT_TOOL_DRAW; + + brush->size = 25.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + + brush->gpencil_settings->draw_strength = 0.8f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; + + break; + } + default: + break; } - brush->size = 30.0f; - brush->gpencil_settings->draw_strength = 0.5f; - brush->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); - brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; - brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; - brush->gpencil_tool = GPAINT_TOOL_ERASE; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - brush->gpencil_settings->era_strength_f = 100.0f; - brush->gpencil_settings->era_thickness_f = 10.0f; +} - /* Hard Eraser brush. */ - brush = BLI_findstring(&bmain->brushes, "Eraser Hard", offsetof(ID, name) + 2); +static Brush *gpencil_brush_ensure(Main *bmain, + ToolSettings *ts, + const char *brush_name, + eObjectMode mode) +{ + Brush *brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2); if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Hard"); + brush = BKE_brush_add_gpencil(bmain, ts, brush_name, mode); } - brush->size = 30.0f; - brush->gpencil_settings->draw_strength = 1.0f; - brush->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - brush->gpencil_settings->era_strength_f = 100.0f; - brush->gpencil_settings->era_thickness_f = 50.0f; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; - brush->gpencil_tool = GPAINT_TOOL_ERASE; - - /* Point Eraser brush. */ - brush = BLI_findstring(&bmain->brushes, "Eraser Point", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Point"); + if (brush->gpencil_settings == NULL) { + BKE_brush_init_gpencil_settings(brush); } - brush->size = 30.0f; - brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; - brush->gpencil_tool = GPAINT_TOOL_ERASE; + return brush; +} + +/* Create a set of grease pencil Drawing presets. */ +void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts) +{ + + Paint *paint = &ts->gp_paint->paint; + + Brush *brush, *deft_draw; + /* Airbrush brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Airbrush", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_AIRBRUSH); + + /* Ink Pen brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Ink Pen", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_INK_PEN); + + /* Ink Pen Rough brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Ink Pen Rough", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_INK_PEN_ROUGH); + + /* Marker Bold brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Marker Bold", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_MARKER_BOLD); + + /* Marker Chisel brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Marker Chisel", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_MARKER_CHISEL); + + /* Pen brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Pen", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PEN); + + /* Pencil Soft brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Pencil Soft", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PENCIL_SOFT); + + /* Pencil brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Pencil", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PENCIL); + deft_draw = brush; /* save default brush. */ + + /* Fill brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Fill Area", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_FILL_AREA); + + /* Soft Eraser brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Eraser Soft", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_SOFT); + + /* Hard Eraser brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Eraser Hard", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_HARD); + + /* Point Eraser brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Eraser Point", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_POINT); /* Stroke Eraser brush. */ - brush = BLI_findstring(&bmain->brushes, "Eraser Stroke", offsetof(ID, name) + 2); - if (brush == NULL) { - brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Stroke"); - } - brush->size = 30.0f; - brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; - brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; + brush = gpencil_brush_ensure(bmain, ts, "Eraser Stroke", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_ERASER_STROKE); + + /* Tint brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Tint", OB_MODE_PAINT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_TINT); + + /* Set default Draw brush. */ + BKE_paint_brush_set(paint, deft_draw); +} + +/* Create a set of grease pencil Vertex Paint presets. */ +void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts) +{ + Paint *vertexpaint = &ts->gp_vertexpaint->paint; + + Brush *brush, *deft_vertex; + /* Vertex Draw brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Vertex Draw", OB_MODE_VERTEX_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_DRAW); + deft_vertex = brush; /* save default brush. */ + + /* Vertex Blur brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Vertex Blur", OB_MODE_VERTEX_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_BLUR); + + /* Vertex Average brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Vertex Average", OB_MODE_VERTEX_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_AVERAGE); + + /* Vertex Smear brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Vertex Smear", OB_MODE_VERTEX_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_SMEAR); + + /* Vertex Replace brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Vertex Replace", OB_MODE_VERTEX_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_VERTEX_REPLACE); + + /* Set default Vertex brush. */ + BKE_paint_brush_set(vertexpaint, deft_vertex); +} + +/* Create a set of grease pencil Sculpt Paint presets. */ +void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts) +{ + Paint *sculptpaint = &ts->gp_sculptpaint->paint; + Brush *brush, *deft_sculpt; + + /* Smooth brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Smooth Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_SMOOTH_STROKE); + deft_sculpt = brush; + + /* Strength brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Strength Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_STRENGTH_STROKE); + + /* Thickness brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Thickness Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_THICKNESS_STROKE); + + /* Grab brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Grab Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_GRAB_STROKE); + + /* Push brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Push Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PUSH_STROKE); + + /* Twist brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Twist Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_TWIST_STROKE); + + /* Pinch brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Pinch Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_PINCH_STROKE); + + /* Randomize brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Randomize Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_RANDOMIZE_STROKE); + + /* Clone brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Clone Stroke", OB_MODE_SCULPT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_CLONE_STROKE); + + /* Set default brush. */ + BKE_paint_brush_set(sculptpaint, deft_sculpt); +} + +/* Create a set of grease pencil Weight Paint presets. */ +void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts) +{ + Paint *weightpaint = &ts->gp_weightpaint->paint; - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; - brush->gpencil_tool = GPAINT_TOOL_ERASE; + Brush *brush, *deft_weight; + /* Vertex Draw brush. */ + brush = gpencil_brush_ensure(bmain, ts, "Draw Weight", OB_MODE_WEIGHT_GPENCIL); + BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_DRAW_WEIGHT); + deft_weight = brush; /* save default brush. */ - /* set default brush. */ - BKE_paint_brush_set(paint, deft); + /* Set default brush. */ + BKE_paint_brush_set(weightpaint, deft_weight); } struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 48985932d87..b2c551a1752 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1124,6 +1124,9 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, else if (object_mode & OB_MODE_WEIGHT_GPENCIL) { return CTX_MODE_WEIGHT_GPENCIL; } + else if (object_mode & OB_MODE_VERTEX_GPENCIL) { + return CTX_MODE_VERTEX_GPENCIL; + } } } @@ -1140,25 +1143,11 @@ enum eContextObjectMode CTX_data_mode_enum(const bContext *C) /* would prefer if we can use the enum version below over this one - Campbell */ /* must be aligned with above enum */ static const char *data_mode_strings[] = { - "mesh_edit", - "curve_edit", - "surface_edit", - "text_edit", - "armature_edit", - "mball_edit", - "lattice_edit", - "posemode", - "sculpt_mode", - "weightpaint", - "vertexpaint", - "imagepaint", - "particlemode", - "objectmode", - "greasepencil_paint", - "greasepencil_edit", - "greasepencil_sculpt", - "greasepencil_weight", - NULL, + "mesh_edit", "curve_edit", "surface_edit", "text_edit", + "armature_edit", "mball_edit", "lattice_edit", "posemode", + "sculpt_mode", "weightpaint", "vertexpaint", "imagepaint", + "particlemode", "objectmode", "greasepencil_paint", "greasepencil_edit", + "greasepencil_sculpt", "greasepencil_weight", "greasepencil_vertex", NULL, }; BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1, "Must have a string for each context mode") diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 747ffb92ad5..0b33eaccb6f 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -39,12 +39,16 @@ #include "BLT_translation.h" +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.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_space_types.h" #include "DNA_object_types.h" #include "BKE_action.h" @@ -55,14 +59,17 @@ #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_icons.h" +#include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BLI_math_color.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" static CLG_LogRef LOG = {"bke.gpencil"}; @@ -139,12 +146,10 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps) /* Free strokes belonging to a gp-frame */ bool BKE_gpencil_free_strokes(bGPDframe *gpf) { - bGPDstroke *gps_next; bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false); /* free strokes */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) { - gps_next = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { BKE_gpencil_free_stroke(gps); } BLI_listbase_clear(&gpf->strokes); @@ -152,24 +157,6 @@ 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 *gpf_eval) -{ - bGPDstroke *gps_next; - if (!gpf_eval) { - return false; - } - - /* free strokes */ - for (bGPDstroke *gps = gpf_eval->strokes.first; gps; gps = gps_next) { - gps_next = gps->next; - BKE_gpencil_free_stroke(gps); - } - BLI_listbase_clear(&gpf_eval->strokes); - - return true; -} - /* Free all of a gp-layer's frames */ void BKE_gpencil_free_frames(bGPDlayer *gpl) { @@ -191,6 +178,15 @@ void BKE_gpencil_free_frames(bGPDlayer *gpl) gpl->actframe = NULL; } +void BKE_gpencil_free_layer_masks(bGPDlayer *gpl) +{ + /* Free masks.*/ + bGPDlayer_Mask *mask_next = NULL; + for (bGPDlayer_Mask *mask = gpl->mask_layers.first; mask; mask = mask_next) { + mask_next = mask->next; + BLI_freelinkN(&gpl->mask_layers, mask); + } +} /* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ void BKE_gpencil_free_layers(ListBase *list) { @@ -207,6 +203,10 @@ void BKE_gpencil_free_layers(ListBase *list) /* free layers and their data */ BKE_gpencil_free_frames(gpl); + + /* Free masks.*/ + BKE_gpencil_free_layer_masks(gpl); + BLI_freelinkN(list, gpl); } } @@ -230,6 +230,13 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all) } } +void BKE_gpencil_eval_delete(bGPdata *gpd_eval) +{ + BKE_gpencil_free(gpd_eval, true); + BKE_libblock_free_data(&gpd_eval->id, false); + MEM_freeN(gpd_eval); +} + /* ************************************************** */ /* Container Creation */ @@ -307,7 +314,7 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) new_frame = BKE_gpencil_frame_duplicate(gpl->actframe); /* Find frame to insert it before */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if (gpf->framenum > cframe) { /* Add it here */ BLI_insertlinkbefore(&gpl->frames, gpf, new_frame); @@ -356,7 +363,7 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti /* allocate memory for frame and add to end of list */ gpl = MEM_callocN(sizeof(bGPDlayer), "bGPDlayer"); - gpl_active = BKE_gpencil_layer_getactive(gpd); + gpl_active = BKE_gpencil_layer_active_get(gpd); /* add to datablock */ if (gpl_active == NULL) { @@ -386,6 +393,8 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti gpl->opacity = 1.0f; /* default channel color */ ARRAY_SET_ITEMS(gpl->color, 0.2f, 0.2f, 0.2f); + /* Default vertex mix. */ + gpl->vertex_paint_opacity = 1.0f; } /* auto-name */ @@ -397,9 +406,11 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti offsetof(bGPDlayer, info), sizeof(gpl->info)); + /* Enable always affected by scene lights. */ + gpl->flag |= GP_LAYER_USE_LIGHTS; /* make this one the active one */ if (setactive) { - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); } /* return layer */ @@ -419,7 +430,6 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) /* general flags */ gpd->flag |= GP_DATA_VIEWALIGN; - gpd->flag |= GP_DATA_STROKE_FORCE_RECALC; /* always enable object onion skin switch */ gpd->flag |= GP_DATA_SHOW_ONIONSKINS; /* GP object specific settings */ @@ -427,6 +437,8 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; + gpd->zdepth_offset = 0.150f; + /* grid settings */ ARRAY_SET_ITEMS(gpd->grid.color, 0.5f, 0.5f, 0.5f); /* Color */ ARRAY_SET_ITEMS(gpd->grid.scale, 1.0f, 1.0f); /* Scale */ @@ -474,43 +486,59 @@ void BKE_gpencil_stroke_add_points(bGPDstroke *gps, } } -/* Create a new stroke, with pre-allocated data buffers */ -bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness) +/* Create a new stroke, with pre-allocated data buffers. */ +bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness) { /* allocate memory for a new stroke */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); gps->thickness = thickness; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; + gps->fill_opacity_fac = 1.0f; + gps->hardeness = 1.0f; + copy_v2_fl(gps->aspect_ratio, 1.0f); + + gps->uv_scale = 1.0f; gps->inittime = 0; - /* enable recalculation flag by default */ - gps->flag = GP_STROKE_RECALC_GEOMETRY | GP_STROKE_3DSPACE; + gps->flag = GP_STROKE_3DSPACE; gps->totpoints = totpoints; gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); /* initialize triangle memory to dummy data */ gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; gps->tot_triangles = 0; gps->mat_nr = mat_idx; - /* add to frame */ - BLI_addtail(&gpf->strokes, gps); + return gps; +} + +/* Create a new stroke and add to frame. */ +bGPDstroke *BKE_gpencil_stroke_add( + bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head) +{ + bGPDstroke *gps = BKE_gpencil_stroke_new(mat_idx, totpoints, thickness); + + /* Add to frame. */ + if ((gps != NULL) && (gpf != NULL)) { + if (!insert_at_head) { + BLI_addtail(&gpf->strokes, gps); + } + else { + BLI_addhead(&gpf->strokes, gps); + } + } return gps; } /* Add a stroke and copy the temporary drawing color value from one of the existing stroke */ -bGPDstroke *BKE_gpencil_add_stroke_existing_style( +bGPDstroke *BKE_gpencil_stroke_add_existing_style( bGPDframe *gpf, bGPDstroke *existing, int mat_idx, int totpoints, short thickness) { - bGPDstroke *gps = BKE_gpencil_add_stroke(gpf, mat_idx, totpoints, thickness); + bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, mat_idx, totpoints, thickness, false); /* Copy run-time color data so that strokes added in the modifier has the style. * There are depsgraph reference pointers inside, * change the copy function if interfere with future drawing implementation. */ @@ -533,30 +561,26 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d } /* make a copy of a given gpencil stroke */ -bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src) +bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_points) { bGPDstroke *gps_dst = NULL; gps_dst = MEM_dupallocN(gps_src); gps_dst->prev = gps_dst->next = NULL; + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->points = MEM_dupallocN(gps_src->points); + if (dup_points) { + gps_dst->points = MEM_dupallocN(gps_src->points); - if (gps_src->dvert != NULL) { - gps_dst->dvert = MEM_dupallocN(gps_src->dvert); - BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); - } - else { - gps_dst->dvert = NULL; + if (gps_src->dvert != NULL) { + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); + } + else { + gps_dst->dvert = NULL; + } } - /* 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. - */ - gps_dst->triangles = MEM_dupallocN(gps_dst->triangles); - /* gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; */ - /* return new stroke */ return gps_dst; } @@ -580,7 +604,7 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) 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); + gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true); BLI_addtail(&gpf_dst->strokes, gps_dst); } @@ -601,7 +625,7 @@ void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_ds 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); + gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true); BLI_addtail(&gpf_dst->strokes, gps_dst); } } @@ -622,6 +646,14 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) gpl_dst = MEM_dupallocN(gpl_src); gpl_dst->prev = gpl_dst->next = NULL; + /* Copy masks. */ + BLI_listbase_clear(&gpl_dst->mask_layers); + LISTBASE_FOREACH (bGPDlayer_Mask *, mask_src, &gpl_src->mask_layers) { + bGPDlayer_Mask *mask_dst = MEM_dupallocN(mask_src); + mask_dst->prev = mask_dst->next = NULL; + BLI_addtail(&gpl_dst->mask_layers, mask_dst); + } + /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { @@ -658,7 +690,7 @@ void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int U /* copy layers */ BLI_listbase_clear(&gpd_dst->layers); - for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { /* make a copy of source layer and its data */ /* TODO here too could add unused flags... */ @@ -766,8 +798,8 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* if frame has no strokes after this, delete it */ if (BLI_listbase_is_empty(&gpf->strokes)) { - BKE_gpencil_layer_delframe(gpl, gpf); - BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV); + BKE_gpencil_layer_frame_delete(gpl, gpf); + BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_USE_PREV); } } @@ -775,7 +807,7 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* GP Layer API */ /* Check if the given layer is able to be edited or not */ -bool gpencil_layer_is_editable(const bGPDlayer *gpl) +bool BKE_gpencil_layer_is_editable(const bGPDlayer *gpl) { /* Sanity check */ if (gpl == NULL) { @@ -797,7 +829,7 @@ bool gpencil_layer_is_editable(const bGPDlayer *gpl) } /* Look up the gp-frame on the requested frame number, but don't add a new one */ -bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe) +bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe) { bGPDframe *gpf; @@ -817,7 +849,7 @@ bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe) * - this sets the layer's actframe var (if allowed to) * - extension beyond range (if first gp-frame is after all frame in interest and cannot add) */ -bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) +bGPDframe *BKE_gpencil_layer_frame_get(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) { bGPDframe *gpf = NULL; bool found = false; @@ -966,7 +998,7 @@ bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_M } /* delete the given frame from a layer */ -bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) +bool BKE_gpencil_layer_frame_delete(bGPDlayer *gpl, bGPDframe *gpf) { bool changed = false; @@ -989,18 +1021,103 @@ bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) return changed; } -/* get the active gp-layer for editing */ -bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd) +bGPDlayer *BKE_gpencil_layer_named_get(bGPdata *gpd, const char *name) +{ + if (name[0] == '\0') { + return NULL; + } + return BLI_findstring(&gpd->layers, name, offsetof(bGPDlayer, info)); +} + +bGPDlayer_Mask *BKE_gpencil_layer_mask_named_get(bGPDlayer *gpl, const char *name) +{ + if (name[0] == '\0') { + return NULL; + } + return BLI_findstring(&gpl->mask_layers, name, offsetof(bGPDlayer_Mask, name)); +} + +bGPDlayer_Mask *BKE_gpencil_layer_mask_add(bGPDlayer *gpl, const char *name) +{ + + bGPDlayer_Mask *mask = MEM_callocN(sizeof(bGPDlayer_Mask), "bGPDlayer_Mask"); + BLI_addtail(&gpl->mask_layers, mask); + BLI_strncpy(mask->name, name, sizeof(mask->name)); + gpl->act_mask++; + + return mask; +} + +void BKE_gpencil_layer_mask_remove(bGPDlayer *gpl, bGPDlayer_Mask *mask) +{ + BLI_freelinkN(&gpl->mask_layers, mask); + gpl->act_mask--; + CLAMP_MIN(gpl->act_mask, 0); +} + +void BKE_gpencil_layer_mask_remove_ref(bGPdata *gpd, const char *name) +{ + bGPDlayer_Mask *mask_next; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + for (bGPDlayer_Mask *mask = gpl->mask_layers.first; mask; mask = mask_next) { + mask_next = mask->next; + if (STREQ(mask->name, name)) { + BKE_gpencil_layer_mask_remove(gpl, mask); + } + } + } +} + +static int gpencil_cb_sort_masks(const void *arg1, const void *arg2) +{ + /* sort is inverted as layer list. */ + const struct bGPDlayer_Mask *mask1 = arg1; + const struct bGPDlayer_Mask *mask2 = arg2; + int val = 0; + + if (mask1->sort_index < mask2->sort_index) { + val = 1; + } + else if (mask1->sort_index > mask2->sort_index) { + val = -1; + } + + return val; +} + +void BKE_gpencil_layer_mask_sort(bGPdata *gpd, bGPDlayer *gpl) +{ + /* Update sort index. */ + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { + bGPDlayer *gpl_mask = BKE_gpencil_layer_named_get(gpd, mask->name); + if (gpl_mask != NULL) { + mask->sort_index = BLI_findindex(&gpd->layers, gpl_mask); + } + else { + mask->sort_index = 0; + } + } + BLI_listbase_sort(&gpl->mask_layers, gpencil_cb_sort_masks); +} + +void BKE_gpencil_layer_mask_sort_all(bGPdata *gpd) { - bGPDlayer *gpl; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + BKE_gpencil_layer_mask_sort(gpd, gpl); + } +} +/* get the active gp-layer for editing */ +bGPDlayer *BKE_gpencil_layer_active_get(bGPdata *gpd) +{ /* error checking */ if (ELEM(NULL, gpd, gpd->layers.first)) { return NULL; } /* loop over layers until found (assume only one active) */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_ACTIVE) { return gpl; } @@ -1011,17 +1128,15 @@ bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd) } /* set the active gp-layer */ -void BKE_gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) +void BKE_gpencil_layer_active_set(bGPdata *gpd, bGPDlayer *active) { - bGPDlayer *gpl; - /* error checking */ if (ELEM(NULL, gpd, gpd->layers.first, active)) { return; } /* loop over layers deactivating all */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag &= ~GP_LAYER_ACTIVE; if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { gpl->flag |= GP_LAYER_LOCKED; @@ -1040,13 +1155,11 @@ void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock) { BLI_assert(gpd != NULL); - bGPDlayer *gpl; - if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { - bGPDlayer *layer_active = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *layer_active = BKE_gpencil_layer_active_get(gpd); /* Lock all other layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* unlock active layer */ if (gpl == layer_active) { gpl->flag &= ~GP_LAYER_LOCKED; @@ -1061,7 +1174,7 @@ void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock) * a problem in the UI because the user expects all layers will be unlocked */ if (unlock) { - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag &= ~GP_LAYER_LOCKED; } } @@ -1079,6 +1192,12 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) /* free layer */ BKE_gpencil_free_frames(gpl); + /* Free Masks. */ + BKE_gpencil_free_layer_masks(gpl); + + /* Remove any reference to that layer in masking lists. */ + BKE_gpencil_layer_mask_remove_ref(gpd, gpl->info); + /* free icon providing preview of icon color */ BKE_icon_delete(gpl->runtime.icon_id); @@ -1119,7 +1238,7 @@ Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Material *ma = BKE_gpencil_brush_material_get(brush); /* check if the material is already on object material slots and add it if missing */ - if (ma && BKE_gpencil_object_material_get_index(ob, ma) < 0) { + if (ma && BKE_gpencil_object_material_index_get(ob, ma) < 0) { BKE_object_material_slot_add(bmain, ob); BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); } @@ -1138,7 +1257,7 @@ int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *materi if (!material) { return -1; } - int index = BKE_gpencil_object_material_get_index(ob, material); + int index = BKE_gpencil_object_material_index_get(ob, material); if (index < 0) { BKE_object_material_slot_add(bmain, ob); BKE_object_material_assign(bmain, ob, material, ob->totcol, BKE_MAT_ASSIGN_USERPREF); @@ -1167,7 +1286,7 @@ Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *n } /* Returns the material for a brush with respect to its pinned state. */ -Material *BKE_gpencil_object_material_get_from_brush(Object *ob, Brush *brush) +Material *BKE_gpencil_object_material_from_brush_get(Object *ob, Brush *brush) { if ((brush) && (brush->gpencil_settings) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) { @@ -1183,7 +1302,7 @@ Material *BKE_gpencil_object_material_get_from_brush(Object *ob, Brush *brush) int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush) { if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) { - return BKE_gpencil_object_material_get_index(ob, brush->gpencil_settings->material); + return BKE_gpencil_object_material_index_get(ob, brush->gpencil_settings->material); } else { return ob->actcol - 1; @@ -1294,11 +1413,11 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3]) return changed; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { bGPDframe *gpf = gpl->actframe; if (gpf != NULL) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { changed = BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); } } @@ -1330,6 +1449,13 @@ void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3]) mul_v3_v3fl(r_centroid, tot, 0.5f); } +/* Compute stroke bounding box. */ +void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps) +{ + INIT_MINMAX(gps->boundbox_min, gps->boundbox_max); + BKE_gpencil_stroke_minmax(gps, false, gps->boundbox_min, gps->boundbox_max); +} + /* create bounding box values */ static void boundbox_gpencil(Object *ob) { @@ -1381,7 +1507,7 @@ void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) } const float scalef = mat4_to_scale(mat); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* FIXME: For now, we just skip parented layers. * Otherwise, we have to update each frame to find * the current parent position/effects. @@ -1390,8 +1516,8 @@ void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) continue; } - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; int i; @@ -1400,9 +1526,8 @@ void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) pt->pressure *= scalef; } - /* TODO: Do we need to do this? distortion may mean we need to re-triangulate */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + /* Distortion may mean we need to re-triangulate. */ + BKE_gpencil_stroke_geometry_update(gps); } } } @@ -1421,9 +1546,9 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (gps->dvert != NULL) { for (int i = 0; i < gps->totpoints; i++) { dvert = &gps->dvert[i]; @@ -1537,6 +1662,7 @@ static int stroke_march_next_point(const bGPDstroke *gps, float *result, float *pressure, float *strength, + float *vert_color, float *ratio_result, int *index_from, int *index_to) @@ -1576,6 +1702,7 @@ static int stroke_march_next_point(const bGPDstroke *gps, copy_v3_v3(result, &pt->x); *pressure = gps->points[next_point_index].pressure; *strength = gps->points[next_point_index].strength; + memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float) * 4); *index_from = next_point_index - 1; *index_to = next_point_index; @@ -1590,6 +1717,10 @@ static int stroke_march_next_point(const bGPDstroke *gps, gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio); *strength = interpf( gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio); + interp_v4_v4v4(vert_color, + gps->points[next_point_index - 1].vert_color, + gps->points[next_point_index].vert_color, + ratio); *index_from = next_point_index - 1; *index_to = next_point_index; @@ -1673,7 +1804,7 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist) * \param gps: Stroke to sample * \param dist: Distance of one segment */ -bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool select) +bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select) { bGPDspoint *pt = gps->points; bGPDspoint *pt1 = NULL; @@ -1701,6 +1832,7 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel int next_point_index = 1; i = 0; float pressure, strength, ratio_result; + float vert_color[4]; int index_from, index_to; float last_coord[3]; @@ -1711,6 +1843,7 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel copy_v3_v3(&pt2->x, last_coord); new_pt[i].pressure = pt[0].pressure; new_pt[i].strength = pt[0].strength; + memcpy(new_pt[i].vert_color, pt[0].vert_color, sizeof(float) * 4); if (select) { new_pt[i].flag |= GP_SPOINT_SELECT; } @@ -1728,6 +1861,7 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel last_coord, &pressure, &strength, + vert_color, &ratio_result, &index_from, &index_to)) > -1) { @@ -1735,6 +1869,7 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel copy_v3_v3(&pt2->x, last_coord); new_pt[i].pressure = pressure; new_pt[i].strength = strength; + memcpy(new_pt[i].vert_color, vert_color, sizeof(float) * 4); if (select) { new_pt[i].flag |= GP_SPOINT_SELECT; } @@ -1766,8 +1901,8 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel gps->totpoints = i; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); return true; } @@ -1778,7 +1913,7 @@ bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool sel * \param dist: Distance of one segment * \param tip_length: Ignore tip jittering, set zero to use default value. */ -bool BKE_gpencil_stretch_stroke(bGPDstroke *gps, const float dist, const float tip_length) +bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length) { bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; int i; @@ -1829,7 +1964,7 @@ bool BKE_gpencil_stretch_stroke(bGPDstroke *gps, const float dist, const float t * \param index_from: the index of the first point to be used in the trimmed result * \param index_to: the index of the last point to be used in the trimmed result */ -bool BKE_gpencil_trim_stroke_points(bGPDstroke *gps, const int index_from, const int index_to) +bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const int index_to) { bGPDspoint *pt = gps->points, *new_pt; MDeformVert *dv, *new_dv; @@ -1879,7 +2014,7 @@ bool BKE_gpencil_trim_stroke_points(bGPDstroke *gps, const int index_from, const return true; } -bool BKE_gpencil_split_stroke(bGPDframe *gpf, +bool BKE_gpencil_stroke_split(bGPDframe *gpf, bGPDstroke *gps, const int before_index, bGPDstroke **remaining_gps) @@ -1897,7 +2032,7 @@ bool BKE_gpencil_split_stroke(bGPDframe *gpf, /* Handle remaining segments first. */ - new_gps = BKE_gpencil_add_stroke_existing_style( + new_gps = BKE_gpencil_stroke_add_existing_style( gpf, gps, gps->mat_nr, new_count, gps->thickness); new_pt = new_gps->points; /* Allocated from above. */ @@ -1907,13 +2042,14 @@ bool BKE_gpencil_split_stroke(bGPDframe *gpf, } if (gps->dvert) { - new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_remaining"); + new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, + "gp_stroke_dverts_remaining(MDeformVert)"); for (int i = 0; i < new_count; i++) { dv = &gps->dvert[i + before_index]; new_dv[i].flag = dv->flag; new_dv[i].totweight = dv->totweight; new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight, - "gp_stroke_dverts_dw_remaining"); + "gp_stroke_dverts_dw_remaining(MDeformWeight)"); for (int j = 0; j < dv->totweight; j++) { new_dv[i].dw[j].weight = dv->dw[j].weight; new_dv[i].dw[j].def_nr = dv->dw[j].def_nr; @@ -1927,8 +2063,8 @@ bool BKE_gpencil_split_stroke(bGPDframe *gpf, /* Trim the original stroke into a shorter one. * Keep the end point. */ - BKE_gpencil_trim_stroke_points(gps, 0, old_count); - + BKE_gpencil_stroke_trim_points(gps, 0, old_count); + BKE_gpencil_stroke_geometry_update(gps); return true; } @@ -1937,7 +2073,7 @@ bool BKE_gpencil_split_stroke(bGPDframe *gpf, * \param gps: Stroke to shrink * \param dist: delta length */ -bool BKE_gpencil_shrink_stroke(bGPDstroke *gps, const float dist) +bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist) { bGPDspoint *pt = gps->points, *second_last; int i; @@ -1981,7 +2117,7 @@ bool BKE_gpencil_shrink_stroke(bGPDstroke *gps, const float dist) index_start = index_end = 0; /* no length left to cut */ } - BKE_gpencil_trim_stroke_points(gps, index_start, index_end); + BKE_gpencil_stroke_trim_points(gps, index_start, index_end); if (gps->totpoints == 0) { return false; @@ -2009,7 +2145,7 @@ bool BKE_gpencil_shrink_stroke(bGPDstroke *gps, const float dist) * \param i: Point index * \param inf: Amount of smoothing to apply */ -bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) +bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) { bGPDspoint *pt = &gps->points[i]; float sco[3] = {0.0f}; @@ -2068,7 +2204,7 @@ bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) /** * Apply smooth for strength to stroke point */ -bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence) +bool BKE_gpencil_stroke_smooth_strength(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; @@ -2128,7 +2264,7 @@ bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float /** * Apply smooth for thickness to stroke point (use pressure) */ -bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence) +bool BKE_gpencil_stroke_smooth_thickness(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; @@ -2188,7 +2324,7 @@ bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float /** * Apply smooth for UV rotation to stroke point (use pressure). */ -bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence) +bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influence) { bGPDspoint *ptb = &gps->points[point_index]; @@ -2233,12 +2369,12 @@ bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influe * \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) +void BKE_gpencil_frame_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) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if (gpf->flag & GP_FRAME_SELECT) { if (gpf->framenum < *r_initframe) { *r_initframe = gpf->framenum; @@ -2291,9 +2427,9 @@ float BKE_gpencil_multiframe_falloff_calc( /* reassign strokes using a material */ void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index) { - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* reassign strokes */ if ((gps->mat_nr > index) || (gps->mat_nr > totcol - 1)) { gps->mat_nr--; @@ -2307,9 +2443,9 @@ void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index) /* remove strokes using a material */ bool BKE_gpencil_material_index_used(bGPdata *gpd, int index) { - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (gps->mat_nr == index) { return true; } @@ -2333,9 +2469,9 @@ void BKE_gpencil_material_remap(struct bGPdata *gpd, } \ ((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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* reassign strokes */ MAT_NR_REMAP(gps->mat_nr); } @@ -2345,6 +2481,102 @@ void BKE_gpencil_material_remap(struct bGPdata *gpd, #undef MAT_NR_REMAP } +/* Load a table with material conversion index for merged materials. */ +bool BKE_gpencil_merge_materials_table_get(Object *ob, + const float hue_threshold, + const float sat_threshold, + const float val_threshold, + GHash *r_mat_table) +{ + bool changed = false; + + Material *ma_primary = NULL; + Material *ma_secondary = NULL; + MaterialGPencilStyle *gp_style_primary = NULL; + MaterialGPencilStyle *gp_style_secondary = NULL; + + short *totcol = BKE_object_material_len_p(ob); + if (totcol == 0) { + return changed; + } + + for (int idx_primary = 0; idx_primary < *totcol; idx_primary++) { + /* Read primary material to compare. */ + ma_primary = BKE_gpencil_material(ob, idx_primary + 1); + if (ma_primary == NULL) { + continue; + } + + for (int idx_secondary = idx_primary + 1; idx_secondary < *totcol; idx_secondary++) { + /* Read secondary material to compare with primary material. */ + ma_secondary = BKE_gpencil_material(ob, idx_secondary + 1); + if ((ma_secondary == NULL) || + (BLI_ghash_haskey(r_mat_table, POINTER_FROM_INT(idx_secondary)))) { + continue; + } + gp_style_primary = ma_primary->gp_style; + gp_style_secondary = ma_secondary->gp_style; + + if ((gp_style_primary == NULL) || (gp_style_secondary == NULL) || + (gp_style_secondary->flag & GP_MATERIAL_LOCKED)) { + continue; + } + + /* Check materials have the same mode. */ + if (gp_style_primary->mode != gp_style_secondary->mode) { + continue; + } + + /* Check materials have same stroke and fill attributes. */ + if ((gp_style_primary->flag & GP_MATERIAL_STROKE_SHOW) != + (gp_style_secondary->flag & GP_MATERIAL_STROKE_SHOW)) { + continue; + } + + if ((gp_style_primary->flag & GP_MATERIAL_FILL_SHOW) != + (gp_style_secondary->flag & GP_MATERIAL_FILL_SHOW)) { + continue; + } + + /* Check materials have the same type. */ + if ((gp_style_primary->stroke_style != gp_style_secondary->stroke_style) || + (gp_style_primary->fill_style != gp_style_secondary->fill_style)) { + continue; + } + + float s_hsv_a[3], s_hsv_b[3], f_hsv_a[3], f_hsv_b[3], col[3]; + copy_v3_v3(col, gp_style_primary->stroke_rgba); + rgb_to_hsv_compat_v(col, s_hsv_a); + copy_v3_v3(col, gp_style_secondary->stroke_rgba); + rgb_to_hsv_compat_v(col, s_hsv_b); + + copy_v3_v3(col, gp_style_primary->fill_rgba); + rgb_to_hsv_compat_v(col, f_hsv_a); + copy_v3_v3(col, gp_style_secondary->fill_rgba); + rgb_to_hsv_compat_v(col, f_hsv_b); + + /* Check stroke and fill color (only Hue and Saturation). */ + if ((!compare_ff(s_hsv_a[0], s_hsv_b[0], hue_threshold)) || + (!compare_ff(s_hsv_a[1], s_hsv_b[1], sat_threshold)) || + (!compare_ff(f_hsv_a[0], f_hsv_b[0], hue_threshold)) || + (!compare_ff(f_hsv_a[1], f_hsv_b[1], sat_threshold)) || + (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || + (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || + (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold)) || + (!compare_ff(s_hsv_a[2], s_hsv_b[2], val_threshold))) { + continue; + } + + /* Save conversion indexes. */ + BLI_ghash_insert( + r_mat_table, POINTER_FROM_INT(idx_secondary), POINTER_FROM_INT(idx_primary)); + changed = true; + } + } + + return changed; +} + /* statistics functions */ void BKE_gpencil_stats_update(bGPdata *gpd) { @@ -2353,11 +2585,11 @@ void BKE_gpencil_stats_update(bGPdata *gpd) gpd->totstroke = 0; gpd->totpoint = 0; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpd->totlayer++; - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { gpd->totframe++; - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { gpd->totstroke++; gpd->totpoint += gps->totpoints; } @@ -2366,7 +2598,7 @@ void BKE_gpencil_stats_update(bGPdata *gpd) } /* get material index (0-based like mat_nr not actcol) */ -int BKE_gpencil_object_material_get_index(Object *ob, Material *ma) +int BKE_gpencil_object_material_index_get(Object *ob, Material *ma) { short *totcol = BKE_object_material_len_p(ob); Material *read_ma = NULL; @@ -2524,61 +2756,48 @@ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points, *r_direction = (int)locy[2]; } -/* calc bounding box in 2d using flat projection data */ -static void gpencil_calc_2d_bounding_box(const float (*points2d)[2], - int totpoints, - float minv[2], - float maxv[2]) -{ - minv[0] = points2d[0][0]; - minv[1] = points2d[0][1]; - maxv[0] = points2d[0][0]; - maxv[1] = points2d[0][1]; - - for (int i = 1; i < totpoints; i++) { - /* min */ - if (points2d[i][0] < minv[0]) { - minv[0] = points2d[i][0]; - } - if (points2d[i][1] < minv[1]) { - minv[1] = points2d[i][1]; - } - /* max */ - if (points2d[i][0] > maxv[0]) { - maxv[0] = points2d[i][0]; - } - if (points2d[i][1] > maxv[1]) { - maxv[1] = points2d[i][1]; - } - } - /* use a perfect square */ - if (maxv[0] > maxv[1]) { - maxv[1] = maxv[0]; - } - else { - maxv[0] = maxv[1]; - } -} - /* calc texture coordinates using flat projected points */ static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2], - int totpoints, + bGPDstroke *gps, const float minv[2], float maxv[2], float (*r_uv)[2]) { + const float s = sin(gps->uv_rotation); + const float c = cos(gps->uv_rotation); + + /* Calc center for rotation. */ + float center[2] = {0.5f, 0.5f}; float d[2]; d[0] = maxv[0] - minv[0]; d[1] = maxv[1] - minv[1]; - for (int i = 0; i < totpoints; i++) { + for (int i = 0; i < gps->totpoints; i++) { r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0]; r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1]; + + /* Apply translation. */ + add_v2_v2(r_uv[i], gps->uv_translation); + + /* Apply Rotation. */ + r_uv[i][0] -= center[0]; + r_uv[i][1] -= center[1]; + + float x = r_uv[i][0] * c - r_uv[i][1] * s; + float y = r_uv[i][0] * s + r_uv[i][1] * c; + + r_uv[i][0] = x + center[0]; + r_uv[i][1] = y + center[1]; + + /* Apply scale. */ + if (gps->uv_scale != 0.0f) { + mul_v2_fl(r_uv[i], 1.0f / gps->uv_scale); + } } } /* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was * modified) */ -void BKE_gpencil_triangulate_stroke_fill(bGPdata *gpd, bGPDstroke *gps) +void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps) { BLI_assert(gps->totpoints >= 3); @@ -2600,36 +2819,25 @@ void BKE_gpencil_triangulate_stroke_fill(bGPdata *gpd, bGPDstroke *gps) float minv[2]; float maxv[2]; /* first needs bounding box data */ - if (gpd->flag & GP_DATA_UV_ADAPTIVE) { - gpencil_calc_2d_bounding_box(points2d, gps->totpoints, minv, maxv); - } - else { - ARRAY_SET_ITEMS(minv, -1.0f, -1.0f); - ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f); - } + ARRAY_SET_ITEMS(minv, -1.0f, -1.0f); + ARRAY_SET_ITEMS(maxv, 1.0f, 1.0f); /* calc uv data */ - gpencil_calc_stroke_fill_uv(points2d, gps->totpoints, minv, maxv, uv); + gpencil_calc_stroke_fill_uv(points2d, gps, minv, maxv, uv); - /* Number of triangles */ - gps->tot_triangles = gps->totpoints - 2; - /* save triangulation data in stroke cache */ + /* Save triangulation data. */ if (gps->tot_triangles > 0) { - if (gps->triangles == NULL) { - gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, - "GP Stroke triangulation"); - } - else { - gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); - } + MEM_SAFE_FREE(gps->triangles); + gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, + "GP Stroke triangulation"); for (int i = 0; i < gps->tot_triangles; i++) { - bGPDtriangle *stroke_triangle = &gps->triangles[i]; memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3])); - /* copy texture coordinates */ - copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]); - copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]); - copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]); + } + + /* Copy UVs to bGPDspoint. */ + for (int i = 0; i < gps->totpoints; i++) { + copy_v2_v2(gps->points[i].uv_fill, uv[i]); } } else { @@ -2641,17 +2849,50 @@ void BKE_gpencil_triangulate_stroke_fill(bGPdata *gpd, bGPDstroke *gps) gps->triangles = NULL; } - /* disable recalculation flag */ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; - } - /* clear memory */ MEM_SAFE_FREE(tmp_triangles); MEM_SAFE_FREE(points2d); MEM_SAFE_FREE(uv); } +/* texture coordinate utilities */ +void BKE_gpencil_stroke_uv_update(bGPDstroke *gps) +{ + if (gps == NULL || gps->totpoints == 0) { + return; + } + + bGPDspoint *pt = gps->points; + float totlen = 0.0f; + pt[0].uv_fac = totlen; + for (int i = 1; i < gps->totpoints; i++) { + totlen += len_v3v3(&pt[i - 1].x, &pt[i].x); + pt[i].uv_fac = totlen; + } +} + +/* Recalc the internal geometry caches for fill and uvs. */ +void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + if (gps->totpoints > 2) { + BKE_gpencil_stroke_fill_triangulate(gps); + } + else { + gps->tot_triangles = 0; + MEM_SAFE_FREE(gps->triangles); + } + + /* calc uv data along the stroke */ + BKE_gpencil_stroke_uv_update(gps); + + /* Calc stroke bounding box. */ + BKE_gpencil_stroke_boundingbox_calc(gps); +} + float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d) { if (!gps->points || gps->totpoints < 2) { @@ -2678,7 +2919,7 @@ float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d) * Trim stroke to the first intersection or loop * \param gps: Stroke data */ -bool BKE_gpencil_trim_stroke(bGPDstroke *gps) +bool BKE_gpencil_stroke_trim(bGPDstroke *gps) { if (gps->totpoints < 4) { return false; @@ -2759,13 +3000,14 @@ bool BKE_gpencil_trim_stroke(bGPDstroke *gps) } } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; gps->totpoints = newtot; MEM_SAFE_FREE(old_points); MEM_SAFE_FREE(old_dvert); } + + BKE_gpencil_stroke_geometry_update(gps); + return intersect; } @@ -2773,7 +3015,7 @@ bool BKE_gpencil_trim_stroke(bGPDstroke *gps) * Close stroke * \param gps: Stroke to close */ -bool BKE_gpencil_close_stroke(bGPDstroke *gps) +bool BKE_gpencil_stroke_close(bGPDstroke *gps) { bGPDspoint *pt1 = NULL; bGPDspoint *pt2 = NULL; @@ -2831,6 +3073,7 @@ bool BKE_gpencil_close_stroke(bGPDstroke *gps) pt->pressure = interpf(pt2->pressure, pt1->pressure, step); pt->strength = interpf(pt2->strength, pt1->strength, step); pt->flag = 0; + interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step); /* Set weights. */ if (gps->dvert != NULL) { @@ -2933,8 +3176,7 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta gps->totpoints = tot; /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + BKE_gpencil_stroke_geometry_update(gps); } } @@ -2947,7 +3189,7 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta * \param threshold: Distance between points * \param use_unselected: Set to true to analyze all stroke and not only selected points */ -void BKE_gpencil_merge_distance_stroke(bGPDframe *gpf, +void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf, bGPDstroke *gps, const float threshold, const bool use_unselected) @@ -3014,6 +3256,9 @@ void BKE_gpencil_merge_distance_stroke(bGPDframe *gpf, if (tagged) { BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG); } + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* Helper: Check materials with same color. */ @@ -3033,7 +3278,8 @@ static int gpencil_check_same_material_color(Object *ob_gp, float color[4], Mate float hsv2[4]; rgb_to_hsv_v(gp_style->fill_rgba, hsv2); hsv2[3] = gp_style->fill_rgba[3]; - if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_SOLID) && (compare_v4v4(hsv1, hsv2, 0.01f))) { + if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) && + (compare_v4v4(hsv1, hsv2, 0.01f))) { *r_mat = ma; return i - 1; } @@ -3058,24 +3304,24 @@ static Material *gpencil_add_from_curve_material(Main *bmain, /* Stroke color. */ if (gpencil_lines) { ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f); - gp_style->flag |= GP_STYLE_STROKE_SHOW; + gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } else { linearrgb_to_srgb_v4(gp_style->stroke_rgba, cu_color); - gp_style->flag &= ~GP_STYLE_STROKE_SHOW; + gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; } /* Fill color. */ linearrgb_to_srgb_v4(gp_style->fill_rgba, cu_color); /* Fill is false if the original curve hasn't material assigned, so enable it. */ if (fill) { - gp_style->flag |= GP_STYLE_FILL_SHOW; + gp_style->flag |= GP_MATERIAL_FILL_SHOW; } /* Check at least one is enabled. */ - if (((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0) && - ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) { - gp_style->flag |= GP_STYLE_STROKE_SHOW; + if (((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0) && + ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) { + gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } return mat_gp; @@ -3140,13 +3386,14 @@ static void gpencil_convert_spline(Main *bmain, /* Create Stroke. */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); gps->thickness = 1.0f; - gps->gradient_f = 1.0f; - ARRAY_SET_ITEMS(gps->gradient_s, 1.0f, 1.0f); + gps->fill_opacity_fac = 1.0f; + gps->hardeness = 1.0f; + gps->uv_scale = 1.0f; + + ARRAY_SET_ITEMS(gps->aspect_ratio, 1.0f, 1.0f); ARRAY_SET_ITEMS(gps->caps, GP_STROKE_CAP_ROUND, GP_STROKE_CAP_ROUND); gps->inittime = 0.0f; - /* Enable recalculation flag by default. */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; gps->flag &= ~GP_STROKE_SELECT; gps->flag |= GP_STROKE_3DSPACE; @@ -3165,10 +3412,6 @@ static void gpencil_convert_spline(Main *bmain, } totpoints = (resolu * segments) - (segments - 1); - /* Initialize triangle memory to dummy data. */ - gps->tot_triangles = 0; - gps->triangles = NULL; - /* Materials * Notice: The color of the material is the color of viewport and not the final shader color. */ @@ -3212,7 +3455,6 @@ static void gpencil_convert_spline(Main *bmain, copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba); gp_style_gp->fill_style = gp_style_cur->fill_style; gp_style_gp->mix_factor = gp_style_cur->mix_factor; - gp_style_gp->gradient_angle = gp_style_cur->gradient_angle; } /* If object has more than 1 material, use second material for stroke color. */ @@ -3228,16 +3470,16 @@ static void gpencil_convert_spline(Main *bmain, if (ob_cu->totcol > 0) { mat_curve = BKE_object_material_get(ob_cu, 1); if (mat_curve) { - linearrgb_to_srgb_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r); + copy_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r); mat_gp->gp_style->stroke_rgba[3] = mat_curve->a; /* Set fill and stroke depending of curve type (3D or 2D). */ if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) { - mat_gp->gp_style->flag |= GP_STYLE_STROKE_SHOW; - mat_gp->gp_style->flag &= ~GP_STYLE_FILL_SHOW; + mat_gp->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + mat_gp->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; } else { - mat_gp->gp_style->flag &= ~GP_STYLE_STROKE_SHOW; - mat_gp->gp_style->flag |= GP_STYLE_FILL_SHOW; + mat_gp->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + mat_gp->gp_style->flag |= GP_MATERIAL_FILL_SHOW; } } } @@ -3349,8 +3591,11 @@ static void gpencil_convert_spline(Main *bmain, } /* Cyclic curve, close stroke. */ if ((cyclic) && (!do_stroke)) { - BKE_gpencil_close_stroke(gps); + BKE_gpencil_stroke_close(gps); } + + /* Recalc fill geometry. */ + BKE_gpencil_stroke_geometry_update(gps); } /* Convert a curve object to grease pencil stroke. @@ -3388,7 +3633,7 @@ void BKE_gpencil_convert_curve(Main *bmain, if (use_collections) { Collection *collection = gpencil_get_parent_collection(scene, ob_cu); if (collection != NULL) { - gpl = BLI_findstring(&gpd->layers, collection->id.name + 2, offsetof(bGPDlayer, info)); + gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2); if (gpl == NULL) { gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true); } @@ -3396,14 +3641,14 @@ void BKE_gpencil_convert_curve(Main *bmain, } if (gpl == NULL) { - gpl = BKE_gpencil_layer_getactive(gpd); + gpl = BKE_gpencil_layer_active_get(gpd); if (gpl == NULL) { gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } } /* Check if there is an active frame and add if needed. */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_COPY); /* Read all splines of the curve and create a stroke for each. */ for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) { @@ -3413,3 +3658,379 @@ void BKE_gpencil_convert_curve(Main *bmain, /* Tag for recalculation */ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); } + +/* Create a default palette */ +void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene) +{ + const int totcol = 120; + const char *hexcol[] = { + "FFFFFF", "F2F2F2", "E6E6E6", "D9D9D9", "CCCCCC", "BFBFBF", "B2B2B2", "A6A6A6", "999999", + "8C8C8C", "808080", "737373", "666666", "595959", "4C4C4C", "404040", "333333", "262626", + "1A1A1A", "000000", "F2FC24", "FFEA00", "FEA711", "FE8B68", "FB3B02", "FE3521", "D00000", + "A81F3D", "780422", "2B0000", "F1E2C5", "FEE4B3", "FEDABB", "FEC28E", "D88F57", "BD6340", + "A2402B", "63352D", "6B2833", "34120C", "E7CB8F", "D1B38B", "C1B17F", "D7980B", "FFB100", + "FE8B00", "FF6A00", "B74100", "5F3E1D", "3B2300", "FECADA", "FE65CB", "FE1392", "DD3062", + "C04A6D", "891688", "4D2689", "441521", "2C1139", "241422", "FFFF7D", "FFFF00", "FF7F00", + "FF7D7D", "FF7DFF", "FF00FE", "FF007F", "FF0000", "7F0000", "0A0A00", "F6FDFF", "E9F7FF", + "CFE6FE", "AAC7FE", "77B3FE", "1E74FD", "0046AA", "2F4476", "003052", "0E0E25", "EEF5F0", + "D6E5DE", "ACD8B9", "6CADC6", "42A9AF", "007F7F", "49675C", "2E4E4E", "1D3239", "0F1C21", + "D8FFF4", "B8F4F5", "AECCB5", "76C578", "358757", "409B68", "468768", "1F512B", "2A3C37", + "122E1D", "EFFFC9", "E6F385", "BCF51C", "D4DC18", "82D322", "5C7F00", "59932B", "297F00", + "004320", "1C3322", "00FF7F", "00FF00", "7DFF7D", "7DFFFF", "00FFFF", "7D7DFF", "7F00FF", + "0000FF", "3F007F", "00007F"}; + + ToolSettings *ts = scene->toolsettings; + GpPaint *gp_paint = ts->gp_paint; + Paint *paint = &gp_paint->paint; + + paint->palette = BLI_findstring(&bmain->palettes, "Palette", offsetof(ID, name) + 2); + if (paint->palette == NULL) { + paint->palette = BKE_palette_add(bmain, "Palette"); + ts->gp_vertexpaint->paint.palette = paint->palette; + + /* Create Colors. */ + for (int i = 0; i < totcol; i++) { + PaletteColor *palcol = BKE_palette_color_add(paint->palette); + if (palcol) { + hex_to_rgb((char *)hexcol[i], palcol->rgb, palcol->rgb + 1, palcol->rgb + 2); + } + } + } +} + +bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size, const bool mask) +{ + Image *image = sima->image; + bool done = false; + + if (image == NULL) { + return false; + } + + ImageUser iuser = sima->iuser; + void *lock; + ImBuf *ibuf; + + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf->rect) { + int img_x = ibuf->x; + int img_y = ibuf->y; + + float color[4]; + bGPDspoint *pt; + for (int row = 0; row < img_y; row++) { + /* Create new stroke */ + bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, 0, img_x, size * 1000, false); + done = true; + for (int col = 0; col < img_x; col++) { + IMB_sampleImageAtLocation(ibuf, col, row, true, color); + pt = &gps->points[col]; + pt->pressure = 1.0f; + pt->x = col * size; + pt->z = row * size; + if (!mask) { + copy_v3_v3(pt->vert_color, color); + pt->vert_color[3] = 1.0f; + pt->strength = color[3]; + } + else { + zero_v3(pt->vert_color); + pt->vert_color[3] = 1.0f; + pt->strength = 1.0f - color[3]; + } + + /* Selet Alpha points. */ + if (pt->strength < 0.03f) { + gps->flag |= GP_STROKE_SELECT; + pt->flag |= GP_SPOINT_SELECT; + } + } + BKE_gpencil_stroke_geometry_update(gps); + } + } + + /* Free memory. */ + BKE_image_release_ibuf(image, ibuf, lock); + + return done; +} + +/* -------------------------------------------------------------------- */ +/** \name Iterators + * + * Iterate over all visible stroke of all visible layers inside a gpObject. + * Also take into account onion skining. + * + * \{ */ + +void BKE_gpencil_visible_stroke_iter( + Object *ob, gpIterCb layer_cb, gpIterCb stroke_cb, void *thunk, bool do_onion, int cfra) +{ + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_multiedit = GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_onion = do_onion && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0); + + /* Onion skinning. */ + const bool onion_mode_abs = (gpd->onion_mode == GP_ONION_MODE_ABSOLUTE); + const bool onion_mode_sel = (gpd->onion_mode == GP_ONION_MODE_SELECTED); + const bool onion_loop = (gpd->onion_flag & GP_ONION_LOOP) != 0; + const short onion_keytype = gpd->onion_keytype; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + + bGPDframe *act_gpf = gpl->actframe; + bGPDframe *sta_gpf = act_gpf; + bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL; + + if (gpl->flag & GP_LAYER_HIDE) { + continue; + } + + if (is_multiedit) { + sta_gpf = end_gpf = NULL; + /* Check the whole range and tag the editable frames. */ + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + if (gpf == act_gpf || (gpf->flag & GP_FRAME_SELECT)) { + gpf->runtime.onion_id = 0; + if (sta_gpf == NULL) { + sta_gpf = gpf; + } + end_gpf = gpf->next; + } + else { + gpf->runtime.onion_id = INT_MAX; + } + } + } + else if (is_onion && (gpl->onion_flag & GP_LAYER_ONIONSKIN)) { + if (act_gpf) { + bGPDframe *last_gpf = gpl->frames.last; + + int frame_len = 0; + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + gpf->runtime.frameid = frame_len++; + } + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + bool is_wrong_keytype = (onion_keytype > -1) && (gpf->key_type != onion_keytype); + bool is_in_range; + int delta = (onion_mode_abs) ? (gpf->framenum - cfra) : + (gpf->runtime.frameid - act_gpf->runtime.frameid); + + if (onion_mode_sel) { + is_in_range = (gpf->flag & GP_FRAME_SELECT) != 0; + } + else { + is_in_range = (-delta <= gpd->gstep) && (delta <= gpd->gstep_next); + + if (onion_loop && !is_in_range) { + /* We wrap the value using the last frame and 0 as reference. */ + /* FIXME: This might not be good for animations not starting at 0. */ + int shift = (onion_mode_abs) ? last_gpf->framenum : last_gpf->runtime.frameid; + delta += (delta < 0) ? (shift + 1) : -(shift + 1); + /* Test again with wrapped value. */ + is_in_range = (-delta <= gpd->gstep) && (delta <= gpd->gstep_next); + } + } + /* Mask frames that have wrong keytype of are not in range. */ + gpf->runtime.onion_id = (is_wrong_keytype || !is_in_range) ? INT_MAX : delta; + } + /* Active frame is always shown. */ + act_gpf->runtime.onion_id = 0; + } + + sta_gpf = gpl->frames.first; + end_gpf = NULL; + } + else { + /* Bypass multiedit/onion skinning. */ + end_gpf = sta_gpf = NULL; + } + + if (sta_gpf == NULL && act_gpf == NULL) { + if (layer_cb) { + layer_cb(gpl, act_gpf, NULL, thunk); + } + continue; + } + + /* Draw multiedit/onion skinning first */ + for (bGPDframe *gpf = sta_gpf; gpf && gpf != end_gpf; gpf = gpf->next) { + if (gpf->runtime.onion_id == INT_MAX || gpf == act_gpf) { + continue; + } + + if (layer_cb) { + layer_cb(gpl, gpf, NULL, thunk); + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + stroke_cb(gpl, gpf, gps, thunk); + } + } + /* Draw Active frame on top. */ + /* Use evaluated frame (with modifiers for active stroke)/ */ + act_gpf = gpl->actframe; + act_gpf->runtime.onion_id = 0; + if (act_gpf) { + if (layer_cb) { + layer_cb(gpl, act_gpf, NULL, thunk); + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) { + stroke_cb(gpl, act_gpf, gps, thunk); + } + } + } +} + +void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig, + const struct bGPDframe *gpf_eval) +{ + bGPDstroke *gps_eval = gpf_eval->strokes.first; + LISTBASE_FOREACH (bGPDstroke *, gps_orig, &gpf_orig->strokes) { + + /* Assign original stroke pointer. */ + if (gps_eval != NULL) { + gps_eval->runtime.gps_orig = gps_orig; + + /* Assign original point pointer. */ + for (int i = 0; i < gps_orig->totpoints; i++) { + bGPDspoint *pt_eval = &gps_eval->points[i]; + pt_eval->runtime.pt_orig = &gps_orig->points[i]; + pt_eval->runtime.idx_orig = i; + } + /* Increase pointer. */ + gps_eval = gps_eval->next; + } + } +} + +void BKE_gpencil_update_orig_pointers(const Object *ob_orig, const Object *ob_eval) +{ + bGPdata *gpd_eval = (bGPdata *)ob_eval->data; + bGPdata *gpd_orig = (bGPdata *)ob_orig->data; + + /* Assign pointers to the original stroke and points to the evaluated data. This must + * be done before applying any modifier because at this moment the structure is equals, + * so we can assume the layer index is the same in both datablocks. + * This data will be used by operators. */ + + bGPDlayer *gpl_eval = gpd_eval->layers.first; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) { + if (gpl_eval != NULL) { + /* Update layer reference pointers. */ + gpl_eval->runtime.gpl_orig = (bGPDlayer *)gpl; + + bGPDframe *gpf_eval = gpl_eval->frames.first; + LISTBASE_FOREACH (bGPDframe *, gpf_orig, &gpl->frames) { + if (gpf_eval != NULL) { + /* Update frame reference pointers. */ + gpf_eval->runtime.gpf_orig = (bGPDframe *)gpf_orig; + BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval); + gpf_eval = gpf_eval->next; + } + } + gpl_eval = gpl_eval->next; + } + } +} + +void BKE_gpencil_parent_matrix_get(const Depsgraph *depsgraph, + Object *obact, + bGPDlayer *gpl, + float diff_mat[4][4]) +{ + Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; + Object *obparent = gpl->parent; + Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : + obparent; + + /* if not layer parented, try with object parented */ + if (obparent_eval == NULL) { + if (ob_eval != NULL) { + if (ob_eval->type == OB_GPENCIL) { + copy_m4_m4(diff_mat, ob_eval->obmat); + return; + } + } + /* not gpencil object */ + unit_m4(diff_mat); + return; + } + else { + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); + add_v3_v3(diff_mat[3], ob_eval->obmat[3]); + return; + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat); + mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); + add_v3_v3(diff_mat[3], ob_eval->obmat[3]); + } + else { + /* if bone not found use object (armature) */ + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); + add_v3_v3(diff_mat[3], ob_eval->obmat[3]); + } + return; + } + else { + unit_m4(diff_mat); /* not defined type */ + } + } +} + +void BKE_gpencil_update_layer_parent(const Depsgraph *depsgraph, Object *ob) +{ + if (ob->type != OB_GPENCIL) { + return; + } + + bGPdata *gpd = (bGPdata *)ob->data; + bGPDspoint *pt; + int i; + float diff_mat[4][4]; + float cur_mat[4][4]; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if ((gpl->parent != NULL) && (gpl->actframe != NULL)) { + Object *ob_eval = DEG_get_evaluated_object(depsgraph, gpl->parent); + + /* calculate new matrix */ + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + invert_m4_m4(cur_mat, ob_eval->obmat); + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_eval->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, ob_eval->obmat, pchan->pose_mat); + invert_m4_m4(cur_mat, tmp_mat); + } + } + /* only redo if any change */ + if (!equals_m4m4(gpl->inverse, cur_mat)) { + + /* first apply current transformation to all strokes */ + BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); + /* undo local object */ + sub_v3_v3(diff_mat[3], ob->obmat[3]); + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + mul_m4_v3(diff_mat, &pt->x); + } + } + /* set new parent matrix */ + copy_m4_m4(gpl->inverse, cur_mat); + } + } + } +} +/** \} */ diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index ebb927a7d60..1014db27d84 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -96,7 +96,7 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3]) * Ramer - Douglas - Peucker algorithm * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm */ -void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float epsilon) +void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon) { bGPDspoint *old_points = MEM_dupallocN(gps->points); int totpoints = gps->totpoints; @@ -165,9 +165,6 @@ void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float epsilon) old_dvert = MEM_dupallocN(gps->dvert); } /* resize gps */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - int j = 0; for (int i = 0; i < totpoints; i++) { bGPDspoint *pt_src = &old_points[i]; @@ -195,13 +192,16 @@ void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float epsilon) gps->totpoints = j; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + MEM_SAFE_FREE(old_points); MEM_SAFE_FREE(old_dvert); MEM_SAFE_FREE(marked); } /* Simplify alternate vertex of stroke except extremes */ -void BKE_gpencil_simplify_fixed(bGPDstroke *gps) +void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps) { if (gps->totpoints < 5) { return; @@ -227,8 +227,6 @@ void BKE_gpencil_simplify_fixed(bGPDstroke *gps) if (gps->dvert != NULL) { gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; int j = 0; for (int i = 0; i < gps->totpoints; i++) { @@ -256,6 +254,8 @@ void BKE_gpencil_simplify_fixed(bGPDstroke *gps) } gps->totpoints = j; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); MEM_SAFE_FREE(old_points); MEM_SAFE_FREE(old_dvert); @@ -357,73 +357,8 @@ bool BKE_gpencil_has_transform_modifiers(Object *ob) return false; } -/* apply stroke modifiers */ -void BKE_gpencil_stroke_modifiers(Depsgraph *depsgraph, - Object *ob, - bGPDlayer *gpl, - bGPDframe *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)) && (!is_render)) { - continue; - } - - if (mti && mti->deformStroke) { - mti->deformStroke(md, depsgraph, ob, gpl, gpf, gps); - /* subdivide always requires update */ - if (md->type == eGpencilModifierType_Subdiv) { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - } - /* some modifiers could require a recalc of fill triangulation data */ - else if (gpd->flag & GP_DATA_STROKE_FORCE_RECALC) { - if (ELEM(md->type, - eGpencilModifierType_Armature, - eGpencilModifierType_Hook, - eGpencilModifierType_Lattice, - eGpencilModifierType_Offset)) { - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - } - } - } - } - } -} - -/* 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)) && (!is_render)) { - continue; - } - - if (mti->generateStrokes) { - mti->generateStrokes(md, depsgraph, ob, gpl, gpf); - } - } - } -} - /* apply time modifiers */ -int BKE_gpencil_time_modifier( +static int gpencil_time_modifier( Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render) { GpencilModifierData *md; @@ -454,14 +389,14 @@ int BKE_gpencil_time_modifier( } /* *************************************************** */ -void BKE_gpencil_eval_geometry(Depsgraph *depsgraph, bGPdata *gpd) +void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd) { DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd); int ctime = (int)DEG_get_ctime(depsgraph); /* update active frame */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV); } if (DEG_is_active(depsgraph)) { @@ -471,8 +406,8 @@ void BKE_gpencil_eval_geometry(Depsgraph *depsgraph, bGPdata *gpd) * 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); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) { + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, ctime, GP_GETFRAME_USE_PREV); } } } @@ -687,7 +622,7 @@ GpencilModifierData *BKE_gpencil_modifiers_findByName(Object *ob, const char *na return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name)); } -void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) +void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type) { bGPDspoint *temp_points; MDeformVert *temp_dverts = NULL; @@ -710,8 +645,6 @@ void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) temp_dverts = MEM_dupallocN(gps->dvert); gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; /* move points from last to first to new place */ i2 = gps->totpoints - 1; @@ -726,6 +659,7 @@ void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) pt_final->flag = pt->flag; pt_final->runtime.pt_orig = pt->runtime.pt_orig; pt_final->runtime.idx_orig = pt->runtime.idx_orig; + copy_v4_v4(pt_final->vert_color, pt->vert_color); if (gps->dvert != NULL) { dvert = &temp_dverts[i]; @@ -749,6 +683,7 @@ void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->runtime.pt_orig = NULL; + interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); if (gps->dvert != NULL) { dvert = &temp_dverts[i]; @@ -775,8 +710,8 @@ void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) MEM_SAFE_FREE(temp_points); MEM_SAFE_FREE(temp_dverts); - /* move points to smooth stroke (not simple flag )*/ - if ((flag & GP_SUBDIV_SIMPLE) == 0) { + /* move points to smooth stroke (not simple type )*/ + if (type != GP_SUBDIV_SIMPLE) { /* duplicate points in a temp area with the new subdivide data */ temp_points = MEM_dupallocN(gps->points); @@ -793,145 +728,186 @@ void BKE_gpencil_subdivide(bGPDstroke *gps, int level, int flag) MEM_SAFE_FREE(temp_points); } } + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); +} + +/* Remap frame (Time modifier) */ +static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl) +{ + const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + const bool time_remap = BKE_gpencil_has_time_modifiers(ob); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + int remap_cfra = cfra_eval; + if (time_remap) { + remap_cfra = gpencil_time_modifier(depsgraph, scene, ob, gpl, cfra_eval, is_render); + } + + return remap_cfra; } -/* Copy frame but do not assign new memory */ -static void gpencil_frame_copy_noalloc(Object *ob, bGPDframe *gpf, bGPDframe *gpf_eval) +/* Get the current frame retimed with time modifiers. */ +bGPDframe *BKE_gpencil_frame_retime_get(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + bGPDlayer *gpl) { - gpf_eval->prev = gpf->prev; - gpf_eval->next = gpf->next; - gpf_eval->framenum = gpf->framenum; - gpf_eval->flag = gpf->flag; - gpf_eval->key_type = gpf->key_type; - gpf_eval->runtime = gpf->runtime; - copy_m4_m4(gpf_eval->runtime.parent_obmat, gpf->runtime.parent_obmat); + int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, remap_cfra, GP_GETFRAME_USE_PREV); - /* copy strokes */ - BLI_listbase_clear(&gpf_eval->strokes); - for (bGPDstroke *gps_src = gpf->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + return gpf; +} - /* copy color to temp fields to apply temporal changes in the stroke */ - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps_src->mat_nr + 1); - if (gp_style) { - copy_v4_v4(gps_dst->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps_dst->runtime.tmp_fill_rgba, gp_style->fill_rgba); - } +static void gpencil_assign_object_eval(Object *object) +{ + BLI_assert(object->id.tag & LIB_TAG_COPIED_ON_WRITE); - /* Save original pointers for using in edit and select operators. */ - gps_dst->runtime.gps_orig = gps_src; - for (int i = 0; i < gps_src->totpoints; i++) { - bGPDspoint *pt_dst = &gps_dst->points[i]; - pt_dst->runtime.pt_orig = &gps_src->points[i]; - pt_dst->runtime.idx_orig = i; - } + bGPdata *gpd_eval = object->runtime.gpd_eval; + + gpd_eval->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; - BLI_addtail(&gpf_eval->strokes, gps_dst); + if (object->id.tag & LIB_TAG_COPIED_ON_WRITE) { + object->data = gpd_eval; } } -/* Ensure there is a evaluated frame */ -static void gpencil_evaluated_frame_ensure(int idx, - Object *ob, - bGPDframe *gpf, - bGPDframe **gpf_eval) +/* Helper: Copy active frame from original datablock to evaluated datablock for modifiers. */ +static void gpencil_copy_activeframe_to_eval( + Depsgraph *depsgraph, Scene *scene, Object *ob, bGPdata *gpd_orig, bGPdata *gpd_eval) { - /* Create evaluated frames array data or expand. */ - bGPDframe *evaluated_frames = ob->runtime.gpencil_evaluated_frames; - *gpf_eval = &evaluated_frames[idx]; - /* If already exist a evaluated frame create a new one. */ - if (*gpf_eval != NULL) { - /* first clear temp data */ - BKE_gpencil_free_frame_runtime_data(*gpf_eval); + bGPDlayer *gpl_eval = gpd_eval->layers.first; + LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd_orig->layers) { + + if (gpl_eval != NULL) { + int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_orig); + + bGPDframe *gpf_orig = BKE_gpencil_layer_frame_get( + gpl_orig, remap_cfra, GP_GETFRAME_USE_PREV); + + if (gpf_orig != NULL) { + int gpf_index = BLI_findindex(&gpl_orig->frames, gpf_orig); + bGPDframe *gpf_eval = BLI_findlink(&gpl_eval->frames, gpf_index); + + if (gpf_eval != NULL) { + /* Delete old strokes. */ + BKE_gpencil_free_strokes(gpf_eval); + /* Copy again strokes. */ + BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval); + + gpf_eval->runtime.gpf_orig = (bGPDframe *)gpf_orig; + BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval); + } + } + + gpl_eval = gpl_eval->next; + } } - /* Copy data (do not assign new memory). */ - gpencil_frame_copy_noalloc(ob, gpf, *gpf_eval); } -/* Calculate gpencil modifiers */ -void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) +static bGPdata *gpencil_copy_for_eval(bGPdata *gpd) { - /* use original data to set reference pointers to original data */ - Object *ob_orig = DEG_get_original_object(ob); - bGPdata *gpd = (bGPdata *)ob_orig->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const bool simplify_modif = GPENCIL_SIMPLIFY_MODIF(scene, false); - const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); - const bool time_remap = BKE_gpencil_has_time_modifiers(ob); - int cfra_eval = (int)DEG_get_ctime(depsgraph); + int flags = LIB_ID_COPY_LOCALIZE; + + bGPdata *result; + BKE_id_copy_ex(NULL, &gpd->id, (ID **)&result, flags); + return result; +} - /* Clear any previous evaluated data. */ - if (ob->runtime.gpencil_tot_layers > 0) { - for (int i = 0; i < ob->runtime.gpencil_tot_layers; i++) { - bGPDframe *gpf_eval = &ob->runtime.gpencil_evaluated_frames[i]; - BKE_gpencil_free_frame_runtime_data(gpf_eval); +void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + bGPdata *gpd_eval = (bGPdata *)ob->data; + Object *ob_orig = (Object *)DEG_get_original_id(&ob->id); + bGPdata *gpd_orig = (bGPdata *)ob_orig->data; + + /* Need check if some layer is parented. */ + bool do_parent = false; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_orig->layers) { + if (gpl->parent != NULL) { + do_parent = true; + break; } } - /* Create array of evaluated frames equal to number of layers. */ - ob->runtime.gpencil_tot_layers = BLI_listbase_count(&gpd->layers); - CLAMP_MIN(ob->runtime.gpencil_tot_layers, 1); - if (ob->runtime.gpencil_evaluated_frames == NULL) { - ob->runtime.gpencil_evaluated_frames = MEM_callocN( - sizeof(struct bGPDframe) * ob->runtime.gpencil_tot_layers, __func__); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval); + const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) && + (!GPENCIL_SIMPLIFY_MODIF(scene))); + if ((!do_modifiers) && (!do_parent)) { + return; + } + DEG_debug_print_eval(depsgraph, __func__, gpd_eval->id.name, gpd_eval); + + /* If only one user, don't need a new copy, just update data of the frame. */ + if (gpd_orig->id.us == 1) { + ob->runtime.gpd_eval = NULL; + gpencil_copy_activeframe_to_eval(depsgraph, scene, ob, ob_orig->data, gpd_eval); + return; } - else { - ob->runtime.gpencil_evaluated_frames = MEM_recallocN(ob->runtime.gpencil_evaluated_frames, - sizeof(struct bGPDframe) * - ob->runtime.gpencil_tot_layers); + + /* Copy full Datablock to evaluated version. */ + ob->runtime.gpd_orig = gpd_orig; + if (ob->runtime.gpd_eval != NULL) { + BKE_gpencil_eval_delete(ob->runtime.gpd_eval); + ob->runtime.gpd_eval = NULL; + ob->data = ob->runtime.gpd_orig; + } + ob->runtime.gpd_eval = gpencil_copy_for_eval(ob->runtime.gpd_orig); + gpencil_assign_object_eval(ob); + BKE_gpencil_update_orig_pointers(ob_orig, (Object *)ob); +} + +/* Calculate gpencil modifiers */ +void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + bGPdata *gpd = (bGPdata *)ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); + const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) && + (!GPENCIL_SIMPLIFY_MODIF(scene))); + if (!do_modifiers) { + return; } /* Init general modifiers data. */ - if (ob->greasepencil_modifiers.first) { - BKE_gpencil_lattice_init(ob); - } - - /* ***************************************************************** - * Loop all layers, duplicate data and apply modifiers. - * - * ******************************************************************/ - int idx = 0; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* Remap frame (Time modifier) */ - int remap_cfra = cfra_eval; - if ((time_remap) && (!simplify_modif)) { - remap_cfra = BKE_gpencil_time_modifier(depsgraph, scene, ob, gpl, cfra_eval, is_render); - } - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, remap_cfra, GP_GETFRAME_USE_PREV); + BKE_gpencil_lattice_init(ob); - if (gpf == NULL) { - idx++; - continue; - } + const bool time_remap = BKE_gpencil_has_time_modifiers(ob); - /* Create a duplicate data set of stroke to modify. */ - bGPDframe *gpf_eval = NULL; - gpencil_evaluated_frame_ensure(idx, ob, gpf, &gpf_eval); + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { - /* Skip all if some disable flag is enabled. */ - if ((ob->greasepencil_modifiers.first == NULL) || (is_multiedit) || (simplify_modif)) { - idx++; - continue; - } + if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); - /* Apply geometry modifiers (create new geometry). */ - if (BKE_gpencil_has_geometry_modifiers(ob)) { - BKE_gpencil_geometry_modifiers(depsgraph, ob, gpl, gpf_eval, is_render); - } + if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) { + continue; + } - /* Loop all strokes and deform them. */ - for (bGPDstroke *gps = gpf_eval->strokes.first; gps; gps = gps->next) { - /* Apply modifiers that only deform geometry */ - BKE_gpencil_stroke_modifiers(depsgraph, ob, gpl, gpf_eval, gps, is_render); - } + /* Apply geometry modifiers (add new geometry). */ + if (mti && mti->generateStrokes) { + mti->generateStrokes(md, depsgraph, ob); + } - idx++; + /* Apply deform modifiers and Time remap (only change geometry). */ + if ((time_remap) || (mti && mti->deformStroke)) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + + if (mti->deformStroke) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + mti->deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } + } + } } /* Clear any lattice data. */ - if (ob->greasepencil_modifiers.first) { - BKE_gpencil_lattice_clear(ob); - } + BKE_gpencil_lattice_clear(ob); } diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index e1868d88351..ee27d0e546d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -738,6 +738,15 @@ static void library_foreach_ID_link(Main *bmain, if (toolsett->gp_paint) { library_foreach_paint(&data, &toolsett->gp_paint->paint); } + if (toolsett->gp_vertexpaint) { + library_foreach_paint(&data, &toolsett->gp_vertexpaint->paint); + } + if (toolsett->gp_sculptpaint) { + library_foreach_paint(&data, &toolsett->gp_sculptpaint->paint); + } + if (toolsett->gp_weightpaint) { + library_foreach_paint(&data, &toolsett->gp_weightpaint->paint); + } CALLBACK_INVOKE(toolsett->gp_sculpt.guide.reference_object, IDWALK_CB_NOP); } diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 10f32a6b0bc..22d7c505e60 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -168,15 +168,12 @@ void BKE_gpencil_material_attr_init(Material *ma) /* set basic settings */ gp_style->stroke_rgba[3] = 1.0f; gp_style->fill_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; - gp_style->flag |= GP_STYLE_STROKE_SHOW; + gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e4a3174c908..211cb633881 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -729,8 +729,11 @@ void BKE_object_free_derived_caches(Object *ob) BKE_object_to_mesh_clear(ob); BKE_object_free_curve_cache(ob); - /* clear grease pencil data */ - DRW_gpencil_freecache(ob); + /* Clear grease pencil data. */ + if (ob->runtime.gpd_eval != NULL) { + BKE_gpencil_eval_delete(ob->runtime.gpd_eval); + ob->runtime.gpd_eval = NULL; + } } void BKE_object_free_caches(Object *object) @@ -803,6 +806,9 @@ bool BKE_object_is_in_editmode(const Object *ob) case OB_SURF: case OB_CURVE: return ((Curve *)ob->data)->editnurb != NULL; + case OB_GPENCIL: + /* Grease Pencil object has no edit mode data. */ + return GPENCIL_EDIT_MODE((bGPdata *)ob->data); default: return false; } @@ -1034,6 +1040,10 @@ static void object_init(Object *ob, const short ob_type) ob->trackflag = OB_NEGZ; ob->upflag = OB_POSY; } + + if (ob->type == OB_GPENCIL) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } } void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) @@ -3963,7 +3973,6 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) runtime->data_eval = NULL; runtime->mesh_deform_eval = NULL; runtime->curve_cache = NULL; - runtime->gpencil_cache = NULL; } /* diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index c647afdd00a..20b2ab9409f 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -24,6 +24,7 @@ #include "DNA_anim_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_gpencil_types.h" #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" @@ -150,6 +151,11 @@ void BKE_object_eval_transform_final(Depsgraph *depsgraph, Object *ob) else { ob->transflag &= ~OB_NEG_SCALE; } + + /* Assign evaluated version. */ + if ((ob->type == OB_GPENCIL) && (ob->runtime.gpd_eval != NULL)) { + ob->data = ob->runtime.gpd_eval; + } } void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -213,9 +219,12 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o case OB_LATTICE: BKE_lattice_modifiers_calc(depsgraph, scene, ob); break; - case OB_GPENCIL: + case OB_GPENCIL: { + BKE_gpencil_prepare_eval_data(depsgraph, scene, ob); BKE_gpencil_modifiers_calc(depsgraph, scene, ob); + BKE_gpencil_update_layer_parent(depsgraph, ob); break; + } } /* particles */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 35683ca93f4..e6042b6ad4c 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -215,6 +215,15 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) case PAINT_MODE_GPENCIL: paint_ptr = (Paint **)&ts->gp_paint; break; + case PAINT_MODE_VERTEX_GPENCIL: + paint_ptr = (Paint **)&ts->gp_vertexpaint; + break; + case PAINT_MODE_SCULPT_GPENCIL: + paint_ptr = (Paint **)&ts->gp_sculptpaint; + break; + case PAINT_MODE_WEIGHT_GPENCIL: + paint_ptr = (Paint **)&ts->gp_weightpaint; + break; case PAINT_MODE_INVALID: break; } @@ -244,6 +253,12 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->uvsculpt->paint; case PAINT_MODE_GPENCIL: return &ts->gp_paint->paint; + case PAINT_MODE_VERTEX_GPENCIL: + return &ts->gp_vertexpaint->paint; + case PAINT_MODE_SCULPT_GPENCIL: + return &ts->gp_sculptpaint->paint; + case PAINT_MODE_WEIGHT_GPENCIL: + return &ts->gp_weightpaint->paint; case PAINT_MODE_INVALID: return NULL; default: @@ -270,6 +285,12 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode) return rna_enum_brush_uv_sculpt_tool_items; case PAINT_MODE_GPENCIL: return rna_enum_brush_gpencil_types_items; + case PAINT_MODE_VERTEX_GPENCIL: + return rna_enum_brush_gpencil_vertex_types_items; + case PAINT_MODE_SCULPT_GPENCIL: + return rna_enum_brush_gpencil_sculpt_types_items; + case PAINT_MODE_WEIGHT_GPENCIL: + return rna_enum_brush_gpencil_weight_types_items; case PAINT_MODE_INVALID: break; } @@ -292,6 +313,12 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode) return "uv_sculpt_tool"; case PAINT_MODE_GPENCIL: return "gpencil_tool"; + case PAINT_MODE_VERTEX_GPENCIL: + return "gpencil_vertex_tool"; + case PAINT_MODE_SCULPT_GPENCIL: + return "gpencil_sculpt_tool"; + case PAINT_MODE_WEIGHT_GPENCIL: + return "gpencil_weight_tool"; default: /* invalid paint mode */ return NULL; @@ -315,6 +342,12 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->imapaint.paint; case OB_MODE_PAINT_GPENCIL: return &ts->gp_paint->paint; + case OB_MODE_VERTEX_GPENCIL: + return &ts->gp_vertexpaint->paint; + case OB_MODE_SCULPT_GPENCIL: + return &ts->gp_sculptpaint->paint; + case OB_MODE_WEIGHT_GPENCIL: + return &ts->gp_weightpaint->paint; case OB_MODE_EDIT: return &ts->uvsculpt->paint; default: @@ -429,6 +462,12 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) return PAINT_MODE_GPENCIL; case CTX_MODE_PAINT_TEXTURE: return PAINT_MODE_TEXTURE_3D; + case CTX_MODE_VERTEX_GPENCIL: + return PAINT_MODE_VERTEX_GPENCIL; + case CTX_MODE_SCULPT_GPENCIL: + return PAINT_MODE_SCULPT_GPENCIL; + case CTX_MODE_WEIGHT_GPENCIL: + return PAINT_MODE_WEIGHT_GPENCIL; } } else if (tref->space_type == SPACE_IMAGE) { @@ -485,6 +524,18 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) paint->runtime.tool_offset = offsetof(Brush, gpencil_tool); paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL; } + else if (paint == &ts->gp_vertexpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool); + paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL; + } + else if (paint == &ts->gp_sculptpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL; + } + else if (paint == &ts->gp_weightpaint->paint) { + paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); + paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL; + } else { BLI_assert(0); } @@ -506,6 +557,12 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode) return offsetof(Brush, uv_sculpt_tool); case PAINT_MODE_GPENCIL: return offsetof(Brush, gpencil_tool); + case PAINT_MODE_VERTEX_GPENCIL: + return offsetof(Brush, gpencil_vertex_tool); + case PAINT_MODE_SCULPT_GPENCIL: + return offsetof(Brush, gpencil_sculpt_tool); + case PAINT_MODE_WEIGHT_GPENCIL: + return offsetof(Brush, gpencil_weight_tool); case PAINT_MODE_INVALID: break; /* We don't use these yet. */ } @@ -639,6 +696,204 @@ bool BKE_palette_is_empty(const struct Palette *palette) return BLI_listbase_is_empty(&palette->colors); } +/* helper function to sort using qsort */ +static int palettecolor_compare_hsv(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = a1, *ps2 = a2; + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + else if (ps1->h < ps2->h) { + return -1; + } + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + else if (ps1->s < ps2->s) { + return -1; + } + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + else if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + return 0; +} + +/* helper function to sort using qsort */ +static int palettecolor_compare_svh(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = a1, *ps2 = a2; + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + else if (ps1->s < ps2->s) { + return -1; + } + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + else if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + else if (ps1->h < ps2->h) { + return -1; + } + + return 0; +} + +static int palettecolor_compare_vhs(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = a1, *ps2 = a2; + + /* Value. */ + if (1.0f - ps1->v > 1.0f - ps2->v) { + return 1; + } + else if (1.0f - ps1->v < 1.0f - ps2->v) { + return -1; + } + + /* Hue */ + if (ps1->h > ps2->h) { + return 1; + } + else if (ps1->h < ps2->h) { + return -1; + } + + /* Saturation. */ + if (ps1->s > ps2->s) { + return 1; + } + else if (ps1->s < ps2->s) { + return -1; + } + + return 0; +} + +static int palettecolor_compare_luminance(const void *a1, const void *a2) +{ + const tPaletteColorHSV *ps1 = a1, *ps2 = a2; + + float lumi1 = (ps1->rgb[0] + ps1->rgb[1] + ps1->rgb[2]) / 3.0f; + float lumi2 = (ps2->rgb[0] + ps2->rgb[1] + ps2->rgb[2]) / 3.0f; + + if (lumi1 > lumi2) { + return -1; + } + else if (lumi1 < lumi2) { + return 1; + } + + return 0; +} + +void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Hue , Saturation and Value. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv); +} + +void BKE_palette_sort_svh(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Saturation, Value and Hue. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_svh); +} + +void BKE_palette_sort_vhs(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Saturation, Value and Hue. */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_vhs); +} + +void BKE_palette_sort_luminance(tPaletteColorHSV *color_array, const int totcol) +{ + /* Sort by Luminance (calculated with the average, enough for sorting). */ + qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_luminance); +} + +bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, const bool linear) +{ + tPaletteColorHSV *color_array = NULL; + tPaletteColorHSV *col_elm = NULL; + bool done = false; + + const int totpal = BLI_ghash_len(color_table); + + if (totpal > 0) { + color_array = MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__); + /* Put all colors in an array. */ + GHashIterator gh_iter; + int t = 0; + GHASH_ITER (gh_iter, color_table) { + const uint col = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter)); + float r, g, b; + float h, s, v; + cpack_to_rgb(col, &r, &g, &b); + rgb_to_hsv(r, g, b, &h, &s, &v); + + col_elm = &color_array[t]; + col_elm->rgb[0] = r; + col_elm->rgb[1] = g; + col_elm->rgb[2] = b; + col_elm->h = h; + col_elm->s = s; + col_elm->v = v; + t++; + } + } + + /* Create the Palette. */ + if (totpal > 0) { + /* Sort by Hue and saturation. */ + BKE_palette_sort_hsv(color_array, totpal); + + Palette *palette = BKE_palette_add(bmain, name); + if (palette) { + for (int i = 0; i < totpal; i++) { + col_elm = &color_array[i]; + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, col_elm->rgb); + if (linear) { + linearrgb_to_srgb_v3_v3(palcol->rgb, palcol->rgb); + } + } + } + done = true; + } + } + else { + done = false; + } + + if (totpal > 0) { + MEM_SAFE_FREE(color_array); + } + + return done; +} + /* are we in vertex paint or weight paint face select mode? */ bool BKE_paint_select_face_test(Object *ob) { @@ -720,6 +975,9 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) BLI_assert(ELEM(*r_paint, /* Cast is annoying, but prevent NULL-pointer access. */ (Paint *)ts->gp_paint, + (Paint *)ts->gp_vertexpaint, + (Paint *)ts->gp_sculptpaint, + (Paint *)ts->gp_weightpaint, (Paint *)ts->sculpt, (Paint *)ts->vpaint, (Paint *)ts->wpaint, @@ -755,6 +1013,18 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) GpPaint *data = MEM_callocN(sizeof(*data), __func__); paint = &data->paint; } + else if ((GpVertexPaint **)r_paint == &ts->gp_vertexpaint) { + GpVertexPaint *data = MEM_callocN(sizeof(*data), __func__); + paint = &data->paint; + } + else if ((GpSculptPaint **)r_paint == &ts->gp_sculptpaint) { + GpSculptPaint *data = MEM_callocN(sizeof(*data), __func__); + paint = &data->paint; + } + else if ((GpWeightPaint **)r_paint == &ts->gp_weightpaint) { + GpWeightPaint *data = MEM_callocN(sizeof(*data), __func__); + paint = &data->paint; + } else if ((UvSculpt **)r_paint == &ts->uvsculpt) { UvSculpt *data = MEM_callocN(sizeof(*data), __func__); paint = &data->paint; diff --git a/source/blender/blenkernel/intern/paint_toolslots.c b/source/blender/blenkernel/intern/paint_toolslots.c index e9601109fd5..7e58bf1eb8b 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.c +++ b/source/blender/blenkernel/intern/paint_toolslots.c @@ -82,6 +82,15 @@ void BKE_paint_toolslots_init_from_main(struct Main *bmain) if (ts->gp_paint) { paint_toolslots_init(bmain, &ts->gp_paint->paint); } + if (ts->gp_vertexpaint) { + paint_toolslots_init(bmain, &ts->gp_vertexpaint->paint); + } + if (ts->gp_sculptpaint) { + paint_toolslots_init(bmain, &ts->gp_sculptpaint->paint); + } + if (ts->gp_weightpaint) { + paint_toolslots_init(bmain, &ts->gp_weightpaint->paint); + } } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a88f5a4daa4..f7f59687cb7 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -201,72 +201,6 @@ static void scene_init_data(ID *id) BKE_color_managed_view_settings_init_render( &scene->r.bake.im_format.view_settings, &scene->r.bake.im_format.display_settings, "Filmic"); - /* GP Sculpt brushes */ - { - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - GP_Sculpt_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_SCULPT_TYPE_SMOOTH]; - gp_brush->size = 25; - gp_brush->strength = 0.3f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_SMOOTH_PRESSURE | - GP_SCULPT_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_SCULPT_TYPE_THICKNESS]; - gp_brush->size = 25; - gp_brush->strength = 0.5f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_STRENGTH]; - gp_brush->size = 25; - gp_brush->strength = 0.5f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_GRAB]; - gp_brush->size = 50; - gp_brush->strength = 0.3f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_PUSH]; - gp_brush->size = 25; - gp_brush->strength = 0.3f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_TWIST]; - gp_brush->size = 50; - gp_brush->strength = 0.3f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_PINCH]; - gp_brush->size = 50; - gp_brush->strength = 0.5f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_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_SCULPT_TYPE_RANDOMIZE]; - gp_brush->size = 25; - gp_brush->strength = 0.5f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR; - copy_v3_v3(gp_brush->curcolor_add, curcolor_add); - copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); - } - /* Curve Profile */ scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); @@ -560,6 +494,18 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->gp_paint = MEM_dupallocN(ts->gp_paint); BKE_paint_copy(&ts->gp_paint->paint, &ts->gp_paint->paint, flag); } + if (ts->gp_vertexpaint) { + ts->gp_vertexpaint = MEM_dupallocN(ts->gp_vertexpaint); + BKE_paint_copy(&ts->gp_vertexpaint->paint, &ts->gp_vertexpaint->paint, flag); + } + if (ts->gp_sculptpaint) { + ts->gp_sculptpaint = MEM_dupallocN(ts->gp_sculptpaint); + BKE_paint_copy(&ts->gp_sculptpaint->paint, &ts->gp_sculptpaint->paint, flag); + } + if (ts->gp_weightpaint) { + ts->gp_weightpaint = MEM_dupallocN(ts->gp_weightpaint); + BKE_paint_copy(&ts->gp_weightpaint->paint, &ts->gp_weightpaint->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->imapaint.paintcursor = NULL; @@ -602,6 +548,18 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->gp_paint->paint); MEM_freeN(toolsettings->gp_paint); } + if (toolsettings->gp_vertexpaint) { + BKE_paint_free(&toolsettings->gp_vertexpaint->paint); + MEM_freeN(toolsettings->gp_vertexpaint); + } + if (toolsettings->gp_sculptpaint) { + BKE_paint_free(&toolsettings->gp_sculptpaint->paint); + MEM_freeN(toolsettings->gp_sculptpaint); + } + if (toolsettings->gp_weightpaint) { + BKE_paint_free(&toolsettings->gp_weightpaint->paint); + MEM_freeN(toolsettings->gp_weightpaint); + } BKE_paint_free(&toolsettings->imapaint.paint); /* free Grease Pencil interpolation curve */ diff --git a/source/blender/blenlib/intern/BLI_memblock.c b/source/blender/blenlib/intern/BLI_memblock.c index f7239f1b9d1..ee9ea15835b 100644 --- a/source/blender/blenlib/intern/BLI_memblock.c +++ b/source/blender/blenlib/intern/BLI_memblock.c @@ -81,7 +81,16 @@ BLI_memblock *BLI_memblock_create_ex(uint elem_size, uint chunk_size) void BLI_memblock_destroy(BLI_memblock *mblk, MemblockValFreeFP free_callback) { - BLI_memblock_clear(mblk, free_callback); + int elem_per_chunk = mblk->chunk_size / mblk->elem_size; + + if (free_callback) { + for (int i = 0; i <= mblk->elem_last; i++) { + int chunk_idx = i / elem_per_chunk; + int elem_idx = i - elem_per_chunk * chunk_idx; + void *val = (char *)(mblk->chunk_list[chunk_idx]) + mblk->elem_size * elem_idx; + free_callback(val); + } + } for (int i = 0; i < mblk->chunk_len; i++) { MEM_SAFE_FREE(mblk->chunk_list[i]); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6cf8c968ed4..01ab4c0603a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3609,23 +3609,23 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) } #if 0 - if (ntree->previews) { - bNodeInstanceHash *new_previews = BKE_node_instance_hash_new("node previews"); - bNodeInstanceHashIterator iter; - - NODE_INSTANCE_HASH_ITER(iter, ntree->previews) { - bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter); - if (preview) { - bNodePreview *new_preview = newimaadr(fd, preview); - if (new_preview) { - bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter); - BKE_node_instance_hash_insert(new_previews, key, new_preview); - } - } - } - BKE_node_instance_hash_free(ntree->previews, NULL); - ntree->previews = new_previews; - } + if (ntree->previews) { + bNodeInstanceHash* new_previews = BKE_node_instance_hash_new("node previews"); + bNodeInstanceHashIterator iter; + + NODE_INSTANCE_HASH_ITER(iter, ntree->previews) { + bNodePreview* preview = BKE_node_instance_hash_iterator_get_value(&iter); + if (preview) { + bNodePreview* new_preview = newimaadr(fd, preview); + if (new_preview) { + bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter); + BKE_node_instance_hash_insert(new_previews, key, new_preview); + } + } + } + BKE_node_instance_hash_free(ntree->previews, NULL); + ntree->previews = new_previews; + } #else /* XXX TODO */ ntree->previews = NULL; @@ -4084,10 +4084,10 @@ static void direct_link_text(FileData *fd, Text *text) text->compiled = NULL; #if 0 - if (text->flags & TXT_ISEXT) { - BKE_text_reload(text); - } - /* else { */ + if (text->flags & TXT_ISEXT) { + BKE_text_reload(text); + } + /* else { */ #endif link_list(fd, &text->lines); @@ -5071,7 +5071,7 @@ static void lib_link_object(FileData *fd, Main *bmain, Object *ob) * some leaked memory rather then crashing immediately * while bad this _is_ an exceptional case - campbell */ #if 0 - BKE_pose_free(ob->pose); + BKE_pose_free(ob->pose); #else MEM_freeN(ob->pose); #endif @@ -5540,15 +5540,15 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb, Object *ob) else if (md->type == eModifierType_Collision) { CollisionModifierData *collmd = (CollisionModifierData *)md; #if 0 - // TODO: CollisionModifier should use pointcache - // + have proper reset events before enabling this - collmd->x = newdataadr(fd, collmd->x); - collmd->xnew = newdataadr(fd, collmd->xnew); - collmd->mfaces = newdataadr(fd, collmd->mfaces); - - collmd->current_x = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_x"); - collmd->current_xnew = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_xnew"); - collmd->current_v = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_v"); + // TODO: CollisionModifier should use pointcache + // + have proper reset events before enabling this + collmd->x = newdataadr(fd, collmd->x); + collmd->xnew = newdataadr(fd, collmd->xnew); + collmd->mfaces = newdataadr(fd, collmd->mfaces); + + collmd->current_x = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_x"); + collmd->current_xnew = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_xnew"); + collmd->current_v = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_v"); #endif collmd->x = NULL; @@ -5752,16 +5752,66 @@ static void direct_link_gpencil_modifiers(FileData *fd, ListBase *lb) direct_link_curvemapping(fd, hmd->curfalloff); } } + else if (md->type == eGpencilModifierType_Noise) { + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + /* initialize the curve. Maybe this could be moved to modififer logic */ + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } else if (md->type == eGpencilModifierType_Thick) { ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; gpmd->curve_thickness = newdataadr(fd, gpmd->curve_thickness); if (gpmd->curve_thickness) { direct_link_curvemapping(fd, gpmd->curve_thickness); - /* initialize the curve. Maybe this could be moved to modififer logic */ BKE_curvemapping_initialize(gpmd->curve_thickness); } } + else if (md->type == eGpencilModifierType_Vertexcolor) { + VertexcolorGpencilModifierData *gpmd = (VertexcolorGpencilModifierData *)md; + gpmd->colorband = newdataadr(fd, gpmd->colorband); + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Smooth) { + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Color) { + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Tint) { + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Opacity) { + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + gpmd->curve_intensity = newdataadr(fd, gpmd->curve_intensity); + if (gpmd->curve_intensity) { + direct_link_curvemapping(fd, gpmd->curve_intensity); + BKE_curvemapping_initialize(gpmd->curve_intensity); + } + } } } @@ -6333,6 +6383,15 @@ static void lib_link_scene(FileData *fd, Main *UNUSED(bmain), Scene *sce) if (sce->toolsettings->gp_paint) { link_paint(fd, sce, &sce->toolsettings->gp_paint->paint); } + if (sce->toolsettings->gp_vertexpaint) { + link_paint(fd, sce, &sce->toolsettings->gp_vertexpaint->paint); + } + if (sce->toolsettings->gp_sculptpaint) { + link_paint(fd, sce, &sce->toolsettings->gp_sculptpaint->paint); + } + if (sce->toolsettings->gp_weightpaint) { + link_paint(fd, sce, &sce->toolsettings->gp_weightpaint->paint); + } if (sce->toolsettings->sculpt) { sce->toolsettings->sculpt->gravity_object = newlibadr( @@ -6615,6 +6674,9 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->wpaint); direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->uvsculpt); direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->gp_paint); + direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->gp_vertexpaint); + direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->gp_sculptpaint); + direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->gp_weightpaint); direct_link_paint(fd, sce, &sce->toolsettings->imapaint.paint); @@ -6895,7 +6957,7 @@ static void lib_link_gpencil(FileData *fd, Main *UNUSED(bmain), bGPdata *gpd) { /* Relink all data-lock linked by GP data-lock */ /* Layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Layer -> Parent References */ gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent); } @@ -6909,9 +6971,6 @@ static void lib_link_gpencil(FileData *fd, Main *UNUSED(bmain), bGPdata *gpd) /* relinks grease-pencil data - used for direct_link and old file linkage */ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; bGPDpalette *palette; /* we must firstly have some grease-pencil data to link! */ @@ -6929,6 +6988,7 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) gpd->flag &= ~GP_DATA_STROKE_EDITMODE; gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; } /* init stroke buffer */ @@ -6952,7 +7012,7 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) /* relink layers */ link_list(fd, &gpd->layers); - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* relink frames */ link_list(fd, &gpl->frames); @@ -6960,24 +7020,24 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) gpl->runtime.icon_id = 0; - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* Relink masks. */ + link_list(fd, &gpl->mask_layers); + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* relink strokes (and their points) */ link_list(fd, &gpf->strokes); - for (gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* relink stroke points array */ gps->points = newdataadr(fd, gps->points); + /* Relink geometry*/ + gps->triangles = newdataadr(fd, gps->triangles); /* relink weight data */ if (gps->dvert) { gps->dvert = newdataadr(fd, gps->dvert); direct_link_dverts(fd, gps->totpoints, gps->dvert); } - - /* the triangulation is not saved, so need to be recalculated */ - gps->triangles = NULL; - gps->tot_triangles = 0; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; } } } @@ -7198,10 +7258,10 @@ static void direct_link_area(FileData *fd, ScrArea *area) * so sacrifice a few old files for now to avoid crashes with new files! * committed: r28002 */ #if 0 - sima->gpd = newdataadr(fd, sima->gpd); - if (sima->gpd) { - direct_link_gpencil(fd, sima->gpd); - } + sima->gpd = newdataadr(fd, sima->gpd); + if (sima->gpd) { + direct_link_gpencil(fd, sima->gpd); + } #endif } else if (sl->spacetype == SPACE_NODE) { @@ -7232,10 +7292,10 @@ static void direct_link_area(FileData *fd, ScrArea *area) * simple return NULL here (sergey) */ #if 0 - if (sseq->gpd) { - sseq->gpd = newdataadr(fd, sseq->gpd); - direct_link_gpencil(fd, sseq->gpd); - } + if (sseq->gpd) { + sseq->gpd = newdataadr(fd, sseq->gpd); + direct_link_gpencil(fd, sseq->gpd); + } #endif sseq->scopes.reference_ibuf = NULL; sseq->scopes.zebra_ibuf = NULL; @@ -7906,11 +7966,11 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, sima->iuser.scene = NULL; #if 0 - /* Those are allocated and freed by space code, no need to handle them here. */ - MEM_SAFE_FREE(sima->scopes.waveform_1); - MEM_SAFE_FREE(sima->scopes.waveform_2); - MEM_SAFE_FREE(sima->scopes.waveform_3); - MEM_SAFE_FREE(sima->scopes.vecscope); + /* Those are allocated and freed by space code, no need to handle them here. */ + MEM_SAFE_FREE(sima->scopes.waveform_1); + MEM_SAFE_FREE(sima->scopes.waveform_2); + MEM_SAFE_FREE(sima->scopes.waveform_3); + MEM_SAFE_FREE(sima->scopes.vecscope); #endif sima->scopes.ok = 0; @@ -8281,8 +8341,8 @@ static void direct_link_speaker(FileData *fd, Speaker *spk) direct_link_animdata(fd, spk->adt); #if 0 - spk->sound = newdataadr(fd, spk->sound); - direct_link_sound(fd, spk->sound); + spk->sound = newdataadr(fd, spk->sound); + direct_link_sound(fd, spk->sound); #endif } @@ -8905,12 +8965,12 @@ static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *a while (bhead && bhead->code == DATA) { void *data; #if 0 - /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */ - short *sp = fd->filesdna->structs[bhead->SDNAnr]; - char *tmp = malloc(100); - allocname = fd->filesdna->types[sp[0]]; - strcpy(tmp, allocname); - data = read_struct(fd, bhead, tmp); + /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */ + short* sp = fd->filesdna->structs[bhead->SDNAnr]; + char* tmp = malloc(100); + allocname = fd->filesdna->types[sp[0]]; + strcpy(tmp, allocname); + data = read_struct(fd, bhead, tmp); #else data = read_struct(fd, bhead, allocname); #endif @@ -9309,11 +9369,6 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) user->walk_navigation.teleport_time = 0.2f; /* s */ } - /* grease pencil multisamples */ - if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "short", "gpencil_multisamples")) { - user->gpencil_multisamples = 4; - } - /* tablet pressure threshold */ if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "float", "pressure_threshold_max")) { user->pressure_threshold_max = 1.0f; @@ -9722,7 +9777,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) bhead = read_libblock(fd, libmain, bhead, 0, true, NULL); } break; - /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ + /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ case ID_SCRN: bhead->code = ID_SCR; /* pass on to default */ @@ -9877,7 +9932,7 @@ static BHead *find_previous_lib(FileData *fd, BHead *bhead) static BHead *find_bhead(FileData *fd, void *old) { #if 0 - BHead *bhead; + BHead* bhead; #endif struct BHeadSort *bhs, bhs_s; @@ -9897,11 +9952,11 @@ static BHead *find_bhead(FileData *fd, void *old) } #if 0 - for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { - if (bhead->old == old) { - return bhead; - } - } + for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->old == old) { + return bhead; + } + } #endif return NULL; @@ -10032,9 +10087,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) /* Commented because this can print way too much. */ #if 0 - if (G.debug & G_DEBUG) { - printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->name); - } + if (G.debug & G_DEBUG) { + printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->name); + } #endif } @@ -10870,7 +10925,7 @@ static void expand_linestyle(FileData *fd, Main *mainvar, FreestyleLineStyle *li static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { expand_doit(fd, mainvar, gpl->parent); } @@ -11813,9 +11868,9 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) /* Does this library have any more linked data-blocks we need to read? */ if (has_linked_ids_to_read(mainptr)) { #if 0 - printf("Reading linked data-blocks from %s (%s)\n", - mainptr->curlib->id.name, - mainptr->curlib->name); + printf("Reading linked data-blocks from %s (%s)\n", + mainptr->curlib->id.name, + mainptr->curlib->name); #endif /* Open file if it has not been done yet. */ diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index df946168056..4500de7bbec 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1116,80 +1116,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 276, 4)) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { ToolSettings *ts = scene->toolsettings; - - if (ts->gp_sculpt.brush[0].size == 0) { - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - GP_Sculpt_Data *brush; - - brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; - brush->size = 25; - brush->strength = 0.3f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_SMOOTH_PRESSURE; - - brush = &gset->brush[GP_SCULPT_TYPE_THICKNESS]; - brush->size = 25; - brush->strength = 0.5f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_GRAB]; - brush->size = 50; - brush->strength = 0.3f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_PUSH]; - brush->size = 25; - brush->strength = 0.3f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_TWIST]; - brush->size = 50; - brush->strength = 0.3f; // XXX? - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_PINCH]; - brush->size = 50; - brush->strength = 0.5f; // XXX? - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_RANDOMIZE]; - brush->size = 25; - brush->strength = 0.5f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - - brush = &gset->brush[GP_SCULPT_TYPE_CLONE]; - brush->size = 50; - brush->strength = 1.0f; - } - if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "gpencil_v3d_align")) { -#if 0 /* XXX: Cannot do this, as we get random crashes... */ - if (scene->gpd) { - bGPdata *gpd = scene->gpd; - - /* Copy over the settings stored in the GP data-block linked to the scene, - * for minimal disruption. */ - ts->gpencil_v3d_align = 0; - - if (gpd->flag & GP_DATA_VIEWALIGN) { - ts->gpencil_v3d_align |= GP_PROJECT_VIEWSPACE; - } - if (gpd->flag & GP_DATA_DEPTH_VIEW) { - ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_VIEW; - } - if (gpd->flag & GP_DATA_DEPTH_STROKE) { - ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_STROKE; - } - - if (gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) { - ts->gpencil_v3d_align |= GP_PROJECT_DEPTH_STROKE_ENDPOINTS; - } - } - else { - /* Default to cursor for all standard 3D views */ - ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE; - } -#endif - ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE; ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE; ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE; @@ -1203,7 +1130,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) /* Ensure that the datablock's onion-skinning toggle flag * stays in sync with the status of the actual layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_ONIONSKIN) { enabled = true; } @@ -1424,22 +1351,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 277, 3)) { /* ------- init of grease pencil initialization --------------- */ if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "bGPDpalettecolor", "*palcolor")) { - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - ToolSettings *ts = scene->toolsettings; - /* initialize use position for sculpt brushes */ - ts->gp_sculpt.flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - - /* new strength sculpt brush */ - if (ts->gp_sculpt.brush[0].size >= 11) { - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - GP_Sculpt_Data *brush; - - brush = &gset->brush[GP_SCULPT_TYPE_STRENGTH]; - brush->size = 25; - brush->strength = 0.5f; - brush->flag = GP_SCULPT_FLAG_USE_FALLOFF; - } - } /* Convert Grease Pencil to new palettes/brushes * Loop all strokes and create the palette and all colors */ @@ -1447,7 +1358,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (BLI_listbase_is_empty(&gpd->palettes)) { /* create palette */ bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette"); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* create color using layer name */ bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info); if (palcolor != NULL) { @@ -1475,8 +1386,8 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) ARRAY_SET_ITEMS(gpl->tintcolor, 0.0f, 0.0f, 0.0f, 0.0f); /* flush relevant layer-settings to strokes */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* set stroke to palette and force recalculation */ BLI_strncpy(gps->colorname, gpl->info, sizeof(gps->colorname)); gps->thickness = gpl->thickness; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index e29c4f8658f..2035ee3590f 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -52,6 +52,7 @@ #include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_view3d_types.h" #include "DNA_genfile.h" #include "DNA_workspace_types.h" @@ -63,6 +64,7 @@ #include "DNA_world_types.h" #include "BKE_animsys.h" +#include "BKE_brush.h" #include "BKE_cloth.h" #include "BKE_collection.h" #include "BKE_constraint.h" @@ -71,6 +73,8 @@ #include "BKE_fcurve.h" #include "BKE_freestyle.h" #include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_idprop.h" #include "BKE_key.h" #include "BKE_lib_id.h" @@ -573,7 +577,7 @@ static void do_versions_fix_annotations(bGPdata *gpd) for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { /* fix layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* unlock/unhide layer */ gpl->flag &= ~GP_LAYER_LOCKED; gpl->flag &= ~GP_LAYER_HIDE; @@ -582,8 +586,8 @@ static void do_versions_fix_annotations(bGPdata *gpd) /* disable tint */ gpl->tintcolor[3] = 0.0f; - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { /* copy color settings */ copy_v4_v4(gpl->color, palcolor->color); @@ -1057,6 +1061,48 @@ static void do_version_curvemapping_walker(Main *bmain, void (*callback)(CurveMa callback(gpmd->curfalloff); } } + else if (md->type == eGpencilModifierType_Noise) { + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Vertexcolor) { + VertexcolorGpencilModifierData *gpmd = (VertexcolorGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Smooth) { + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Color) { + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Opacity) { + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Tint) { + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + callback(gpmd->curve_intensity); + } + } } } @@ -1554,7 +1600,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) if (gpd == NULL) { continue; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (STREQ(gpl->info, "RulerData3D")) { gpl->flag |= GP_LAYER_IS_RULER; break; @@ -1588,6 +1634,34 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 282, 2)) { + /* Init all Vertex/Sculpt and Weight Paint brushes. */ + Brush *brush = BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2); + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + ToolSettings *ts = scene->toolsettings; + + BKE_brush_gpencil_vertex_presets(bmain, ts); + BKE_brush_gpencil_sculpt_presets(bmain, ts); + BKE_brush_gpencil_weight_presets(bmain, ts); + + /* Ensure new Paint modes. */ + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + + /* Set default Draw brush. */ + if (brush != NULL) { + Paint *paint = &ts->gp_paint->paint; + BKE_paint_brush_set(paint, brush); + /* Enable cursor by default. */ + paint->flags |= PAINT_SHOW_BRUSH; + } + /* Ensure Palette by default. */ + BKE_gpencil_palette_ensure(bmain, scene); + } + } + /** * Versioning code until next subversion bump goes here. * @@ -1774,36 +1848,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) #endif { - /* Grease pencil sculpt and paint cursors */ - if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "int", "weighttype")) { - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* sculpt brushes */ - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - if (gset) { - gset->weighttype = GP_SCULPT_TYPE_WEIGHT; - } - } - } - - { - 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_Sculpt_Data *gp_brush; - - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - ToolSettings *ts = scene->toolsettings; - /* sculpt brushes */ - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - for (int i = 0; i < GP_SCULPT_TYPE_MAX; i++) { - gp_brush = &gset->brush[i]; - gp_brush->flag |= GP_SCULPT_FLAG_ENABLE_CURSOR; - copy_v3_v3(gp_brush->curcolor_add, curcolor_add); - copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); - } - } - } - /* Init grease pencil edit line color */ if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "float", "line_color[4]")) { for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { @@ -1866,8 +1910,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 280, 3)) { /* init grease pencil grids and paper */ - if (!DNA_struct_elem_find( - fd->filesdna, "gp_paper_opacity", "float", "gpencil_paper_color[3]")) { + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_paper_color[3]")) { for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { for (ScrArea *area = screen->areabase.first; area; area = area->next) { for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) { @@ -2621,7 +2664,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "short", "line_change")) { for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->line_change = gpl->thickness; if ((gpl->thickness < 1) || (gpl->thickness > 10)) { gpl->thickness = 3; @@ -2953,34 +2996,13 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) /* grease pencil main material show switches */ for (Material *mat = bmain->materials.first; mat; mat = mat->id.next) { if (mat->gp_style) { - mat->gp_style->flag |= GP_STYLE_STROKE_SHOW; - mat->gp_style->flag |= GP_STYLE_FILL_SHOW; + mat->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + mat->gp_style->flag |= GP_MATERIAL_FILL_SHOW; } } } if (!MAIN_VERSION_ATLEAST(bmain, 280, 33)) { - /* Grease pencil reset sculpt brushes after struct rename */ - if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "int", "weighttype")) { - 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); - - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* sculpt brushes */ - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - if (gset) { - for (int i = 0; i < GP_SCULPT_TYPE_MAX; i++) { - GP_Sculpt_Data *gp_brush = &gset->brush[i]; - gp_brush->size = 30; - gp_brush->strength = 0.5f; - gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR; - copy_v3_v3(gp_brush->curcolor_add, curcolor_add); - copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); - } - } - } - } if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "overscan")) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { @@ -3214,7 +3236,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) /* init Annotations onion skin */ if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "int", "gstep")) { for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.302f, 0.851f, 0.302f); ARRAY_SET_ITEMS(gpl->gcolor_next, 0.250f, 0.1f, 1.0f); } @@ -3420,20 +3442,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Grease pencil target weight */ - if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "float", "weight")) { - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* sculpt brushes */ - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - if (gset) { - for (int i = 0; i < GP_SCULPT_TYPE_MAX; i++) { - GP_Sculpt_Data *gp_brush = &gset->brush[i]; - gp_brush->weight = 1.0f; - } - } - } - } - /* Grease pencil cutter/select segment intersection threshold */ if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "float", "isect_threshold")) { for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { @@ -3653,7 +3661,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (gpd->flag & GP_DATA_ANNOTATIONS) { continue; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* default channel color */ ARRAY_SET_ITEMS(gpl->color, 0.2f, 0.2f, 0.2f); } @@ -3744,26 +3752,24 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } /* init grease pencil brush gradients */ - if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "float", "gradient_f")) { + if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "float", "hardeness")) { for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { if (brush->gpencil_settings != NULL) { BrushGpencilSettings *gp = brush->gpencil_settings; - gp->gradient_f = 1.0f; - gp->gradient_s[0] = 1.0f; - gp->gradient_s[1] = 1.0f; + gp->hardeness = 1.0f; + copy_v2_fl(gp->aspect_ratio, 1.0f); } } } /* init grease pencil stroke gradients */ - if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "float", "gradient_f")) { + if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "float", "hardeness")) { for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { - 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) { - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + gps->hardeness = 1.0f; + copy_v2_fl(gps->aspect_ratio, 1.0f); } } } @@ -4217,7 +4223,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Fix wrong 3D viewport copying causing corrupt pointers (T69974). */ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -4507,6 +4512,272 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 283, 7)) { + /* Init default Grease Pencil Vertex paint mix factor for Viewport. */ + if (!DNA_struct_elem_find( + fd->filesdna, "View3DOverlay", "float", "gpencil_vertex_paint_opacity")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_vertex_paint_opacity = 1.0f; + } + } + } + } + } + + /* Update Grease Pencil after drawing engine and code refactor. + * It uses the seed variable of Array modifier to avoid double patching for + * files created with a development version. */ + if (!DNA_struct_elem_find(fd->filesdna, "ArrayGpencilModifierData", "int", "seed")) { + /* Init new Grease Pencil Paint tools. */ + { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->gpencil_settings != NULL) { + brush->gpencil_vertex_tool = brush->gpencil_settings->brush_type; + brush->gpencil_sculpt_tool = brush->gpencil_settings->brush_type; + brush->gpencil_weight_tool = brush->gpencil_settings->brush_type; + } + } + + BKE_paint_toolslots_init_from_main(bmain); + } + + LISTBASE_FOREACH (Material *, mat, &bmain->materials) { + MaterialGPencilStyle *gp_style = mat->gp_style; + if (gp_style == NULL) { + continue; + } + /* Fix Grease Pencil Material colors to Linear. */ + srgb_to_linearrgb_v4(gp_style->stroke_rgba, gp_style->stroke_rgba); + srgb_to_linearrgb_v4(gp_style->fill_rgba, gp_style->fill_rgba); + + /* Move old gradient variables to texture. */ + if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) { + gp_style->texture_angle = gp_style->gradient_angle; + copy_v2_v2(gp_style->texture_scale, gp_style->gradient_scale); + copy_v2_v2(gp_style->texture_offset, gp_style->gradient_shift); + } + /* Set Checker material as Solid. This fill mode has been removed and replaced + * by textures. */ + if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_CHECKER) { + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + } + /* Update Alpha channel for texture opacity. */ + if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) { + gp_style->fill_rgba[3] *= gp_style->texture_opacity; + } + /* Stroke stencil mask to mix = 1. */ + if (gp_style->flag & GP_MATERIAL_STROKE_PATTERN) { + gp_style->mix_stroke_factor = 1.0f; + gp_style->flag &= ~GP_MATERIAL_STROKE_PATTERN; + } + /* Mix disabled, set mix factor to 0. */ + else if ((gp_style->flag & GP_MATERIAL_STROKE_TEX_MIX) == 0) { + gp_style->mix_stroke_factor = 0.0f; + } + } + + /* Fix Grease Pencil VFX and modifiers. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type != OB_GPENCIL) { + continue; + } + + /* VFX. */ + LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) { + switch (fx->type) { + case eShaderFxType_Colorize: { + ColorizeShaderFxData *vfx = (ColorizeShaderFxData *)fx; + if (ELEM(vfx->mode, eShaderFxColorizeMode_GrayScale, eShaderFxColorizeMode_Sepia)) { + vfx->factor = 1.0f; + } + srgb_to_linearrgb_v4(vfx->low_color, vfx->low_color); + srgb_to_linearrgb_v4(vfx->high_color, vfx->high_color); + break; + } + case eShaderFxType_Pixel: { + PixelShaderFxData *vfx = (PixelShaderFxData *)fx; + srgb_to_linearrgb_v4(vfx->rgba, vfx->rgba); + break; + } + case eShaderFxType_Rim: { + RimShaderFxData *vfx = (RimShaderFxData *)fx; + srgb_to_linearrgb_v3_v3(vfx->rim_rgb, vfx->rim_rgb); + srgb_to_linearrgb_v3_v3(vfx->mask_rgb, vfx->mask_rgb); + break; + } + case eShaderFxType_Shadow: { + ShadowShaderFxData *vfx = (ShadowShaderFxData *)fx; + srgb_to_linearrgb_v4(vfx->shadow_rgba, vfx->shadow_rgba); + break; + } + case eShaderFxType_Glow: { + GlowShaderFxData *vfx = (GlowShaderFxData *)fx; + srgb_to_linearrgb_v3_v3(vfx->glow_color, vfx->glow_color); + vfx->glow_color[3] = 1.0f; + srgb_to_linearrgb_v3_v3(vfx->select_color, vfx->select_color); + vfx->blur[1] = vfx->blur[0]; + break; + } + default: + break; + } + } + + /* Modifiers. */ + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + switch ((GpencilModifierType)md->type) { + case eGpencilModifierType_Array: { + ArrayGpencilModifierData *mmd = (ArrayGpencilModifierData *)md; + mmd->seed = 1; + if ((mmd->offset[0] != 0.0f) || (mmd->offset[1] != 0.0f) || + (mmd->offset[2] != 0.0f)) { + mmd->flag |= GP_ARRAY_USE_OFFSET; + } + if ((mmd->shift[0] != 0.0f) || (mmd->shift[1] != 0.0f) || (mmd->shift[2] != 0.0f)) { + mmd->flag |= GP_ARRAY_USE_OFFSET; + } + if (mmd->object != NULL) { + mmd->flag |= GP_ARRAY_USE_OB_OFFSET; + } + break; + } + case eGpencilModifierType_Noise: { + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + mmd->factor /= 25.0f; + mmd->factor_thickness = mmd->factor; + mmd->factor_strength = mmd->factor; + mmd->factor_uvs = mmd->factor; + mmd->noise_scale = (mmd->flag & GP_NOISE_FULL_STROKE) ? 0.0f : 1.0f; + + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + case eGpencilModifierType_Tint: { + TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; + srgb_to_linearrgb_v3_v3(mmd->rgb, mmd->rgb); + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + case eGpencilModifierType_Smooth: { + SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md; + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + case eGpencilModifierType_Opacity: { + OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + case eGpencilModifierType_Color: { + ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + case eGpencilModifierType_Thick: { + if (!DNA_struct_elem_find( + fd->filesdna, "ThickGpencilModifierData", "float", "thickness_fac")) { + ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; + mmd->thickness_fac = mmd->thickness; + } + break; + } + case eGpencilModifierType_Multiply: { + MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md; + mmd->fading_opacity = 1.0 - mmd->fading_opacity; + break; + } + case eGpencilModifierType_Subdiv: { + const short simple = (1 << 0); + SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md; + if (mmd->flag & simple) { + mmd->flag &= ~simple; + mmd->type = GP_SUBDIV_SIMPLE; + } + break; + } + case eGpencilModifierType_Vertexcolor: { + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + if (mmd->curve_intensity == NULL) { + mmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (mmd->curve_intensity) { + BKE_curvemapping_initialize(mmd->curve_intensity); + } + } + break; + } + default: + break; + } + } + } + + /* Fix Layers Colors and Vertex Colors to Linear. + * Also set lights to on for layers. */ + LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) { + if (gpd->flag & GP_DATA_ANNOTATIONS) { + continue; + } + /* Onion colors. */ + srgb_to_linearrgb_v3_v3(gpd->gcolor_prev, gpd->gcolor_prev); + srgb_to_linearrgb_v3_v3(gpd->gcolor_next, gpd->gcolor_next); + /* Z-depth Offset. */ + gpd->zdepth_offset = 0.150f; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + gpl->flag |= GP_LAYER_USE_LIGHTS; + srgb_to_linearrgb_v4(gpl->tintcolor, gpl->tintcolor); + gpl->vertex_paint_opacity = 1.0f; + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* Set initial opacity for fill color. */ + gps->fill_opacity_fac = 1.0f; + + /* Calc geometry data because in old versions this data was not saved. */ + BKE_gpencil_stroke_geometry_update(gps); + + srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); + int i; + bGPDspoint *pt; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + srgb_to_linearrgb_v4(pt->vert_color, pt->vert_color); + } + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 00276f9c4a9..f0127330b1b 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -44,10 +44,12 @@ #include "BKE_appdir.h" #include "BKE_brush.h" #include "BKE_colortools.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_material.h" #include "BKE_node.h" #include "BKE_paint.h" #include "BKE_screen.h" @@ -235,6 +237,13 @@ static void blo_update_defaults_screen(bScreen *screen, /* Enable Sliders. */ saction->flag |= SACTION_SLIDERS; } + else if (sa->spacetype == SPACE_VIEW3D) { + View3D *v3d = sa->spacedata.first; + /* Set Material Color by default. */ + v3d->shading.color_type = V3D_SHADING_MATERIAL_COLOR; + /* Enable Annotations. */ + v3d->flag2 |= V3D_SHOW_ANNOTATION; + } } } } @@ -583,7 +592,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } - if (app_template && STREQ(app_template, "2D_Animation")) { + /* New grease pencil brushes and vertex paint setup. */ + { /* Update Grease Pencil brushes. */ Brush *brush; @@ -617,8 +627,49 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) BKE_id_delete(bmain, brush); } + /* Rename and fix materials. */ + if (app_template && STREQ(app_template, "2D_Animation")) { + Material *ma = NULL; + rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); + rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); + rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); + rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + + /* Dots Stroke. */ + ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); + } + ma->gp_style->mode = GP_MATERIAL_MODE_DOT; + + /* Squares Stroke. */ + ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, "Squares Stroke"); + } + ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE; + + /* Change Solid Fill settings. */ + ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2); + if (ma != NULL) { + ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + } + } + /* Reset all grease pencil brushes. */ Scene *scene = bmain->scenes.first; - BKE_brush_gpencil_presets(bmain, scene->toolsettings); + BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings); + + /* Ensure new Paint modes. */ + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + + /* Enable cursor. */ + GpPaint *gp_paint = scene->toolsettings->gp_paint; + gp_paint->paint.flags |= PAINT_SHOW_BRUSH; + + /* Ensure Palette by default. */ + BKE_gpencil_palette_ensure(bmain, scene); } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 0c9363043ee..81df00ebdef 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1830,6 +1830,13 @@ static void write_gpencil_modifiers(WriteData *wd, ListBase *modbase) write_curvemapping(wd, gpmd->curve_thickness); } } + else if (md->type == eGpencilModifierType_Noise) { + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } else if (md->type == eGpencilModifierType_Hook) { HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md; @@ -1837,6 +1844,39 @@ static void write_gpencil_modifiers(WriteData *wd, ListBase *modbase) write_curvemapping(wd, gpmd->curfalloff); } } + else if (md->type == eGpencilModifierType_Vertexcolor) { + VertexcolorGpencilModifierData *gpmd = (VertexcolorGpencilModifierData *)md; + if (gpmd->colorband) { + writestruct(wd, DATA, ColorBand, 1, gpmd->colorband); + } + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Smooth) { + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Color) { + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Opacity) { + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } + else if (md->type == eGpencilModifierType_Tint) { + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + if (gpmd->curve_intensity) { + write_curvemapping(wd, gpmd->curve_intensity); + } + } } } @@ -2546,6 +2586,18 @@ static void write_scene(WriteData *wd, Scene *sce) writestruct(wd, DATA, GpPaint, 1, tos->gp_paint); write_paint(wd, &tos->gp_paint->paint); } + if (tos->gp_vertexpaint) { + writestruct(wd, DATA, GpVertexPaint, 1, tos->gp_vertexpaint); + write_paint(wd, &tos->gp_vertexpaint->paint); + } + if (tos->gp_sculptpaint) { + writestruct(wd, DATA, GpSculptPaint, 1, tos->gp_sculptpaint); + write_paint(wd, &tos->gp_sculptpaint->paint); + } + if (tos->gp_weightpaint) { + writestruct(wd, DATA, GpWeightPaint, 1, tos->gp_weightpaint); + write_paint(wd, &tos->gp_weightpaint->paint); + } /* write grease-pencil custom ipo curve to file */ if (tos->gp_interpolate.custom_ipo) { write_curvemapping(wd, tos->gp_interpolate.custom_ipo); @@ -2741,14 +2793,17 @@ static void write_gpencil(WriteData *wd, bGPdata *gpd) /* write grease-pencil layers to file */ writelist(wd, DATA, bGPDlayer, &gpd->layers); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* Write mask list. */ + writelist(wd, DATA, bGPDlayer_Mask, &gpl->mask_layers); /* write this layer's frames to file */ writelist(wd, DATA, bGPDframe, &gpl->frames); - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* write strokes */ writelist(wd, DATA, bGPDstroke, &gpf->strokes); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { writestruct(wd, DATA, bGPDspoint, gps->totpoints, gps->points); + writestruct(wd, DATA, bGPDtriangle, gps->tot_triangles, gps->triangles); write_dverts(wd, gps->totpoints, gps->dvert); } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 687ee492c3b..01d84f6b5d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1310,7 +1310,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL, - function_bind(BKE_gpencil_eval_geometry, _1, (bGPdata *)obdata_cow)); + function_bind(BKE_gpencil_frame_active_set, _1, (bGPdata *)obdata_cow)); op_node->set_as_entry(); break; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 78e05a69819..6791125d1e9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2123,6 +2123,15 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) add_relation(material_key, geometry_key, "Material -> GP Data"); } } + + /* Layer parenting need react to the parent object transformation. */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (gpl->parent != NULL) { + ComponentKey transform_key(&gpl->parent->id, NodeType::TRANSFORM); + ComponentKey gpd_geom_key(&gpd->id, NodeType::GEOMETRY); + add_relation(transform_key, gpd_geom_key, "GPencil Parent Layer"); + } + } break; } default: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 95521ee9e47..1841f5f024f 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -43,6 +43,7 @@ #include "BKE_curve.h" #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_scene.h" @@ -791,6 +792,9 @@ void update_id_after_copy(const Depsgraph *depsgraph, } BKE_pose_pchan_index_rebuild(object_cow->pose); } + if (object_cow->type == OB_GPENCIL) { + BKE_gpencil_update_orig_pointers(object_orig, object_cow); + } update_particles_after_copy(depsgraph, object_orig, object_cow); update_modifiers_orig_pointers(object_orig, object_cow); update_proxy_pointers_after_copy(depsgraph, object_orig, object_cow); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6d7b422c4fb..1f04739644e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/draw_cache_extract_mesh.c intern/draw_cache_impl_curve.c intern/draw_cache_impl_displist.c + intern/draw_cache_impl_gpencil.c intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.c intern/draw_cache_impl_metaball.c @@ -111,12 +112,13 @@ set(SRC engines/workbench/workbench_studiolight.c engines/workbench/workbench_volume.c engines/external/external_engine.c + engines/gpencil/gpencil_antialiasing.c engines/gpencil/gpencil_cache_utils.c - engines/gpencil/gpencil_draw_cache_impl.c - engines/gpencil/gpencil_draw_utils.c + engines/gpencil/gpencil_draw_data.c engines/gpencil/gpencil_engine.c engines/gpencil/gpencil_engine.h engines/gpencil/gpencil_render.c + engines/gpencil/gpencil_shader.c engines/gpencil/gpencil_shader_fx.c engines/select/select_draw_utils.c engines/select/select_engine.c @@ -129,6 +131,7 @@ set(SRC engines/overlay/overlay_engine.c engines/overlay/overlay_extra.c engines/overlay/overlay_facing.c + engines/overlay/overlay_gpencil.c engines/overlay/overlay_grid.c engines/overlay/overlay_image.c engines/overlay/overlay_lattice.c @@ -277,36 +280,16 @@ data_to_c_simple(intern/shaders/common_fxaa_lib.glsl SRC) data_to_c_simple(intern/shaders/common_smaa_lib.glsl SRC) data_to_c_simple(intern/shaders/common_fullscreen_vert.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_fill_vert.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_fill_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_stroke_vert.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_stroke_geom.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_stroke_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_simple_mix_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_blend_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_point_vert.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_point_geom.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_point_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_background_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_paper_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_edit_point_vert.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_edit_point_geom.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/gpencil_edit_point_frag.glsl SRC) - -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl SRC) -data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_vert.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_antialiasing_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_antialiasing_vert.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_common_lib.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_layer_blend_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_mask_invert_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_depth_merge_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_depth_merge_vert.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/gpencil_vfx_frag.glsl SRC) data_to_c_simple(engines/select/shaders/selection_id_3D_vert.glsl SRC) data_to_c_simple(engines/select/shaders/selection_id_frag.glsl SRC) @@ -340,6 +323,9 @@ data_to_c_simple(engines/overlay/shaders/edit_curve_handle_geom.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_curve_handle_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_curve_point_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_curve_wire_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/edit_gpencil_canvas_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/edit_gpencil_guide_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/edit_gpencil_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_lattice_point_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_lattice_wire_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/edit_mesh_common_lib.glsl SRC) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index c6aedd40afb..b4c23d5e57c 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -129,7 +129,6 @@ void DRW_draw_select_id(struct Depsgraph *depsgraph, /* grease pencil render */ bool DRW_render_check_grease_pencil(struct Depsgraph *depsgraph); void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph); -void DRW_gpencil_freecache(struct Object *ob); /* This is here because GPUViewport needs it */ struct DRWInstanceDataList *DRW_instance_data_list_create(void); diff --git a/source/blender/draw/engines/gpencil/gpencil_antialiasing.c b/source/blender/draw/engines/gpencil/gpencil_antialiasing.c new file mode 100644 index 00000000000..e81073db4a5 --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_antialiasing.c @@ -0,0 +1,169 @@ +/* + * 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. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "DRW_render.h" + +#include "gpencil_engine.h" + +#include "smaa_textures.h" + +void GPENCIL_antialiasing_init(struct GPENCIL_Data *vedata) +{ + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_TextureList *txl = vedata->txl; + GPENCIL_PassList *psl = vedata->psl; + DRWShadingGroup *grp; + + const float *size = DRW_viewport_size_get(); + const float *sizeinv = DRW_viewport_invert_size_get(); + float metrics[4] = {sizeinv[0], sizeinv[1], size[0], size[1]}; + + if (pd->simplify_antialias) { + /* No AA fallback. */ + DRW_PASS_CREATE(psl->smaa_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + + GPUShader *sh = GPENCIL_shader_antialiasing(2); + grp = DRW_shgroup_create(sh, psl->smaa_resolve_ps); + DRW_shgroup_uniform_texture(grp, "blendTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_bool_copy(grp, "doAntiAliasing", false); + DRW_shgroup_uniform_bool_copy(grp, "onlyAlpha", pd->draw_wireframe); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + return; + } + + if (txl->smaa_search_tx == NULL) { + txl->smaa_search_tx = GPU_texture_create_nD(SEARCHTEX_WIDTH, + SEARCHTEX_HEIGHT, + 0, + 2, + searchTexBytes, + GPU_R8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + txl->smaa_area_tx = GPU_texture_create_nD(AREATEX_WIDTH, + AREATEX_HEIGHT, + 0, + 2, + areaTexBytes, + GPU_RG8, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + + GPU_texture_bind(txl->smaa_search_tx, 0); + GPU_texture_filter_mode(txl->smaa_search_tx, true); + GPU_texture_unbind(txl->smaa_search_tx); + + GPU_texture_bind(txl->smaa_area_tx, 0); + GPU_texture_filter_mode(txl->smaa_area_tx, true); + GPU_texture_unbind(txl->smaa_area_tx); + } + + { + pd->smaa_edge_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_RG8, &draw_engine_gpencil_type); + pd->smaa_weight_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_RGBA8, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->smaa_edge_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(pd->smaa_edge_tx), + }); + + GPU_framebuffer_ensure_config(&fbl->smaa_weight_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(pd->smaa_weight_tx), + }); + } + + { + /* Stage 1: Edge detection. */ + DRW_PASS_CREATE(psl->smaa_edge_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = GPENCIL_shader_antialiasing(0); + grp = DRW_shgroup_create(sh, psl->smaa_edge_ps); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 2: Blend Weight/Coord. */ + DRW_PASS_CREATE(psl->smaa_weight_ps, DRW_STATE_WRITE_COLOR); + + GPUShader *sh = GPENCIL_shader_antialiasing(1); + grp = DRW_shgroup_create(sh, psl->smaa_weight_ps); + DRW_shgroup_uniform_texture(grp, "edgesTex", pd->smaa_edge_tx); + DRW_shgroup_uniform_texture(grp, "areaTex", txl->smaa_area_tx); + DRW_shgroup_uniform_texture(grp, "searchTex", txl->smaa_search_tx); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_clear_framebuffer(grp, GPU_COLOR_BIT, 0, 0, 0, 0, 0.0f, 0x0); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + { + /* Stage 3: Resolve. */ + DRW_PASS_CREATE(psl->smaa_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM); + + GPUShader *sh = GPENCIL_shader_antialiasing(2); + grp = DRW_shgroup_create(sh, psl->smaa_resolve_ps); + DRW_shgroup_uniform_texture(grp, "blendTex", pd->smaa_weight_tx); + DRW_shgroup_uniform_texture(grp, "colorTex", pd->color_tx); + DRW_shgroup_uniform_texture(grp, "revealTex", pd->reveal_tx); + DRW_shgroup_uniform_bool_copy(grp, "doAntiAliasing", true); + DRW_shgroup_uniform_bool_copy(grp, "onlyAlpha", pd->draw_wireframe); + DRW_shgroup_uniform_vec4_copy(grp, "viewportMetrics", metrics); + + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +void GPENCIL_antialiasing_draw(struct GPENCIL_Data *vedata) +{ + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_PassList *psl = vedata->psl; + + if (!pd->simplify_antialias) { + GPU_framebuffer_bind(fbl->smaa_edge_fb); + DRW_draw_pass(psl->smaa_edge_ps); + + GPU_framebuffer_bind(fbl->smaa_weight_fb); + DRW_draw_pass(psl->smaa_weight_ps); + } + + GPU_framebuffer_bind(pd->scene_fb); + DRW_draw_pass(psl->smaa_resolve_ps); +} diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 5357d6167be..743171b09fb 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -33,324 +33,368 @@ #include "BKE_gpencil.h" #include "BKE_object.h" +#include "BLI_memblock.h" +#include "BLI_link_utils.h" + #include "gpencil_engine.h" #include "draw_cache_impl.h" #include "DEG_depsgraph.h" -/* verify if exist a non instanced version of the object */ -static bool gpencil_has_noninstanced_object(Object *ob_instance) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = draw_ctx->view_layer; - Object *ob = NULL; - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - ob = base->object; - if (ob->type != OB_GPENCIL) { - continue; - } - /* is not duplicated and the name is equals */ - if ((ob->base_flag & BASE_FROM_DUPLI) == 0) { - if (STREQ(ob->id.name, ob_instance->id.name)) { - return true; - } - } - } - - return false; -} +/* -------------------------------------------------------------------- */ +/** \name Object + * \{ */ -/* add a gpencil object to cache to defer drawing */ -tGPencilObjectCache *gpencil_object_cache_add(tGPencilObjectCache *cache_array, - Object *ob, - int *gp_cache_size, - int *gp_cache_used) +GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob) { - const DRWContextState *draw_ctx = DRW_context_state_get(); - tGPencilObjectCache *cache_elem = NULL; - RegionView3D *rv3d = draw_ctx->rv3d; - View3D *v3d = draw_ctx->v3d; - tGPencilObjectCache *p = NULL; - - /* By default a cache is created with one block with a predefined number of free slots, - * if the size is not enough, the cache is reallocated adding a new block of free slots. - * This is done in order to keep cache small. */ - if (*gp_cache_used + 1 > *gp_cache_size) { - if ((*gp_cache_size == 0) || (cache_array == NULL)) { - p = MEM_callocN(sizeof(struct tGPencilObjectCache) * GP_CACHE_BLOCK_SIZE, - "tGPencilObjectCache"); - *gp_cache_size = GP_CACHE_BLOCK_SIZE; - } - else { - *gp_cache_size += GP_CACHE_BLOCK_SIZE; - p = MEM_recallocN(cache_array, sizeof(struct tGPencilObjectCache) * *gp_cache_size); - } - cache_array = p; + bGPdata *gpd = (bGPdata *)ob->data; + GPENCIL_tObject *tgp_ob = BLI_memblock_alloc(pd->gp_object_pool); + + tgp_ob->layers.first = tgp_ob->layers.last = NULL; + tgp_ob->vfx.first = tgp_ob->vfx.last = NULL; + tgp_ob->camera_z = dot_v3v3(pd->camera_z_axis, ob->obmat[3]); + tgp_ob->is_drawmode3d = (gpd->draw_mode == GP_DRAWMODE_3D) || pd->draw_depth_only; + tgp_ob->object_scale = mat4_to_scale(ob->obmat); + + /* Find the normal most likely to represent the gpObject. */ + /* TODO: This does not work quite well if you use + * strokes not aligned with the object axes. Maybe we could try to + * compute the minimum axis of all strokes. But this would be more + * computationaly heavy and should go into the GPData evaluation. */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + /* Convert bbox to matrix */ + float mat[4][4], size[3], center[3]; + BKE_boundbox_calc_size_aabb(bbox, size); + BKE_boundbox_calc_center_aabb(bbox, center); + unit_m4(mat); + copy_v3_v3(mat[3], center); + /* Avoid division by 0.0 later. */ + add_v3_fl(size, 1e-8f); + rescale_m4(mat, size); + /* BBox space to World. */ + mul_m4_m4m4(mat, ob->obmat, mat); + if (DRW_view_is_persp_get(NULL)) { + /* BBox center to camera vector. */ + sub_v3_v3v3(tgp_ob->plane_normal, pd->camera_pos, mat[3]); } - /* zero out all pointers */ - cache_elem = &cache_array[*gp_cache_used]; - memset(cache_elem, 0, sizeof(*cache_elem)); - - cache_elem->ob = ob; - cache_elem->gpd = (bGPdata *)ob->data; - cache_elem->name = BKE_id_to_unique_string_key(&ob->id); - - copy_v3_v3(cache_elem->loc, ob->obmat[3]); - copy_m4_m4(cache_elem->obmat, ob->obmat); - cache_elem->idx = *gp_cache_used; - - /* object is duplicated (particle) */ - if (ob->base_flag & BASE_FROM_DUPLI) { - /* Check if the original object is not in the viewlayer - * and cannot be managed as dupli. This is slower, but required to keep - * the particle drawing FPS and display instanced objects in scene - * without the original object */ - bool has_original = gpencil_has_noninstanced_object(ob); - cache_elem->is_dup_ob = (has_original) ? ob->base_flag & BASE_FROM_DUPLI : false; + else { + copy_v3_v3(tgp_ob->plane_normal, pd->camera_z_axis); + } + /* World to BBox space. */ + invert_m4(mat); + /* Normalize the vector in BBox space. */ + mul_mat3_m4_v3(mat, tgp_ob->plane_normal); + normalize_v3(tgp_ob->plane_normal); + + transpose_m4(mat); + /* mat is now a "normal" matrix which will transform + * BBox space normal to world space. */ + mul_mat3_m4_v3(mat, tgp_ob->plane_normal); + normalize_v3(tgp_ob->plane_normal); + + /* Define a matrix that will be used to render a triangle to merge the depth of the rendered + * gpencil object with the rest of the scene. */ + unit_m4(tgp_ob->plane_mat); + copy_v3_v3(tgp_ob->plane_mat[2], tgp_ob->plane_normal); + orthogonalize_m4(tgp_ob->plane_mat, 2); + mul_mat3_m4_v3(ob->obmat, size); + float radius = len_v3(size); + mul_m4_v3(ob->obmat, center); + rescale_m4(tgp_ob->plane_mat, (float[3]){radius, radius, radius}); + copy_v3_v3(tgp_ob->plane_mat[3], center); + + /* Add to corresponding list if is in front. */ + if (ob->dtx & OB_DRAWXRAY) { + BLI_LINKS_APPEND(&pd->tobjects_infront, tgp_ob); } else { - cache_elem->is_dup_ob = false; + BLI_LINKS_APPEND(&pd->tobjects, tgp_ob); } - cache_elem->scale = mat4_to_scale(ob->obmat); + return tgp_ob; +} - /* save FXs */ - cache_elem->pixfactor = cache_elem->gpd->pixfactor; - cache_elem->shader_fx = ob->shader_fx; +#define SORT_IMPL_LINKTYPE GPENCIL_tObject - /* save wire mode (object mode is always primary option) */ - if (ob->dt == OB_WIRE) { - cache_elem->shading_type[0] = (int)OB_WIRE; +#define SORT_IMPL_FUNC gpencil_tobject_sort_fn_r +#include "../../blenlib/intern/list_sort_impl.h" +#undef SORT_IMPL_FUNC + +#undef SORT_IMPL_LINKTYPE + +static int gpencil_tobject_dist_sort(const void *a, const void *b) +{ + const GPENCIL_tObject *ob_a = (const GPENCIL_tObject *)a; + const GPENCIL_tObject *ob_b = (const GPENCIL_tObject *)b; + /* Reminder, camera_z is negative in front of the camera. */ + if (ob_a->camera_z > ob_b->camera_z) { + return 1; } - else { - if (v3d) { - cache_elem->shading_type[0] = (int)v3d->shading.type; - } + else if (ob_a->camera_z < ob_b->camera_z) { + return -1; } - - /* shgrp array */ - cache_elem->tot_layers = 0; - int totgpl = BLI_listbase_count(&cache_elem->gpd->layers); - if (totgpl > 0) { - cache_elem->shgrp_array = MEM_callocN(sizeof(tGPencilObjectCache_shgrp) * totgpl, __func__); + else { + return 0; } +} - /* calculate zdepth from point of view */ - float zdepth = 0.0; - if (rv3d) { - if (rv3d->is_persp) { - zdepth = ED_view3d_calc_zfac(rv3d, ob->obmat[3], NULL); - } - else { - zdepth = -dot_v3v3(rv3d->viewinv[2], ob->obmat[3]); +void gpencil_object_cache_sort(GPENCIL_PrivateData *pd) +{ + /* Sort object by distance to the camera. */ + if (pd->tobjects.first) { + pd->tobjects.first = gpencil_tobject_sort_fn_r(pd->tobjects.first, gpencil_tobject_dist_sort); + /* Relink last pointer. */ + while (pd->tobjects.last->next) { + pd->tobjects.last = pd->tobjects.last->next; } } - else { - /* In render mode, rv3d is not available, so use the distance to camera. - * The real distance is not important, but the relative distance to the camera plane - * in order to sort by z_depth of the objects - */ - float vn[3] = {0.0f, 0.0f, -1.0f}; /* always face down */ - float plane_cam[4]; - struct Object *camera = draw_ctx->scene->camera; - if (camera) { - mul_m4_v3(camera->obmat, vn); - normalize_v3(vn); - plane_from_point_normal_v3(plane_cam, camera->loc, vn); - zdepth = dist_squared_to_plane_v3(ob->obmat[3], plane_cam); + if (pd->tobjects_infront.first) { + pd->tobjects_infront.first = gpencil_tobject_sort_fn_r(pd->tobjects_infront.first, + gpencil_tobject_dist_sort); + /* Relink last pointer. */ + while (pd->tobjects_infront.last->next) { + pd->tobjects_infront.last = pd->tobjects_infront.last->next; } } - cache_elem->zdepth = zdepth; - /* increase slots used in cache */ - (*gp_cache_used)++; - - return cache_array; -} -/* add a shading group to the cache to create later */ -GpencilBatchGroup *gpencil_group_cache_add(GpencilBatchGroup *cache_array, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps, - const short type, - const bool onion, - const int vertex_idx, - int *grp_size, - int *grp_used) -{ - GpencilBatchGroup *cache_elem = NULL; - GpencilBatchGroup *p = NULL; - - /* By default a cache is created with one block with a predefined number of free slots, - * if the size is not enough, the cache is reallocated adding a new block of free slots. - * This is done in order to keep cache small. */ - if (*grp_used + 1 > *grp_size) { - if ((*grp_size == 0) || (cache_array == NULL)) { - p = MEM_callocN(sizeof(struct GpencilBatchGroup) * GPENCIL_GROUPS_BLOCK_SIZE, - "GpencilBatchGroup"); - *grp_size = GPENCIL_GROUPS_BLOCK_SIZE; + /* Join both lists, adding infront. */ + if (pd->tobjects_infront.first != NULL) { + if (pd->tobjects.last != NULL) { + pd->tobjects.last->next = pd->tobjects_infront.first; + pd->tobjects.last = pd->tobjects_infront.last; } else { - *grp_size += GPENCIL_GROUPS_BLOCK_SIZE; - p = MEM_recallocN(cache_array, sizeof(struct GpencilBatchGroup) * *grp_size); + /* Only in front objects. */ + pd->tobjects.first = pd->tobjects_infront.first; + pd->tobjects.last = pd->tobjects_infront.last; } - cache_array = p; } - /* zero out all data */ - cache_elem = &cache_array[*grp_used]; - memset(cache_elem, 0, sizeof(*cache_elem)); - - cache_elem->gpl = gpl; - cache_elem->gpf = gpf; - cache_elem->gps = gps; - cache_elem->type = type; - cache_elem->onion = onion; - cache_elem->vertex_idx = vertex_idx; +} - /* increase slots used in cache */ - (*grp_used)++; +/** \} */ - return cache_array; -} +/* -------------------------------------------------------------------- */ +/** \name Layer + * \{ */ -/* get current cache data */ -static GpencilBatchCache *gpencil_batch_get_element(Object *ob) +static float gpencil_layer_final_opacity_get(const GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl) { - return ob->runtime.gpencil_cache; + const bool is_obact = ((pd->obact) && (pd->obact == ob)); + const bool is_fade = ((pd->fade_layer_opacity > -1.0f) && (is_obact) && + ((gpl->flag & GP_LAYER_ACTIVE) == 0)); + + /* Defines layer opacity. For active object depends of layer opacity factor, and + * for no active object, depends if the fade grease pencil objects option is enabled. */ + if (!pd->is_render) { + if (is_obact && is_fade) { + return gpl->opacity * pd->fade_layer_opacity; + } + else if (!is_obact && (pd->fade_gp_object_opacity > -1.0f)) { + return gpl->opacity * pd->fade_gp_object_opacity; + } + } + return gpl->opacity; } -/* verify if cache is valid */ -static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra) +static void gpencil_layer_final_tint_and_alpha_get(const GPENCIL_PrivateData *pd, + const bGPdata *gpd, + const bGPDlayer *gpl, + const bGPDframe *gpf, + float r_tint[4], + float *r_alpha) { - bool valid = true; - if (cache == NULL) { - return false; - } - - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); - if (cfra != cache->cache_frame) { - valid = false; - } - else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) { - valid = false; - } - else if (gpd->flag & GP_DATA_PYTHON_UPDATED) { - gpd->flag &= ~GP_DATA_PYTHON_UPDATED; - valid = false; + const bool use_onion = (gpf != NULL) && (gpf->runtime.onion_id != 0.0f); + if (use_onion) { + const bool use_onion_custom_col = (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) != 0; + const bool use_onion_fade = (gpd->onion_flag & GP_ONION_FADE) != 0; + const bool use_next_col = gpf->runtime.onion_id > 0.0f; + + const float *onion_col_custom = (use_onion_custom_col) ? + (use_next_col ? gpd->gcolor_next : gpd->gcolor_prev) : + U.gpencil_new_layer_col; + + copy_v4_fl4(r_tint, UNPACK3(onion_col_custom), 1.0f); + + *r_alpha = use_onion_fade ? (1.0f / abs(gpf->runtime.onion_id)) : 0.5f; + *r_alpha *= gpd->onion_factor; + *r_alpha = (gpd->onion_factor > 0.0f) ? clamp_f(*r_alpha, 0.1f, 1.0f) : + clamp_f(*r_alpha, 0.01f, 1.0f); } - else if (cache->is_editmode) { - valid = false; - } - else if (cache->is_dirty) { - valid = false; + else { + copy_v4_v4(r_tint, gpl->tintcolor); + if (GPENCIL_SIMPLIFY_TINT(pd->scene)) { + r_tint[3] = 0.0f; + } + *r_alpha = 1.0f; } - return valid; + *r_alpha *= pd->xray_alpha; } -/* cache init */ -static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra) +GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl, + const bGPDframe *gpf, + GPENCIL_tObject *tgp_ob) { bGPdata *gpd = (bGPdata *)ob->data; - GpencilBatchCache *cache = gpencil_batch_get_element(ob); + const bool is_in_front = (ob->dtx & OB_DRAWXRAY); + const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0; + const bool overide_vertcol = (pd->v3d_color_type != -1); + const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) || + GPENCIL_VERTEX_MODE(gpd) || pd->is_render; + bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers); + + float vert_col_opacity = (overide_vertcol) ? (is_vert_col_mode ? 1.0f : 0.0f) : + gpl->vertex_paint_opacity; + /* Negate thickness sign to tag that strokes are in screen space. + * Convert to world units (by default, 1 meter = 2000 px). */ + float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR); + float layer_opacity = gpencil_layer_final_opacity_get(pd, ob, gpl); + float layer_tint[4]; + float layer_alpha; + gpencil_layer_final_tint_and_alpha_get(pd, gpd, gpl, gpf, layer_tint, &layer_alpha); + + /* Create the new layer descriptor. */ + GPENCIL_tLayer *tgp_layer = BLI_memblock_alloc(pd->gp_layer_pool); + BLI_LINKS_APPEND(&tgp_ob->layers, tgp_layer); + tgp_layer->layer_id = BLI_findindex(&gpd->layers, gpl); + tgp_layer->mask_bits = NULL; + tgp_layer->mask_invert_bits = NULL; + tgp_layer->blend_ps = NULL; + + /* Masking: Go through mask list and extract valid masks in a bitmap. */ + if (is_masked) { + bool valid_mask = false; + /* Warning: only GP_MAX_MASKBITS amount of bits. + * TODO(fclem) Find a better system without any limitation. */ + tgp_layer->mask_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); + tgp_layer->mask_invert_bits = BLI_memblock_alloc(pd->gp_maskbit_pool); + BLI_bitmap_set_all(tgp_layer->mask_bits, false, GP_MAX_MASKBITS); + + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) { + bGPDlayer *gpl_mask = BKE_gpencil_layer_named_get(gpd, mask->name); + if (gpl_mask && (gpl_mask != gpl) && ((gpl_mask->flag & GP_LAYER_HIDE) == 0) && + ((mask->flag & GP_MASK_HIDE) == 0)) { + int index = BLI_findindex(&gpd->layers, gpl_mask); + if (index < GP_MAX_MASKBITS) { + const bool invert = (mask->flag & GP_MASK_INVERT) != 0; + BLI_BITMAP_SET(tgp_layer->mask_bits, index, true); + BLI_BITMAP_SET(tgp_layer->mask_invert_bits, index, invert); + valid_mask = true; + } + } + } - if (!cache) { - cache = MEM_callocN(sizeof(*cache), __func__); - ob->runtime.gpencil_cache = cache; - } - else { - memset(cache, 0, sizeof(*cache)); + if (valid_mask) { + pd->use_mask_fb = true; + } + else { + tgp_layer->mask_bits = NULL; + } + is_masked = valid_mask; } - cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); - - cache->is_dirty = true; + /* Blending: Force blending for masked layer. */ + if (is_masked || (gpl->blend_mode != eGplBlendMode_Regular) || (layer_opacity < 1.0f)) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_STENCIL_EQUAL; + switch (gpl->blend_mode) { + case eGplBlendMode_Regular: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eGplBlendMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eGplBlendMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eGplBlendMode_Multiply: + case eGplBlendMode_Divide: + case eGplBlendMode_Overlay: + state |= DRW_STATE_BLEND_MUL; + break; + } - cache->cache_frame = cfra; + if (ELEM(gpl->blend_mode, eGplBlendMode_Subtract, eGplBlendMode_Overlay)) { + /* For these effect to propagate, we need a signed floating point buffer. */ + pd->use_signed_fb = true; + } - return cache; -} + tgp_layer->blend_ps = DRW_pass_create("GPencil Blend Layer", state); + + GPUShader *sh = GPENCIL_shader_layer_blend_get(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); + DRW_shgroup_uniform_int_copy(grp, "blendMode", gpl->blend_mode); + DRW_shgroup_uniform_float_copy(grp, "blendOpacity", layer_opacity); + DRW_shgroup_uniform_texture_ref(grp, "colorBuf", &pd->color_layer_tx); + DRW_shgroup_uniform_texture_ref(grp, "revealBuf", &pd->reveal_layer_tx); + DRW_shgroup_uniform_texture_ref(grp, "maskBuf", (is_masked) ? &pd->mask_tx : &pd->dummy_tx); + DRW_shgroup_stencil_mask(grp, 0xFF); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (gpl->blend_mode == eGplBlendMode_Overlay) { + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create(sh, tgp_layer->blend_ps); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "blendMode", 999); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } -/* clear cache */ -static void gpencil_batch_cache_clear(GpencilBatchCache *cache) -{ - if (!cache) { - return; + pd->use_layer_fb = true; } - GPU_BATCH_DISCARD_SAFE(cache->b_stroke.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_point.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_fill.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_edit.batch); - GPU_BATCH_DISCARD_SAFE(cache->b_edlin.batch); - - MEM_SAFE_FREE(cache->b_stroke.batch); - MEM_SAFE_FREE(cache->b_point.batch); - MEM_SAFE_FREE(cache->b_fill.batch); - MEM_SAFE_FREE(cache->b_edit.batch); - MEM_SAFE_FREE(cache->b_edlin.batch); - - /* internal format data */ - MEM_SAFE_FREE(cache->b_stroke.format); - MEM_SAFE_FREE(cache->b_point.format); - MEM_SAFE_FREE(cache->b_fill.format); - MEM_SAFE_FREE(cache->b_edit.format); - MEM_SAFE_FREE(cache->b_edlin.format); - - MEM_SAFE_FREE(cache->grp_cache); - cache->grp_size = 0; - cache->grp_used = 0; -} - -/* get cache */ -GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra) -{ - bGPdata *gpd = (bGPdata *)ob->data; - - GpencilBatchCache *cache = gpencil_batch_get_element(ob); - if (!gpencil_batch_cache_valid(cache, gpd, cfra)) { - if (cache) { - gpencil_batch_cache_clear(cache); - } - return gpencil_batch_cache_init(ob, cfra); - } - else { - return cache; + /* Geometry pass */ + { + GPUTexture *depth_tex = (is_in_front) ? pd->dummy_tx : pd->scene_depth_tx; + GPUTexture **mask_tex = (is_masked) ? &pd->mask_tx : &pd->dummy_tx; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_BLEND_ALPHA_PREMUL; + /* For 2D mode, we render all strokes with uniform depth (increasing with stroke id). */ + state |= tgp_ob->is_drawmode3d ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_GREATER; + /* Always write stencil. Only used as optimization for blending. */ + state |= DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_ALWAYS; + + tgp_layer->geom_ps = DRW_pass_create("GPencil Layer", state); + + struct GPUShader *sh = GPENCIL_shader_geometry_get(); + DRWShadingGroup *grp = tgp_layer->base_shgrp = DRW_shgroup_create(sh, tgp_layer->geom_ps); + + DRW_shgroup_uniform_texture_persistent(grp, "gpSceneDepthTexture", depth_tex); + DRW_shgroup_uniform_texture_ref(grp, "gpMaskTexture", mask_tex); + DRW_shgroup_uniform_vec3_copy(grp, "gpNormal", tgp_ob->plane_normal); + DRW_shgroup_uniform_bool_copy(grp, "strokeOrder3d", tgp_ob->is_drawmode3d); + DRW_shgroup_uniform_float_copy(grp, "thicknessScale", tgp_ob->object_scale); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewportInv", DRW_viewport_invert_size_get()); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewport", DRW_viewport_size_get()); + DRW_shgroup_uniform_float_copy(grp, "thicknessOffset", (float)gpl->line_change); + DRW_shgroup_uniform_float_copy(grp, "thicknessWorldScale", thickness_scale); + DRW_shgroup_uniform_float_copy(grp, "vertexColorOpacity", vert_col_opacity); + DRW_shgroup_uniform_vec4_copy(grp, "layerTint", layer_tint); + DRW_shgroup_uniform_float_copy(grp, "layerOpacity", layer_alpha); + DRW_shgroup_stencil_mask(grp, 0xFF); } -} -/* set cache as dirty */ -void DRW_gpencil_batch_cache_dirty_tag(bGPdata *gpd) -{ - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + return tgp_layer; } -/* free batch cache */ -void DRW_gpencil_batch_cache_free(bGPdata *UNUSED(gpd)) +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number) { - return; -} - -/* wrapper to clear cache */ -void DRW_gpencil_freecache(struct Object *ob) -{ - if ((ob) && (ob->type == OB_GPENCIL)) { - gpencil_batch_cache_clear(ob->runtime.gpencil_cache); - MEM_SAFE_FREE(ob->runtime.gpencil_cache); - bGPdata *gpd = (bGPdata *)ob->data; - if (gpd) { - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + if (number >= 0) { + GPENCIL_tLayer *layer = tgp_ob->layers.first; + while (layer != NULL) { + if (layer->layer_id == number) { + return layer; + } + layer = layer->next; } } - - /* clear all frames evaluated data */ - for (int i = 0; i < ob->runtime.gpencil_tot_layers; i++) { - bGPDframe *gpf_eval = &ob->runtime.gpencil_evaluated_frames[i]; - BKE_gpencil_free_frame_runtime_data(gpf_eval); - } - - ob->runtime.gpencil_tot_layers = 0; - MEM_SAFE_FREE(ob->runtime.gpencil_evaluated_frames); + return NULL; } + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c deleted file mode 100644 index a2016b9c1e6..00000000000 --- a/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * 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) 2008, Blender Foundation - * This is a new part of Blender - */ - -/** \file - * \ingroup draw - */ - -#include "BLI_polyfill_2d.h" -#include "BLI_math_color.h" - -#include "DNA_meshdata_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_screen_types.h" -#include "DNA_view3d_types.h" - -#include "BKE_deform.h" -#include "BKE_gpencil.h" - -#include "DRW_render.h" - -#include "ED_gpencil.h" -#include "ED_view3d.h" - -#include "UI_resources.h" - -#include "gpencil_engine.h" - -/* Helper to add stroke point to vbo */ -static void gpencil_set_stroke_point(GPUVertBuf *vbo, - const bGPDspoint *pt, - int idx, - uint pos_id, - uint color_id, - uint thickness_id, - uint uvdata_id, - short thickness, - const float ink[4]) -{ - - float alpha = ink[3] * pt->strength; - CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); - float col[4]; - ARRAY_SET_ITEMS(col, ink[0], ink[1], ink[2], alpha); - - GPU_vertbuf_attr_set(vbo, color_id, idx, col); - - /* transfer both values using the same shader variable */ - float uvdata[2] = {pt->uv_fac, pt->uv_rot}; - GPU_vertbuf_attr_set(vbo, uvdata_id, idx, uvdata); - - /* the thickness of the stroke must be affected by zoom, so a pixel scale is calculated */ - float thick = max_ff(pt->pressure * thickness, 1.0f); - GPU_vertbuf_attr_set(vbo, thickness_id, idx, &thick); - - GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); -} - -/* Helper to add buffer_stroke point to vbo */ -static void gpencil_set_buffer_stroke_point(GPUVertBuf *vbo, - const bGPDspoint *pt, - int idx, - uint pos_id, - uint color_id, - uint thickness_id, - uint uvdata_id, - uint prev_pos_id, - const float ref_pt[3], - short thickness, - const float ink[4]) -{ - - float alpha = ink[3] * pt->strength; - CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); - float col[4]; - ARRAY_SET_ITEMS(col, ink[0], ink[1], ink[2], alpha); - - GPU_vertbuf_attr_set(vbo, color_id, idx, col); - - /* transfer both values using the same shader variable */ - float uvdata[2] = {pt->uv_fac, pt->uv_rot}; - GPU_vertbuf_attr_set(vbo, uvdata_id, idx, uvdata); - - /* the thickness of the stroke must be affected by zoom, so a pixel scale is calculated */ - float thick = max_ff(pt->pressure * thickness, 1.0f); - GPU_vertbuf_attr_set(vbo, thickness_id, idx, &thick); - - GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); - /* reference point to follow drawing path */ - GPU_vertbuf_attr_set(vbo, prev_pos_id, idx, ref_pt); -} - -/* Helper to add a new fill point and texture coordinates to vertex buffer */ -static void gpencil_set_fill_point(GPUVertBuf *vbo, - int idx, - bGPDspoint *pt, - const float fcolor[4], - const float uv[2], - uint pos_id, - uint color_id, - uint text_id) -{ - GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); - GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor); - GPU_vertbuf_attr_set(vbo, text_id, idx, uv); -} - -static void gpencil_vbo_ensure_size(GpencilBatchCacheElem *be, int totvertex) -{ - if (be->vbo->vertex_alloc <= be->vbo_len + totvertex) { - uint newsize = be->vbo->vertex_alloc + - (((totvertex / GPENCIL_VBO_BLOCK_SIZE) + 1) * GPENCIL_VBO_BLOCK_SIZE); - GPU_vertbuf_data_resize(be->vbo, newsize); - } -} - -static void gpencil_elem_format_ensure(GpencilBatchCacheElem *be) -{ - if (be->format == NULL) { - be->format = MEM_callocN(sizeof(GPUVertFormat), __func__); - } -} - -/* create batch geometry data for points stroke shader */ -void gpencil_get_point_geom(GpencilBatchCacheElem *be, - bGPDstroke *gps, - short thickness, - const float ink[4], - const int alignment_mode) -{ - int totvertex = gps->totpoints; - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - be->prev_pos_id = GPU_vertformat_attr_add( - be->format, "prev_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* draw stroke curve */ - const bGPDspoint *pt = gps->points; - float alpha; - float col[4]; - - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* set point */ - alpha = ink[3] * pt->strength; - CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); - ARRAY_SET_ITEMS(col, ink[0], ink[1], ink[2], alpha); - - float thick = max_ff(pt->pressure * thickness, 1.0f); - - GPU_vertbuf_attr_set(be->vbo, be->color_id, be->vbo_len, col); - GPU_vertbuf_attr_set(be->vbo, be->thickness_id, be->vbo_len, &thick); - - /* transfer both values using the same shader variable */ - float uvdata[2] = {pt->uv_fac, pt->uv_rot}; - GPU_vertbuf_attr_set(be->vbo, be->uvdata_id, be->vbo_len, uvdata); - - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - - /* use previous point to determine stroke direction */ - bGPDspoint *pt2 = NULL; - float fpt[3]; - if (alignment_mode != GP_STYLE_FOLLOW_PATH) { - /* add small offset to get a vector */ - copy_v3_v3(fpt, &pt->x); - fpt[0] += 0.00001f; - fpt[1] += 0.00001f; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - else { - if (i == 0) { - if (gps->totpoints > 1) { - /* extrapolate a point before first point */ - pt2 = &gps->points[1]; - interp_v3_v3v3(fpt, &pt2->x, &pt->x, 1.5f); - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - else { - /* add small offset to get a vector */ - copy_v3_v3(fpt, &pt->x); - fpt[0] += 0.00001f; - fpt[1] += 0.00001f; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, fpt); - } - } - else { - pt2 = &gps->points[i - 1]; - GPU_vertbuf_attr_set(be->vbo, be->prev_pos_id, be->vbo_len, &pt2->x); - } - } - be->vbo_len++; - } -} - -/* create batch geometry data for stroke shader */ -void gpencil_get_stroke_geom(struct GpencilBatchCacheElem *be, - bGPDstroke *gps, - short thickness, - const float ink[4]) -{ - bGPDspoint *points = gps->points; - int totpoints = gps->totpoints; - /* if cyclic needs more vertex */ - int cyclic_add = (gps->flag & GP_STROKE_CYCLIC) ? 1 : 0; - int totvertex = totpoints + cyclic_add + 2; - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* draw stroke curve */ - const bGPDspoint *pt = points; - for (int i = 0; i < totpoints; i++, pt++) { - /* first point for adjacency (not drawn) */ - if (i == 0) { - if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) { - gpencil_set_stroke_point(be->vbo, - &points[totpoints - 1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - else { - gpencil_set_stroke_point(be->vbo, - &points[1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - } - /* set point */ - gpencil_set_stroke_point(be->vbo, - pt, - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - - if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) { - /* draw line to first point to complete the cycle */ - gpencil_set_stroke_point(be->vbo, - &points[0], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - /* now add adjacency point (not drawn) */ - gpencil_set_stroke_point(be->vbo, - &points[1], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } - /* last adjacency point (not drawn) */ - else { - gpencil_set_stroke_point(be->vbo, - &points[totpoints - 2], - be->vbo_len, - be->pos_id, - be->color_id, - be->thickness_id, - be->uvdata_id, - thickness, - ink); - be->vbo_len++; - } -} - -/* create batch geometry data for stroke shader */ -void gpencil_get_fill_geom(struct GpencilBatchCacheElem *be, - Object *ob, - bGPDstroke *gps, - const float color[4]) -{ - BLI_assert(gps->totpoints >= 3); - - /* Calculate triangles cache for filling area (must be done only after changes) */ - if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) || - (gps->triangles == NULL)) { - BKE_gpencil_triangulate_stroke_fill((bGPdata *)ob->data, gps); - } - - BLI_assert(gps->tot_triangles >= 1); - int totvertex = gps->tot_triangles * 3; - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->uvdata_id = GPU_vertformat_attr_add( - be->format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, totvertex); - - /* Draw all triangles for filling the polygon (cache must be calculated before) */ - bGPDtriangle *stroke_triangle = gps->triangles; - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - gpencil_set_fill_point(be->vbo, - be->vbo_len, - &gps->points[stroke_triangle->verts[j]], - color, - stroke_triangle->uv[j], - be->pos_id, - be->color_id, - be->uvdata_id); - be->vbo_len++; - } - } -} - -/* create batch geometry data for current buffer stroke shader */ -GPUBatch *gpencil_get_buffer_stroke_geom(bGPdata *gpd, short thickness) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - RegionView3D *rv3d = draw_ctx->rv3d; - ToolSettings *ts = scene->toolsettings; - Object *ob = draw_ctx->obact; - - tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - /* if cyclic needs more vertex */ - int cyclic_add = (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC) ? 1 : 0; - int totvertex = totpoints + cyclic_add + 2; - - static GPUVertFormat format = {0}; - static uint pos_id, color_id, thickness_id, uvdata_id; - if (format.attr_len == 0) { - pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - thickness_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, totvertex); - - /* draw stroke curve */ - const tGPspoint *tpt = points; - bGPDspoint pt, pt2, pt3; - int idx = 0; - - /* get origin to reproject point */ - float origin[3]; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); - - for (int i = 0; i < totpoints; i++, tpt++) { - ED_gpencil_tpoint_to_point(region, origin, tpt, &pt); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); - - /* first point for adjacency (not drawn) */ - if (i == 0) { - if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) { - ED_gpencil_tpoint_to_point(region, origin, &points[totpoints - 1], &pt2); - gpencil_set_stroke_point(vbo, - &pt2, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - thickness, - gpd->runtime.scolor); - idx++; - } - else { - ED_gpencil_tpoint_to_point(region, origin, &points[1], &pt2); - gpencil_set_stroke_point(vbo, - &pt2, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - thickness, - gpd->runtime.scolor); - idx++; - } - } - - /* set point */ - gpencil_set_stroke_point( - vbo, &pt, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - - /* last adjacency point (not drawn) */ - if (gpd->runtime.sbuffer_sflag & GP_STROKE_CYCLIC && totpoints > 2) { - /* draw line to first point to complete the cycle */ - ED_gpencil_tpoint_to_point(region, origin, &points[0], &pt2); - gpencil_set_stroke_point( - vbo, &pt2, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - /* now add adjacency point (not drawn) */ - ED_gpencil_tpoint_to_point(region, origin, &points[1], &pt3); - gpencil_set_stroke_point( - vbo, &pt3, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - /* last adjacency point (not drawn) */ - else { - ED_gpencil_tpoint_to_point(region, origin, &points[totpoints - 2], &pt2); - gpencil_set_stroke_point( - vbo, &pt2, idx, pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); - idx++; - } - - return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -/* create batch geometry data for current buffer point shader */ -GPUBatch *gpencil_get_buffer_point_geom(bGPdata *gpd, short thickness) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - RegionView3D *rv3d = draw_ctx->rv3d; - ToolSettings *ts = scene->toolsettings; - Object *ob = draw_ctx->obact; - - tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - - static GPUVertFormat format = {0}; - static uint pos_id, color_id, thickness_id, uvdata_id, prev_pos_id; - if (format.attr_len == 0) { - pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - thickness_id = GPU_vertformat_attr_add(&format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uvdata_id = GPU_vertformat_attr_add(&format, "uvdata", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - prev_pos_id = GPU_vertformat_attr_add(&format, "prev_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, totpoints); - - /* draw stroke curve */ - const tGPspoint *tpt = points; - bGPDspoint pt; - int idx = 0; - - /* get origin to reproject point */ - float origin[3]; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); - - for (int i = 0; i < totpoints; i++, tpt++) { - ED_gpencil_tpoint_to_point(region, origin, tpt, &pt); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); - - /* use previous point to determine stroke direction (drawing path) */ - bGPDspoint pt2; - float ref_pt[3]; - - if (i == 0) { - if (totpoints > 1) { - /* extrapolate a point before first point */ - tGPspoint *tpt2 = &points[1]; - ED_gpencil_tpoint_to_point(region, origin, tpt2, &pt2); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt2); - - interp_v3_v3v3(ref_pt, &pt2.x, &pt.x, 1.5f); - } - else { - copy_v3_v3(ref_pt, &pt.x); - } - } - else { - tGPspoint *tpt2 = &points[i - 1]; - ED_gpencil_tpoint_to_point(region, origin, tpt2, &pt2); - ED_gp_project_point_to_plane(scene, ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt2); - - copy_v3_v3(ref_pt, &pt2.x); - } - - /* set point */ - gpencil_set_buffer_stroke_point(vbo, - &pt, - idx, - pos_id, - color_id, - thickness_id, - uvdata_id, - prev_pos_id, - ref_pt, - thickness, - gpd->runtime.scolor); - idx++; - } - - return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -/* create batch geometry data for current buffer control point shader */ -GPUBatch *gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd) -{ - bGPDcontrolpoint *cps = gpd->runtime.cp_points; - int totpoints = gpd->runtime.tot_cp_points; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - - if (ts->gp_sculpt.guide.use_guide) { - totpoints++; - } - - static GPUVertFormat format = {0}; - static uint pos_id, color_id, size_id; - if (format.attr_len == 0) { - pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, totpoints); - - int idx = 0; - for (int i = 0; i < gpd->runtime.tot_cp_points; i++) { - bGPDcontrolpoint *cp = &cps[i]; - - GPU_vertbuf_attr_set(vbo, color_id, idx, cp->color); - - /* scale size */ - float size = cp->size * 0.8f; - GPU_vertbuf_attr_set(vbo, size_id, idx, &size); - - GPU_vertbuf_attr_set(vbo, pos_id, idx, &cp->x); - idx++; - } - - if (ts->gp_sculpt.guide.use_guide) { - float size = 10 * 0.8f; - float color[4]; - float position[3]; - if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_CUSTOM) { - UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); - copy_v3_v3(position, ts->gp_sculpt.guide.location); - } - else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT && - ts->gp_sculpt.guide.reference_object != NULL) { - UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color); - copy_v3_v3(position, ts->gp_sculpt.guide.reference_object->loc); - } - else { - UI_GetThemeColor4fv(TH_REDALERT, color); - copy_v3_v3(position, scene->cursor.location); - } - GPU_vertbuf_attr_set(vbo, pos_id, idx, position); - GPU_vertbuf_attr_set(vbo, size_id, idx, &size); - GPU_vertbuf_attr_set(vbo, color_id, idx, color); - } - - return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -/* create batch geometry data for current buffer fill shader */ -GPUBatch *gpencil_get_buffer_fill_geom(bGPdata *gpd) -{ - if (gpd == NULL) { - return NULL; - } - - const tGPspoint *points = gpd->runtime.sbuffer; - int totpoints = gpd->runtime.sbuffer_used; - if (totpoints < 3) { - return NULL; - } - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ARegion *region = draw_ctx->region; - ToolSettings *ts = scene->toolsettings; - Object *ob = draw_ctx->obact; - - /* get origin to reproject point */ - float origin[3]; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); - - int tot_triangles = totpoints - 2; - /* allocate memory for temporary areas */ - uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, __func__); - float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, __func__); - - /* Convert points to array and triangulate - * Here a cache is not used because while drawing the information changes all the time, so the - * cache would be recalculated constantly, so it is better to do direct calculation for each - * function call - */ - for (int i = 0; i < totpoints; i++) { - const tGPspoint *pt = &points[i]; - points2d[i][0] = pt->x; - points2d[i][1] = pt->y; - } - BLI_polyfill_calc(points2d, (uint)totpoints, 0, tmp_triangles); - - static GPUVertFormat format = {0}; - static uint pos_id, color_id; - if (format.attr_len == 0) { - pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - - /* draw triangulation data */ - if (tot_triangles > 0) { - GPU_vertbuf_data_alloc(vbo, tot_triangles * 3); - - const tGPspoint *tpt; - bGPDspoint pt; - - int idx = 0; - for (int i = 0; i < tot_triangles; i++) { - for (int j = 0; j < 3; j++) { - tpt = &points[tmp_triangles[i][j]]; - ED_gpencil_tpoint_to_point(region, origin, tpt, &pt); - GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt.x); - GPU_vertbuf_attr_set(vbo, color_id, idx, gpd->runtime.sfill); - idx++; - } - } - } - - /* clear memory */ - if (tmp_triangles) { - MEM_freeN(tmp_triangles); - } - if (points2d) { - MEM_freeN(points2d); - } - - return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -/* Draw selected verts for strokes being edited */ -void gpencil_get_edit_geom(struct GpencilBatchCacheElem *be, - bGPDstroke *gps, - float alpha, - short dflag) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Object *ob = draw_ctx->obact; - bGPdata *gpd = ob->data; - const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - int vgindex = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, vgindex)) { - vgindex = -1; - } - - /* Get size of verts: - * - The selected state needs to be larger than the unselected state so that - * they stand out more. - * - We use the theme setting for size of the unselected verts - */ - float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); - float vsize; - if ((int)bsize > 8) { - vsize = 10.0f; - bsize = 8.0f; - } - else { - vsize = bsize + 2; - } - - /* for now, we assume that the base color of the points is not too close to the real color */ - float selectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); - selectColor[3] = alpha; - - float unselectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX, unselectColor); - unselectColor[3] = alpha; - - float linecolor[4]; - copy_v4_v4(linecolor, gpd->line_color); - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - be->thickness_id = GPU_vertformat_attr_add( - be->format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, gps->totpoints); - - /* Draw start and end point differently if enabled stroke direction hint */ - bool show_direction_hint = (dflag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1); - - /* Draw all the stroke points (selected or not) */ - bGPDspoint *pt = gps->points; - MDeformVert *dvert = gps->dvert; - - float fcolor[4]; - float fsize = 0; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* weight paint */ - if (is_weight_paint) { - float weight = (dvert && dvert->dw && (vgindex > -1)) ? - BKE_defvert_find_weight(dvert, vgindex) : - 0.0f; - float hue = 2.0f * (1.0f - weight) / 3.0f; - hsv_to_rgb(hue, 1.0f, 1.0f, &selectColor[0], &selectColor[1], &selectColor[2]); - selectColor[3] = 1.0f; - copy_v4_v4(fcolor, selectColor); - fsize = vsize; - } - else { - if (show_direction_hint && i == 0) { - /* start point in green bigger */ - ARRAY_SET_ITEMS(fcolor, 0.0f, 1.0f, 0.0f, 1.0f); - fsize = vsize + 4; - } - else if (show_direction_hint && (i == gps->totpoints - 1)) { - /* end point in red smaller */ - ARRAY_SET_ITEMS(fcolor, 1.0f, 0.0f, 0.0f, 1.0f); - fsize = vsize + 1; - } - else if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { - ARRAY_SET_ITEMS(fcolor, linecolor[0], linecolor[1], linecolor[2], selectColor[3]); - mul_v4_fl(fcolor, 0.9f); - copy_v4_v4(fcolor, fcolor); - fsize = vsize * 0.8f; - } - else if (pt->flag & GP_SPOINT_SELECT) { - copy_v4_v4(fcolor, selectColor); - fsize = vsize; - } - else { - copy_v4_v4(fcolor, unselectColor); - fsize = bsize; - } - } - - GPU_vertbuf_attr_set(be->vbo, be->color_id, be->vbo_len, fcolor); - GPU_vertbuf_attr_set(be->vbo, be->thickness_id, be->vbo_len, &fsize); - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - be->vbo_len++; - if (gps->dvert != NULL) { - dvert++; - } - } -} - -/* Draw lines for strokes being edited */ -void gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, - bGPDstroke *gps, - float alpha, - const bool hide_select) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Object *ob = draw_ctx->obact; - bGPdata *gpd = ob->data; - const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); - - int vgindex = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, vgindex)) { - vgindex = -1; - } - - float selectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); - selectColor[3] = alpha; - float linecolor[4]; - copy_v4_v4(linecolor, gpd->line_color); - - if (be->vbo == NULL) { - gpencil_elem_format_ensure(be); - be->pos_id = GPU_vertformat_attr_add(be->format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - be->color_id = GPU_vertformat_attr_add(be->format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - be->vbo = GPU_vertbuf_create_with_format(be->format); - GPU_vertbuf_data_alloc(be->vbo, be->tot_vertex); - be->vbo_len = 0; - } - gpencil_vbo_ensure_size(be, gps->totpoints); - - /* Draw all the stroke lines (selected or not) */ - bGPDspoint *pt = gps->points; - MDeformVert *dvert = gps->dvert; - - float fcolor[4]; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* weight paint */ - if (is_weight_paint) { - float weight = (dvert && dvert->dw && (vgindex > -1)) ? - BKE_defvert_find_weight(dvert, vgindex) : - 0.0f; - float hue = 2.0f * (1.0f - weight) / 3.0f; - hsv_to_rgb(hue, 1.0f, 1.0f, &selectColor[0], &selectColor[1], &selectColor[2]); - selectColor[3] = 1.0f; - copy_v4_v4(fcolor, selectColor); - } - else { - if ((pt->flag & GP_SPOINT_SELECT) && (!hide_select)) { - copy_v4_v4(fcolor, selectColor); - } - else { - copy_v4_v4(fcolor, linecolor); - } - } - - GPU_vertbuf_attr_set(be->vbo, be->color_id, be->vbo_len, fcolor); - GPU_vertbuf_attr_set(be->vbo, be->pos_id, be->vbo_len, &pt->x); - be->vbo_len++; - - if (gps->dvert != NULL) { - dvert++; - } - } -} - -static void set_grid_point(GPUVertBuf *vbo, - int idx, - const float col_grid[4], - uint pos_id, - uint color_id, - float v1, - float v2, - const int axis) -{ - GPU_vertbuf_attr_set(vbo, color_id, idx, col_grid); - - float pos[3]; - /* Set the grid in the selected axis */ - switch (axis) { - case GP_LOCKAXIS_X: { - ARRAY_SET_ITEMS(pos, 0.0f, v1, v2); - break; - } - case GP_LOCKAXIS_Y: { - ARRAY_SET_ITEMS(pos, v1, 0.0f, v2); - break; - } - case GP_LOCKAXIS_Z: - default: /* view aligned */ - { - ARRAY_SET_ITEMS(pos, v1, v2, 0.0f); - break; - } - } - - GPU_vertbuf_attr_set(vbo, pos_id, idx, pos); -} - -/* Draw grid lines */ -GPUBatch *gpencil_get_grid(Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - View3D *v3d = draw_ctx->v3d; - bGPdata *gpd = (bGPdata *)ob->data; - const bool do_center = (gpd->grid.lines <= 0) ? false : true; - - float col_grid[4]; - - /* verify we have something to draw and valid values */ - if (gpd->grid.scale[0] == 0.0f) { - gpd->grid.scale[0] = 1.0f; - } - if (gpd->grid.scale[1] == 0.0f) { - gpd->grid.scale[1] = 1.0f; - } - - if (v3d->overlay.gpencil_grid_opacity < 0.1f) { - v3d->overlay.gpencil_grid_opacity = 0.1f; - } - - /* set color */ - copy_v3_v3(col_grid, gpd->grid.color); - col_grid[3] = v3d->overlay.gpencil_grid_opacity; - - const int axis = ts->gp_sculpt.lock_axis; - - const char *grid_unit = NULL; - const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; - const float grid_w = gpd->grid.scale[0] * ED_scene_grid_scale(scene, &grid_unit); - const float grid_h = gpd->grid.scale[1] * ED_scene_grid_scale(scene, &grid_unit); - const float space_w = (grid_w / gridlines); - const float space_h = (grid_h / gridlines); - const float offset[2] = {gpd->grid.offset[0], gpd->grid.offset[1]}; - - const uint vertex_len = 2 * (gridlines * 4 + 2); - - static GPUVertFormat format = {0}; - static uint pos_id, color_id; - if (format.attr_len == 0) { - pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(vbo, vertex_len); - - int idx = 0; - - for (int a = 1; a <= gridlines; a++) { - const float line_w = a * space_w; - const float line_h = a * space_h; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], -line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], -line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], +line_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], +line_h + offset[1], axis); - idx++; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -line_w + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -line_w + offset[0], +grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +line_w + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +line_w + offset[0], +grid_h + offset[1], axis); - idx++; - } - /* center lines */ - if (do_center) { - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, -grid_w + offset[0], 0.0f + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, +grid_w + offset[0], 0.0f + offset[1], axis); - idx++; - - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, 0.0f + offset[0], -grid_h + offset[1], axis); - idx++; - set_grid_point( - vbo, idx, col_grid, pos_id, color_id, 0.0f + offset[0], +grid_h + offset[1], axis); - idx++; - } - return GPU_batch_create_ex(GPU_PRIM_LINES, vbo, NULL, GPU_BATCH_OWNS_VBO); -} diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c new file mode 100644 index 00000000000..77baadfc83a --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -0,0 +1,502 @@ +/* + * 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. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "DNA_light_types.h" + +#include "BKE_image.h" + +#include "BLI_hash.h" +#include "BLI_math_color.h" +#include "BLI_memblock.h" + +#include "GPU_uniformbuffer.h" + +#include "IMB_imbuf_types.h" + +#include "gpencil_engine.h" + +/* -------------------------------------------------------------------- */ +/** \name Material + * \{ */ + +static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_PrivateData *pd) +{ + GPENCIL_MaterialPool *matpool = BLI_memblock_alloc(pd->gp_material_pool); + matpool->next = NULL; + matpool->used_count = 0; + if (matpool->ubo == NULL) { + matpool->ubo = GPU_uniformbuffer_create(sizeof(matpool->mat_data), NULL, NULL); + } + pd->last_material_pool = matpool; + return matpool; +} + +static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_premult) +{ + ImBuf *ibuf; + ImageUser iuser = {NULL}; + struct GPUTexture *gpu_tex = NULL; + void *lock; + + iuser.ok = true; + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf != NULL && ibuf->rect != NULL) { + gpu_tex = GPU_texture_from_blender(image, &iuser, ibuf, GL_TEXTURE_2D); + *r_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); + } + BKE_image_release_ibuf(image, ibuf, lock); + + return gpu_tex; +} + +static void gpencil_uv_transform_get(const float ofs[2], + const float scale[2], + const float rotation, + float r_uvmat[3][2]) +{ + /* OPTI this could use 3x2 matrices and reduce the number of operations drastically. */ + float mat[4][4]; + unit_m4(mat); + /* Offset to center. */ + translate_m4(mat, 0.5f, 0.5f, 0.0f); + /* Reversed order. */ + rescale_m4(mat, (float[3]){1.0f / scale[0], 1.0f / scale[1], 0.0}); + rotate_m4(mat, 'Z', -rotation); + translate_m4(mat, ofs[0], ofs[1], 0.0f); + /* Convert to 3x2 */ + copy_v2_v2(r_uvmat[0], mat[0]); + copy_v2_v2(r_uvmat[1], mat[1]); + copy_v2_v2(r_uvmat[2], mat[3]); +} + +#define HSV_SATURATION 0.5 +#define HSV_VALUE 0.8 + +static void gpencil_object_random_color_get(const Object *ob, float r_color[3]) +{ + /* Duplicated from workbench_material.c */ + uint hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); + if (ob->id.lib) { + hash = (hash * 13) ^ BLI_ghashutil_strhash_p_murmur(ob->id.lib->name); + } + float hue = BLI_hash_int_01(hash); + float hsv[3] = {hue, HSV_SATURATION, HSV_VALUE}; + hsv_to_rgb_v(hsv, r_color); +} + +static void gpencil_shade_color(float color[3]) +{ + /* This is scene refered color, not gamma corrected and not per perceptual. + * So we lower the threshold a bit. (1.0 / 3.0) */ + if (color[0] + color[1] + color[2] > 1.1) { + add_v3_fl(color, -0.25f); + } + else { + add_v3_fl(color, 0.15f); + } + CLAMP3(color, 0.0f, 1.0f); +} + +/* Apply all overrides from the solid viewport mode to the GPencil material. */ +static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_PrivateData *pd, + Object *ob, + int color_type, + MaterialGPencilStyle *gp_style) +{ + static MaterialGPencilStyle gp_style_tmp; + + switch (color_type) { + case V3D_SHADING_MATERIAL_COLOR: + copy_v4_v4(gp_style_tmp.stroke_rgba, gp_style->stroke_rgba); + copy_v4_v4(gp_style_tmp.fill_rgba, gp_style->fill_rgba); + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + break; + case V3D_SHADING_TEXTURE_COLOR: + memcpy(&gp_style_tmp, gp_style, sizeof(*gp_style)); + gp_style = &gp_style_tmp; + if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) { + copy_v4_fl(gp_style->stroke_rgba, 1.0f); + gp_style->mix_stroke_factor = 0.0f; + } + + if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) { + copy_v4_fl(gp_style->fill_rgba, 1.0f); + gp_style->mix_factor = 0.0f; + } + else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) { + /* gp_style->fill_rgba is needed for correct gradient. */ + gp_style->mix_factor = 0.0f; + } + break; + case V3D_SHADING_RANDOM_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + gpencil_object_random_color_get(ob, gp_style->fill_rgba); + gp_style->fill_rgba[3] = 1.0f; + copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_SINGLE_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v3_v3(gp_style->fill_rgba, pd->v3d_single_color); + gp_style->fill_rgba[3] = 1.0f; + copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_OBJECT_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v4_v4(gp_style->fill_rgba, ob->color); + copy_v4_v4(gp_style->stroke_rgba, ob->color); + gpencil_shade_color(gp_style->stroke_rgba); + break; + case V3D_SHADING_VERTEX_COLOR: + gp_style = &gp_style_tmp; + gp_style->stroke_style = GP_MATERIAL_STROKE_STYLE_SOLID; + gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID; + copy_v4_fl(gp_style->fill_rgba, 1.0f); + copy_v4_fl(gp_style->stroke_rgba, 1.0f); + break; + default: + break; + } + return gp_style; +} + +/** + * Creates a linked list of material pool containing all materials assigned for a given object. + * We merge the material pools together if object does not contain a huge amount of materials. + * Also return an offset to the first material of the object in the ubo. + **/ +GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs) +{ + GPENCIL_MaterialPool *matpool = pd->last_material_pool; + + int mat_len = max_ii(1, ob->totcol); + + bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN); + + if (reuse_matpool) { + /* Share the matpool with other objects. Return offset to first material. */ + *ofs = matpool->used_count; + } + else { + matpool = gpencil_material_pool_add(pd); + *ofs = 0; + } + + /* Force vertex color in solid mode with vertex paint mode. Same behavior as meshes. */ + bGPdata *gpd = (bGPdata *)ob->data; + int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ? + V3D_SHADING_VERTEX_COLOR : + pd->v3d_color_type; + + GPENCIL_MaterialPool *pool = matpool; + for (int i = 0; i < mat_len; i++) { + if ((i > 0) && (pool->used_count == GP_MATERIAL_BUFFER_LEN)) { + pool->next = gpencil_material_pool_add(pd); + pool = pool->next; + } + int mat_id = pool->used_count++; + + gpMaterial *mat_data = &pool->mat_data[mat_id]; + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1); + + if (gp_style->mode == GP_MATERIAL_MODE_LINE) { + mat_data->flag = 0; + } + else { + switch (gp_style->alignment_mode) { + case GP_MATERIAL_FOLLOW_PATH: + mat_data->flag = GP_STROKE_ALIGNMENT_STROKE; + break; + case GP_MATERIAL_FOLLOW_OBJ: + mat_data->flag = GP_STROKE_ALIGNMENT_OBJECT; + break; + case GP_MATERIAL_FOLLOW_FIXED: + default: + mat_data->flag = GP_STROKE_ALIGNMENT_FIXED; + break; + } + + if (gp_style->mode == GP_MATERIAL_MODE_DOT) { + mat_data->flag |= GP_STROKE_DOTS; + } + } + + if ((gp_style->mode != GP_MATERIAL_MODE_LINE) || + (gp_style->flag & GP_MATERIAL_DISABLE_STENCIL)) { + mat_data->flag |= GP_STROKE_OVERLAP; + } + + gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style); + + /* Stroke Style */ + if ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && (gp_style->sima)) { + bool premul; + pool->tex_stroke[mat_id] = gpencil_image_texture_get(gp_style->sima, &premul); + mat_data->flag |= pool->tex_stroke[mat_id] ? GP_STROKE_TEXTURE_USE : 0; + mat_data->flag |= premul ? GP_STROKE_TEXTURE_PREMUL : 0; + copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba); + mat_data->stroke_texture_mix = 1.0f - gp_style->mix_stroke_factor; + mat_data->stroke_u_scale = 500.0f / gp_style->texture_pixsize; + } + else /* if (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_SOLID) */ { + pool->tex_stroke[mat_id] = NULL; + mat_data->flag &= ~GP_STROKE_TEXTURE_USE; + copy_v4_v4(mat_data->stroke_color, gp_style->stroke_rgba); + mat_data->stroke_texture_mix = 0.0f; + } + + /* Fill Style */ + if ((gp_style->fill_style == GP_MATERIAL_FILL_STYLE_TEXTURE) && (gp_style->ima)) { + bool use_clip = (gp_style->flag & GP_MATERIAL_TEX_CLAMP) != 0; + bool premul; + pool->tex_fill[mat_id] = gpencil_image_texture_get(gp_style->ima, &premul); + mat_data->flag |= pool->tex_fill[mat_id] ? GP_FILL_TEXTURE_USE : 0; + mat_data->flag |= premul ? GP_FILL_TEXTURE_PREMUL : 0; + mat_data->flag |= use_clip ? GP_FILL_TEXTURE_CLIP : 0; + gpencil_uv_transform_get(gp_style->texture_offset, + gp_style->texture_scale, + gp_style->texture_angle, + mat_data->fill_uv_transform); + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; + } + else if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_GRADIENT) { + bool use_radial = (gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL); + pool->tex_fill[mat_id] = NULL; + mat_data->flag |= GP_FILL_GRADIENT_USE; + mat_data->flag |= use_radial ? GP_FILL_GRADIENT_RADIAL : 0; + gpencil_uv_transform_get(gp_style->texture_offset, + gp_style->texture_scale, + gp_style->texture_angle, + mat_data->fill_uv_transform); + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + copy_v4_v4(mat_data->fill_mix_color, gp_style->mix_rgba); + mat_data->fill_texture_mix = 1.0f - gp_style->mix_factor; + if (gp_style->flag & GP_MATERIAL_FLIP_FILL) { + swap_v4_v4(mat_data->fill_color, mat_data->fill_mix_color); + } + } + else /* if (gp_style->fill_style == GP_MATERIAL_FILL_STYLE_SOLID) */ { + pool->tex_fill[mat_id] = NULL; + copy_v4_v4(mat_data->fill_color, gp_style->fill_rgba); + mat_data->fill_texture_mix = 0.0f; + } + } + + return matpool; +} + +void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, + int mat_id, + GPUTexture **r_tex_stroke, + GPUTexture **r_tex_fill, + GPUUniformBuffer **r_ubo_mat) +{ + GPENCIL_MaterialPool *matpool = first_pool; + int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN; + for (int i = 0; i < pool_id; i++) { + matpool = matpool->next; + } + mat_id = mat_id % GP_MATERIAL_BUFFER_LEN; + *r_ubo_mat = matpool->ubo; + if (r_tex_fill) { + *r_tex_fill = matpool->tex_fill[mat_id]; + } + if (r_tex_stroke) { + *r_tex_stroke = matpool->tex_stroke[mat_id]; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lights + * \{ */ + +GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd) +{ + GPENCIL_LightPool *lightpool = BLI_memblock_alloc(pd->gp_light_pool); + lightpool->light_used = 0; + /* Tag light list end. */ + lightpool->light_data[0].color[0] = -1.0; + if (lightpool->ubo == NULL) { + lightpool->ubo = GPU_uniformbuffer_create(sizeof(lightpool->light_data), NULL, NULL); + } + pd->last_light_pool = lightpool; + return lightpool; +} + +void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]) +{ + if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) { + return; + } + + gpLight *gp_light = &lightpool->light_data[lightpool->light_used]; + gp_light->type = GP_LIGHT_TYPE_AMBIENT; + copy_v3_v3(gp_light->color, color); + lightpool->light_used++; + + if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) { + /* Tag light list end. */ + gp_light[1].color[0] = -1.0f; + } +} + +static float light_power_get(const Light *la) +{ + if (la->type == LA_AREA) { + return 1.0f / (4.0f * M_PI); + } + else if (la->type == LA_SPOT || la->type == LA_LOCAL) { + return 1.0f / (4.0f * M_PI * M_PI); + } + else { + return 1.0f / M_PI; + } +} + +void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob) +{ + Light *la = (Light *)ob->data; + + if (lightpool->light_used >= GPENCIL_LIGHT_BUFFER_LEN) { + return; + } + + gpLight *gp_light = &lightpool->light_data[lightpool->light_used]; + float(*mat)[4] = (float(*)[4])gp_light->right; + + if (la->type == LA_SPOT) { + copy_m4_m4(mat, ob->imat); + gp_light->type = GP_LIGHT_TYPE_SPOT; + gp_light->spotsize = cosf(la->spotsize * 0.5f); + gp_light->spotblend = (1.0f - gp_light->spotsize) * la->spotblend; + } + else if (la->type == LA_AREA) { + /* Simulate area lights using a spot light. */ + normalize_m4_m4(mat, ob->obmat); + invert_m4(mat); + gp_light->type = GP_LIGHT_TYPE_SPOT; + gp_light->spotsize = cosf(M_PI * 0.5f); + gp_light->spotblend = (1.0f - gp_light->spotsize) * 1.0f; + } + else if (la->type == LA_SUN) { + normalize_v3_v3(gp_light->forward, ob->obmat[2]); + gp_light->type = GP_LIGHT_TYPE_SUN; + } + else { + gp_light->type = GP_LIGHT_TYPE_POINT; + } + copy_v4_v4(gp_light->position, ob->obmat[3]); + copy_v3_v3(gp_light->color, &la->r); + mul_v3_fl(gp_light->color, la->energy * light_power_get(la)); + + lightpool->light_used++; + + if (lightpool->light_used < GPENCIL_LIGHT_BUFFER_LEN) { + /* Tag light list end. */ + gp_light[1].color[0] = -1.0f; + } +} + +/** + * Creates a single pool containing all lights assigned (light linked) for a given object. + **/ +GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_PrivateData *pd, Object *UNUSED(ob)) +{ + GPENCIL_LightPool *lightpool = pd->last_light_pool; + + if (lightpool == NULL) { + lightpool = gpencil_light_pool_add(pd); + } + /* TODO(fclem) Light linking. */ + // gpencil_light_pool_populate(lightpool, ob); + + return lightpool; +} + +void gpencil_material_pool_free(void *storage) +{ + GPENCIL_MaterialPool *matpool = (GPENCIL_MaterialPool *)storage; + DRW_UBO_FREE_SAFE(matpool->ubo); +} + +void gpencil_light_pool_free(void *storage) +{ + GPENCIL_LightPool *lightpool = (GPENCIL_LightPool *)storage; + DRW_UBO_FREE_SAFE(lightpool->ubo); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Layer Data + * \{ */ + +static void gpencil_view_layer_data_free(void *storage) +{ + GPENCIL_ViewLayerData *vldata = (GPENCIL_ViewLayerData *)storage; + + BLI_memblock_destroy(vldata->gp_light_pool, gpencil_light_pool_free); + BLI_memblock_destroy(vldata->gp_material_pool, gpencil_material_pool_free); + BLI_memblock_destroy(vldata->gp_maskbit_pool, NULL); + BLI_memblock_destroy(vldata->gp_object_pool, NULL); + BLI_memblock_destroy(vldata->gp_layer_pool, NULL); + BLI_memblock_destroy(vldata->gp_vfx_pool, NULL); +} + +GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void) +{ + GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure( + &draw_engine_gpencil_type, gpencil_view_layer_data_free); + + /* NOTE(fclem) Putting this stuff in viewlayer means it is shared by all viewports. + * For now it is ok, but in the future, it could become a problem if we implement + * the caching system. */ + if (*vldata == NULL) { + *vldata = MEM_callocN(sizeof(**vldata), "GPENCIL_ViewLayerData"); + + (*vldata)->gp_light_pool = BLI_memblock_create(sizeof(GPENCIL_LightPool)); + (*vldata)->gp_material_pool = BLI_memblock_create(sizeof(GPENCIL_MaterialPool)); + (*vldata)->gp_maskbit_pool = BLI_memblock_create(BLI_BITMAP_SIZE(GP_MAX_MASKBITS)); + (*vldata)->gp_object_pool = BLI_memblock_create(sizeof(GPENCIL_tObject)); + (*vldata)->gp_layer_pool = BLI_memblock_create(sizeof(GPENCIL_tLayer)); + (*vldata)->gp_vfx_pool = BLI_memblock_create(sizeof(GPENCIL_tVfx)); + } + + return *vldata; +} + +/** \} */ diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c deleted file mode 100644 index d44aa5764b1..00000000000 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ /dev/null @@ -1,2071 +0,0 @@ -/* - * 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. - * - * Copyright 2017, Blender Foundation. - */ - -/** \file - * \ingroup draw - */ - -#include "DRW_render.h" - -#include "BKE_gpencil.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_image.h" -#include "BKE_material.h" -#include "BKE_paint.h" - -#include "BLI_hash.h" - -#include "ED_gpencil.h" - -#include "DNA_gpencil_types.h" -#include "DNA_material_types.h" -#include "DNA_view3d_types.h" - -/* If builtin shaders are needed */ -#include "GPU_shader.h" -#include "GPU_texture.h" - -/* For EvaluationContext... */ -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "IMB_imbuf_types.h" - -#include "gpencil_engine.h" - -#include "UI_resources.h" - -/* fill type to communicate to shader */ -#define SOLID 0 -#define GRADIENT 1 -#define RADIAL 2 -#define CHECKER 3 -#define TEXTURE 4 -#define PATTERN 5 - -/* Verify if must fade object or not. */ -static bool gpencil_fade_object_check(GPENCIL_StorageList *stl, Object *ob) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool is_overlay = (bool)((v3d) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && - (v3d->gp_flag & V3D_GP_SHOW_PAPER)); - - if ((!is_overlay) || (ob == draw_ctx->obact) || - ((v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL) == 0) || - (v3d->overlay.gpencil_paper_opacity == 1.0f)) { - return false; - } - - const bool playing = stl->storage->is_playing; - const bool is_render = (bool)stl->storage->is_render; - const bool is_mat_preview = (bool)stl->storage->is_mat_preview; - const bool is_select = (bool)(DRW_state_is_select() || DRW_state_is_depth()); - - return (bool)((!is_render) && (!playing) && (!is_mat_preview) && (!is_select)); -} - -/* Define Fade layer uniforms. */ -static void gpencil_set_fade_layer_uniforms( - GPENCIL_StorageList *stl, DRWShadingGroup *grp, Object *ob, bGPDlayer *gpl, const bool skip) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true; - const bool is_fade = (v3d) && (v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS) && - (draw_ctx->obact) && (draw_ctx->obact == ob) && - ((gpl->flag & GP_LAYER_ACTIVE) == 0); - - const bool playing = stl->storage->is_playing; - const bool is_render = (bool)stl->storage->is_render; - const bool is_mat_preview = (bool)stl->storage->is_mat_preview; - const bool is_select = (bool)(DRW_state_is_select() || DRW_state_is_depth()); - - /* If drawing or not fading layer, skip. */ - if ((!overlay) || (skip) || (!is_fade) || (is_render) || (playing) || (is_mat_preview) || - (is_select)) { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 0); - return; - } - - /* If layer is above active, use alpha (2) if below use mix with background (1). */ - if (stl->storage->is_ontop) { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 2); - } - else { - DRW_shgroup_uniform_int_copy(grp, "fade_layer", 1); - } - if (v3d) { - DRW_shgroup_uniform_vec3(grp, "fade_color", v3d->shading.background_color, 1); - DRW_shgroup_uniform_float(grp, "fade_layer_factor", &v3d->overlay.gpencil_fade_layer, 1); - } -} - -/* Define Fade object uniforms. */ -static void gpencil_set_fade_ob_uniforms(View3D *v3d, DRWShadingGroup *grp, bool status) -{ - DRW_shgroup_uniform_bool_copy(grp, "fade_ob", status); - if (v3d) { - DRW_shgroup_uniform_vec3(grp, "fade_color", v3d->shading.background_color, 1); - DRW_shgroup_uniform_float(grp, "fade_ob_factor", &v3d->overlay.gpencil_paper_opacity, 1); - } -} - -/* Get number of vertex for using in GPU VBOs */ -static void gpencil_calc_vertex(GPENCIL_StorageList *stl, - tGPencilObjectCache *cache_ob, - GpencilBatchCache *cache, - bGPdata *gpd) -{ - if ((!cache->is_dirty) || (gpd == NULL)) { - return; - } - - Object *ob = cache_ob->ob; - const bool main_onion = stl->storage->is_main_onion; - const bool playing = stl->storage->is_playing; - const bool overlay = stl->storage->is_main_overlay; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && overlay && - main_onion && !playing && gpencil_onion_active(gpd); - - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - - /* Onion skinning. */ - const int step = gpd->gstep; - const int mode = gpd->onion_mode; - const short onion_keytype = gpd->onion_keytype; - - cache_ob->tot_vertex = 0; - cache_ob->tot_triangles = 0; - int idx_eval = 0; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *init_gpf = NULL; - const bool is_onion = ((do_onion) && (gpl->onion_flag & GP_LAYER_ONIONSKIN)); - if (gpl->flag & GP_LAYER_HIDE) { - idx_eval++; - continue; - } - - /* Relative onion mode needs to find the frame range before. */ - int frame_from = -9999; - int frame_to = 9999; - if ((is_onion) && (mode == GP_ONION_MODE_RELATIVE)) { - /* 1) Found first Frame. */ - int idx = 0; - if (gpl->actframe) { - for (bGPDframe *gf = gpl->actframe->prev; gf; gf = gf->prev) { - idx++; - frame_from = gf->framenum; - if (idx >= step) { - break; - } - } - /* 2) Found last Frame. */ - idx = 0; - for (bGPDframe *gf = gpl->actframe->next; gf; gf = gf->next) { - idx++; - frame_to = gf->framenum; - if (idx >= gpd->gstep_next) { - break; - } - } - } - } - - /* If multiedit or onion skin need to count all frames of the layer. */ - if ((is_multiedit) || (is_onion)) { - init_gpf = gpl->frames.first; - } - else { - init_gpf = &ob->runtime.gpencil_evaluated_frames[idx_eval]; - } - - if (init_gpf == NULL) { - continue; - } - - for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (!is_onion) { - if ((!is_multiedit) || - ((is_multiedit) && ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)))) { - cache_ob->tot_vertex += gps->totpoints + 3; - cache_ob->tot_triangles += gps->totpoints - 1; - } - } - else { - bool select = ((is_multiedit) && - ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT))); - - if (!select) { - /* Only selected frames. */ - if ((mode == GP_ONION_MODE_SELECTED) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { - continue; - } - /* Verify keyframe type. */ - if ((onion_keytype > -1) && (gpf->key_type != onion_keytype)) { - continue; - } - /* Absolute range. */ - if (mode == GP_ONION_MODE_ABSOLUTE) { - if ((gpl->actframe) && (abs(gpl->actframe->framenum - gpf->framenum) > step)) { - continue; - } - } - /* Relative range. */ - if (mode == GP_ONION_MODE_RELATIVE) { - if ((gpf->framenum < frame_from) || (gpf->framenum > frame_to)) { - continue; - } - } - } - - cache_ob->tot_vertex += gps->totpoints + 3; - cache_ob->tot_triangles += gps->totpoints - 1; - } - } - - /* If not multiframe nor Onion skin, don't need follow counting. */ - if ((!is_multiedit) && (!is_onion)) { - break; - } - } - idx_eval++; - } - - cache->b_fill.tot_vertex = cache_ob->tot_triangles * 3; - cache->b_stroke.tot_vertex = cache_ob->tot_vertex; - cache->b_point.tot_vertex = cache_ob->tot_vertex; - cache->b_edit.tot_vertex = cache_ob->tot_vertex; - cache->b_edlin.tot_vertex = cache_ob->tot_vertex; -} - -/* Helper for doing all the checks on whether a stroke can be drawn */ -static bool gpencil_can_draw_stroke(struct MaterialGPencilStyle *gp_style, - const bGPDstroke *gps, - const bool onion, - const bool is_mat_preview) -{ - /* skip stroke if it doesn't have any valid data */ - if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) { - return false; - } - - /* if mat preview render always visible */ - if (is_mat_preview) { - return true; - } - - /* check if the color is visible */ - if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE) || - (onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) { - return false; - } - - /* stroke can be drawn */ - return true; -} - -/* recalc the internal geometry caches for fill and uvs */ -static void gpencil_recalc_geometry_caches(Object *ob, - bGPDlayer *gpl, - MaterialGPencilStyle *gp_style, - bGPDstroke *gps) -{ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - /* Calculate triangles cache for filling area (must be done only after changes) */ - if ((gps->tot_triangles == 0) || (gps->triangles == NULL)) { - if ((gps->totpoints > 2) && (gp_style->flag & GP_STYLE_FILL_SHOW) && - ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0) || - (gpl->blend_mode != eGplBlendMode_Regular))) { - BKE_gpencil_triangulate_stroke_fill((bGPdata *)ob->data, gps); - } - } - - /* calc uv data along the stroke */ - ED_gpencil_calc_stroke_uv(ob, gps); - - /* clear flag */ - gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; - } -} - -static void set_wireframe_color(Object *ob, - bGPDlayer *gpl, - View3D *v3d, - GPENCIL_StorageList *stl, - MaterialGPencilStyle *gp_style, - int id, - const bool is_fill) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - World *world = draw_ctx->scene->world; - - float color[4]; - if (((gp_style->stroke_rgba[3] < GPENCIL_ALPHA_OPACITY_THRESH) || - (((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0))) && - (gp_style->fill_rgba[3] >= GPENCIL_ALPHA_OPACITY_THRESH)) { - copy_v4_v4(color, gp_style->fill_rgba); - } - else { - copy_v4_v4(color, gp_style->stroke_rgba); - } - - /* wire color */ - if ((v3d) && (id > -1)) { - const char type = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - /* if fill and wire, use background color */ - if ((is_fill) && (stl->shgroups[id].shading_type[0] == OB_WIRE)) { - if (v3d->shading.background_type == V3D_SHADING_BACKGROUND_THEME) { - UI_GetThemeColor4fv(TH_BACK, stl->shgroups[id].wire_color); - stl->shgroups[id].wire_color[3] = 1.0f; - } - else if (v3d->shading.background_type == V3D_SHADING_BACKGROUND_WORLD) { - color[0] = world->horr; - color[1] = world->horg; - color[2] = world->horb; - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - else { - copy_v3_v3(color, v3d->shading.background_color); - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - return; - } - - /* strokes */ - switch (type) { - case V3D_SHADING_SINGLE_COLOR: { - if (stl->shgroups[id].shading_type[0] == OB_WIRE) { - UI_GetThemeColor4fv(TH_WIRE, color); - } - else { - copy_v3_v3(color, v3d->shading.single_color); - } - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - case V3D_SHADING_OBJECT_COLOR: { - copy_v4_v4(color, ob->color); - color[3] = 1.0f; - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - case V3D_SHADING_RANDOM_COLOR: { - uint gpl_hash = 1; - uint ob_hash = BLI_ghashutil_strhash_p_murmur(ob->id.name); - if (gpl) { - gpl_hash = BLI_ghashutil_strhash_p_murmur(gpl->info); - } - - float hue = BLI_hash_int_01(ob_hash * gpl_hash); - float hsv[3] = {hue, 0.40f, 0.8f}; - float wire_col[3]; - hsv_to_rgb_v(hsv, &wire_col[0]); - - copy_v3_v3(stl->shgroups[id].wire_color, wire_col); - stl->shgroups[id].wire_color[3] = 1.0f; - break; - } - default: { - copy_v4_v4(stl->shgroups[id].wire_color, color); - break; - } - } - } - else { - copy_v4_v4(stl->shgroups[id].wire_color, color); - } - - /* if solid, the alpha must be set to alpha */ - if (stl->shgroups[id].shading_type[0] == OB_SOLID) { - stl->shgroups[id].wire_color[3] = 1.0f; - } -} - -/* create shading group for filling */ -static DRWShadingGroup *gpencil_shgroup_fill_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - MaterialGPencilStyle *gp_style, - int id, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* e_data.gpencil_fill_sh */ - DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); - - DRW_shgroup_uniform_mat4(grp, "gpModelMatrix", obmat); - - DRW_shgroup_uniform_vec4(grp, "color2", gp_style->mix_rgba, 1); - - /* set style type */ - switch (gp_style->fill_style) { - case GP_STYLE_FILL_STYLE_SOLID: - stl->shgroups[id].fill_style = SOLID; - break; - case GP_STYLE_FILL_STYLE_GRADIENT: - if (gp_style->gradient_type == GP_STYLE_GRADIENT_LINEAR) { - stl->shgroups[id].fill_style = GRADIENT; - } - else { - stl->shgroups[id].fill_style = RADIAL; - } - break; - case GP_STYLE_FILL_STYLE_CHECKER: - stl->shgroups[id].fill_style = CHECKER; - break; - case GP_STYLE_FILL_STYLE_TEXTURE: - if (gp_style->flag & GP_STYLE_FILL_PATTERN) { - stl->shgroups[id].fill_style = PATTERN; - } - else { - stl->shgroups[id].fill_style = TEXTURE; - } - break; - default: - stl->shgroups[id].fill_style = GP_STYLE_FILL_STYLE_SOLID; - break; - } - DRW_shgroup_uniform_int(grp, "fill_type", &stl->shgroups[id].fill_style, 1); - - DRW_shgroup_uniform_float(grp, "mix_factor", &gp_style->mix_factor, 1); - - DRW_shgroup_uniform_float(grp, "gradient_angle", &gp_style->gradient_angle, 1); - DRW_shgroup_uniform_float(grp, "gradient_radius", &gp_style->gradient_radius, 1); - DRW_shgroup_uniform_float(grp, "pattern_gridsize", &gp_style->pattern_gridsize, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_scale", gp_style->gradient_scale, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_shift", gp_style->gradient_shift, 1); - - DRW_shgroup_uniform_float(grp, "texture_angle", &gp_style->texture_angle, 1); - DRW_shgroup_uniform_vec2(grp, "texture_scale", gp_style->texture_scale, 1); - DRW_shgroup_uniform_vec2(grp, "texture_offset", gp_style->texture_offset, 1); - DRW_shgroup_uniform_float(grp, "texture_opacity", &gp_style->texture_opacity, 1); - DRW_shgroup_uniform_float(grp, "layer_opacity", &gpl->opacity, 1); - - stl->shgroups[id].texture_mix = gp_style->flag & GP_STYLE_FILL_TEX_MIX ? 1 : 0; - DRW_shgroup_uniform_int(grp, "texture_mix", &stl->shgroups[id].texture_mix, 1); - - stl->shgroups[id].texture_flip = gp_style->flag & GP_STYLE_COLOR_FLIP_FILL ? 1 : 0; - DRW_shgroup_uniform_int(grp, "texture_flip", &stl->shgroups[id].texture_flip, 1); - - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", &stl->shgroups[id].xray_mode, 1); - DRW_shgroup_uniform_int(grp, "drawmode", (const int *)&gpd->draw_mode, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - /* shading type */ - stl->shgroups[id].shading_type[0] = GPENCIL_USE_SOLID(stl) ? (int)OB_RENDER : shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, true); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* image texture */ - if ((gp_style->flag & GP_STYLE_FILL_TEX_MIX) || - (gp_style->fill_style & GP_STYLE_FILL_STYLE_TEXTURE)) { - ImBuf *ibuf; - Image *image = gp_style->ima; - ImageUser iuser = {NULL}; - void *lock; - - iuser.ok = true; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(image, ibuf, NULL); - } - else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->ima, &iuser, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - stl->shgroups[id].texture_clamp = gp_style->flag & GP_STYLE_COLOR_TEX_CLAMP ? 1 : 0; - DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1); - - BKE_image_release_ibuf(image, ibuf, NULL); - } - } - else { - /* if no texture defined, need a blank texture to avoid errors in draw manager */ - DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture); - stl->shgroups[id].texture_clamp = 0; - DRW_shgroup_uniform_int(grp, "texture_clamp", &stl->shgroups[id].texture_clamp, 1); - } - - return grp; -} - -/* check if some onion is enabled */ -bool gpencil_onion_active(bGPdata *gpd) -{ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->onion_flag & GP_LAYER_ONIONSKIN) { - return true; - } - } - return false; -} - -/* create shading group for strokes */ -DRWShadingGroup *gpencil_shgroup_stroke_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - bGPDstroke *gps, - MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const float *viewport_size = DRW_viewport_size_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* e_data.gpencil_stroke_sh */ - DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); - - DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1); - - DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1); - - DRW_shgroup_uniform_mat4(grp, "gpModelMatrix", obmat); - - /* avoid wrong values */ - if ((gpd) && (gpd->pixfactor == 0.0f)) { - gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - } - - /* object scale and depth */ - if ((ob) && (id > -1)) { - stl->shgroups[id].obj_scale = scale; - DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1); - stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); - DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1); - - stl->shgroups[id].stroke_style = gp_style->stroke_style; - stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID; - if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) { - stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE; - if (gp_style->flag & GP_STYLE_STROKE_PATTERN) { - stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN; - } - } - DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1); - DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1); - - stl->shgroups[id].caps_mode[0] = gps->caps[0]; - stl->shgroups[id].caps_mode[1] = gps->caps[1]; - DRW_shgroup_uniform_int(grp, "caps_mode", &stl->shgroups[id].caps_mode[0], 2); - - stl->shgroups[id].gradient_f = gps->gradient_f; - copy_v2_v2(stl->shgroups[id].gradient_s, gps->gradient_s); - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->shgroups[id].gradient_f, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - stl->shgroups[id].shading_type[0] = (GPENCIL_USE_SOLID(stl) || onion) ? (int)OB_RENDER : - shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, false); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* mix stroke factor */ - stl->shgroups[id].mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->shgroups[id].mix_stroke_factor, 1); - } - else { - stl->storage->obj_scale = 1.0f; - stl->storage->keep_size = 0; - stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR; - DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1); - DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1); - DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1); - if (gpd) { - DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1); - } - else { - DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1); - } - const int zero[2] = {0, 0}; - DRW_shgroup_uniform_int(grp, "caps_mode", &zero[0], 2); - - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->storage->gradient_f, 1); - - /* viewport x-ray */ - DRW_shgroup_uniform_int(grp, "viewport_xray", &stl->storage->is_xray, 1); - DRW_shgroup_uniform_int(grp, "shading_type", (const int *)&stl->storage->shade_render, 2); - - /* mix stroke factor */ - stl->storage->mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->storage->mix_stroke_factor, 1); - } - - DRW_shgroup_uniform_vec4(grp, "colormix", gp_style->stroke_rgba, 1); - - if ((gpd) && (id > -1)) { - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", &stl->shgroups[id].xray_mode, 1); - } - else { - /* for drawing always on predefined z-depth */ - DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); - } - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, true); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, false); - - /* image texture for pattern */ - if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) { - ImBuf *ibuf; - Image *image = gp_style->sima; - ImageUser iuser = {NULL}; - void *lock; - - iuser.ok = true; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(image, ibuf, NULL); - } - else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - BKE_image_release_ibuf(image, ibuf, NULL); - } - } - else { - /* if no texture defined, need a blank texture to avoid errors in draw manager */ - DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture); - } - - return grp; -} - -/* create shading group for points */ -static DRWShadingGroup *gpencil_shgroup_point_create(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - DRWPass *pass, - GPUShader *shader, - Object *ob, - float (*obmat)[4], - bGPdata *gpd, - bGPDlayer *gpl, - bGPDstroke *gps, - MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const float *viewport_size = DRW_viewport_size_get(); - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - - /* e_data.gpencil_stroke_sh */ - DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); - - DRW_shgroup_uniform_vec2(grp, "Viewport", viewport_size, 1); - DRW_shgroup_uniform_float(grp, "pixsize", stl->storage->pixsize, 1); - - DRW_shgroup_uniform_mat4(grp, "gpModelMatrix", obmat); - - /* avoid wrong values */ - if ((gpd) && (gpd->pixfactor == 0.0f)) { - gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; - } - - /* object scale and depth */ - if ((ob) && (id > -1)) { - stl->shgroups[id].obj_scale = scale; - DRW_shgroup_uniform_float(grp, "objscale", &stl->shgroups[id].obj_scale, 1); - stl->shgroups[id].keep_size = (int)((gpd) && (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); - DRW_shgroup_uniform_int(grp, "keep_size", &stl->shgroups[id].keep_size, 1); - - stl->shgroups[id].mode = gp_style->mode; - stl->shgroups[id].stroke_style = gp_style->stroke_style; - stl->shgroups[id].color_type = GPENCIL_COLOR_SOLID; - if ((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) { - stl->shgroups[id].color_type = GPENCIL_COLOR_TEXTURE; - if (gp_style->flag & GP_STYLE_STROKE_PATTERN) { - stl->shgroups[id].color_type = GPENCIL_COLOR_PATTERN; - } - } - DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1); - DRW_shgroup_uniform_int(grp, "mode", &stl->shgroups[id].mode, 1); - DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1); - - stl->shgroups[id].gradient_f = gps->gradient_f; - copy_v2_v2(stl->shgroups[id].gradient_s, gps->gradient_s); - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->shgroups[id].gradient_f, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_s", stl->shgroups[id].gradient_s, 1); - - /* viewport x-ray */ - stl->shgroups[id].is_xray = (ob->dt == OB_WIRE) ? 1 : stl->storage->is_xray; - DRW_shgroup_uniform_int(grp, "viewport_xray", (const int *)&stl->shgroups[id].is_xray, 1); - - stl->shgroups[id].shading_type[0] = (GPENCIL_USE_SOLID(stl) || onion) ? (int)OB_RENDER : - shading_type[0]; - if (v3d) { - stl->shgroups[id].shading_type[1] = ((stl->shgroups[id].shading_type[0] == OB_WIRE) ? - v3d->shading.wire_color_type : - v3d->shading.color_type); - } - DRW_shgroup_uniform_int(grp, "shading_type", &stl->shgroups[id].shading_type[0], 2); - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, false); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, gpencil_fade_object_check(stl, ob)); - - /* wire color */ - set_wireframe_color(ob, gpl, v3d, stl, gp_style, id, false); - DRW_shgroup_uniform_vec4(grp, "wire_color", stl->shgroups[id].wire_color, 1); - - /* mix stroke factor */ - stl->shgroups[id].mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->shgroups[id].mix_stroke_factor, 1); - - /* lock rotation of dots and boxes */ - stl->shgroups[id].alignment_mode = gp_style->alignment_mode; - DRW_shgroup_uniform_int(grp, "alignment_mode", &stl->shgroups[id].alignment_mode, 1); - } - else { - stl->storage->obj_scale = 1.0f; - stl->storage->keep_size = 0; - stl->storage->pixfactor = GP_DEFAULT_PIX_FACTOR; - stl->storage->mode = gp_style->mode; - DRW_shgroup_uniform_float(grp, "objscale", &stl->storage->obj_scale, 1); - DRW_shgroup_uniform_int(grp, "keep_size", &stl->storage->keep_size, 1); - DRW_shgroup_uniform_int(grp, "color_type", &stl->storage->color_type, 1); - DRW_shgroup_uniform_int(grp, "mode", &stl->storage->mode, 1); - if (gpd) { - DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1); - } - else { - DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1); - } - - DRW_shgroup_uniform_float(grp, "gradient_f", &stl->storage->gradient_f, 1); - DRW_shgroup_uniform_vec2(grp, "gradient_s", stl->storage->gradient_s, 1); - - /* viewport x-ray */ - DRW_shgroup_uniform_int(grp, "viewport_xray", &stl->storage->is_xray, 1); - DRW_shgroup_uniform_int(grp, "shading_type", (const int *)&stl->storage->shade_render, 2); - - /* mix stroke factor */ - stl->storage->mix_stroke_factor = (gp_style->flag & GP_STYLE_STROKE_TEX_MIX) ? - gp_style->mix_stroke_factor : - 0.0f; - DRW_shgroup_uniform_float(grp, "mix_stroke_factor", &stl->storage->mix_stroke_factor, 1); - - /* lock rotation of dots and boxes */ - DRW_shgroup_uniform_int(grp, "alignment_mode", &stl->storage->alignment_mode, 1); - } - - DRW_shgroup_uniform_vec4(grp, "colormix", gp_style->stroke_rgba, 1); - - if ((gpd) && (id > -1)) { - stl->shgroups[id].xray_mode = (ob->dtx & OB_DRAWXRAY) ? GP_XRAY_FRONT : GP_XRAY_3DSPACE; - DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&stl->shgroups[id].xray_mode, 1); - } - else { - /* for drawing always on predefined z-depth */ - DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); - } - - /* Fade layer uniforms. */ - gpencil_set_fade_layer_uniforms(stl, grp, ob, gpl, true); - - /* Fade object uniforms. */ - gpencil_set_fade_ob_uniforms(v3d, grp, false); - - /* image texture */ - if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && (!onion)) { - ImBuf *ibuf; - Image *image = gp_style->sima; - ImageUser iuser = {NULL}; - void *lock; - - iuser.ok = true; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(image, ibuf, NULL); - } - else { - GPUTexture *texture = GPU_texture_from_blender(gp_style->sima, &iuser, ibuf, GL_TEXTURE_2D); - DRW_shgroup_uniform_texture(grp, "myTexture", texture); - DRW_shgroup_uniform_bool_copy( - grp, "myTexturePremultiplied", (image->alpha_mode == IMA_ALPHA_PREMUL)); - - BKE_image_release_ibuf(image, ibuf, NULL); - } - } - else { - /* if no texture defined, need a blank texture to avoid errors in draw manager */ - DRW_shgroup_uniform_texture(grp, "myTexture", e_data->gpencil_blank_texture); - } - - return grp; -} - -/* add fill vertex info */ -static void gpencil_add_fill_vertexdata(GpencilBatchCache *cache, - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps, - float opacity, - const float tintcolor[4], - const bool onion, - const bool custonion) -{ - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - if (gps->totpoints >= 3) { - float tfill[4]; - /* set color using material, tint color and opacity */ - interp_v3_v3v3(tfill, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]); - tfill[3] = gps->runtime.tmp_fill_rgba[3] * opacity; - if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0) || - (gpl->blend_mode != eGplBlendMode_Regular)) { - if (cache->is_dirty) { - const float *color; - if (!onion) { - color = tfill; - } - else { - if (custonion) { - color = tintcolor; - } - else { - ARRAY_SET_ITEMS(tfill, UNPACK3(gps->runtime.tmp_fill_rgba), tintcolor[3]); - color = tfill; - } - } - /* create vertex data */ - const int old_len = cache->b_fill.vbo_len; - gpencil_get_fill_geom(&cache->b_fill, ob, gps, color); - - /* add to list of groups */ - if (old_len < cache->b_fill.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Fill, - onion, - cache->b_fill.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } - } -} - -/* add stroke vertex info */ -static void gpencil_add_stroke_vertexdata(GpencilBatchCache *cache, - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps, - const float opacity, - const float tintcolor[4], - const bool onion, - const bool custonion) -{ - float tcolor[4]; - float ink[4]; - short sthickness; - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - const int alignment_mode = (gp_style) ? gp_style->alignment_mode : GP_STYLE_FOLLOW_PATH; - - /* set color using base color, tint color and opacity */ - if (cache->is_dirty) { - if (!onion) { - /* if special stroke, use fill color as stroke color */ - if (gps->flag & GP_STROKE_NOFILL) { - interp_v3_v3v3(tcolor, gps->runtime.tmp_fill_rgba, tintcolor, tintcolor[3]); - tcolor[3] = gps->runtime.tmp_fill_rgba[3] * opacity; - } - else { - interp_v3_v3v3(tcolor, gps->runtime.tmp_stroke_rgba, tintcolor, tintcolor[3]); - tcolor[3] = gps->runtime.tmp_stroke_rgba[3] * opacity; - } - copy_v4_v4(ink, tcolor); - } - else { - if (custonion) { - copy_v4_v4(ink, tintcolor); - } - else { - ARRAY_SET_ITEMS(tcolor, UNPACK3(gps->runtime.tmp_stroke_rgba), opacity); - copy_v4_v4(ink, tcolor); - } - } - - sthickness = gps->thickness + gpl->line_change; - CLAMP_MIN(sthickness, 1); - - if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) { - /* create vertex data */ - const int old_len = cache->b_stroke.vbo_len; - gpencil_get_stroke_geom(&cache->b_stroke, gps, sthickness, ink); - - /* add to list of groups */ - if (old_len < cache->b_stroke.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Stroke, - onion, - cache->b_stroke.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - else { - /* create vertex data */ - const int old_len = cache->b_point.vbo_len; - gpencil_get_point_geom(&cache->b_point, gps, sthickness, ink, alignment_mode); - - /* add to list of groups */ - if (old_len < cache->b_point.vbo_len) { - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Point, - onion, - cache->b_point.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } -} - -/* add edit points vertex info */ -static void gpencil_add_editpoints_vertexdata(GpencilBatchCache *cache, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf, - bGPDstroke *gps) -{ - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - ToolSettings *ts = draw_ctx->scene->toolsettings; - const bool use_sculpt_mask = (GPENCIL_SCULPT_MODE(gpd) && (ts->gpencil_selectmode_sculpt & - (GP_SCULPT_MASK_SELECTMODE_POINT | - GP_SCULPT_MASK_SELECTMODE_STROKE | - GP_SCULPT_MASK_SELECTMODE_SEGMENT))); - - const bool show_sculpt_points = (GPENCIL_SCULPT_MODE(gpd) && - (ts->gpencil_selectmode_sculpt & - (GP_SCULPT_MASK_SELECTMODE_POINT | - GP_SCULPT_MASK_SELECTMODE_SEGMENT))); - - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - - /* alpha factor for edit points/line to make them more subtle */ - float edit_alpha = v3d->vertex_opacity; - - if (GPENCIL_ANY_EDIT_MODE(gpd)) { - Object *obact = DRW_context_state_get()->obact; - if ((!obact) || (obact->type != OB_GPENCIL)) { - return; - } - const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); - - /* If Sculpt mode and the mask is disabled, the select must be hidden. */ - const bool hide_select = GPENCIL_SCULPT_MODE(gpd) && !use_sculpt_mask; - - /* Show Edit points if: - * Edit mode: Not in Stroke selection mode - * Sculpt mode: Not in Stroke mask mode and any other mask mode enabled - * Weight mode: Always - */ - const bool show_points = (show_sculpt_points) || (is_weight_paint) || - (GPENCIL_EDIT_MODE(gpd) && - ((ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE) || - (gps->totpoints == 1))); - - if (cache->is_dirty) { - if ((obact == ob) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && - (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES) && (gps->totpoints > 1)) { - - /* line of the original stroke */ - gpencil_get_edlin_geom(&cache->b_edlin, gps, edit_alpha, hide_select); - - /* add to list of groups */ - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Edlin, - false, - cache->b_edlin.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - - /* If the points are hidden return. */ - if ((!show_points) || (hide_select)) { - return; - } - - /* edit points */ - if ((gps->flag & GP_STROKE_SELECT) || (is_weight_paint)) { - if ((gpl->flag & GP_LAYER_UNLOCK_COLOR) || - ((gp_style->flag & GP_STYLE_COLOR_LOCKED) == 0)) { - if (obact == ob) { - gpencil_get_edit_geom(&cache->b_edit, gps, edit_alpha, gpd->flag); - - /* add to list of groups */ - cache->grp_cache = gpencil_group_cache_add(cache->grp_cache, - gpl, - gpf, - gps, - eGpencilBatchGroupType_Edit, - false, - cache->b_edit.vbo_len, - &cache->grp_size, - &cache->grp_used); - } - } - } - } - } -} - -/* main function to draw strokes */ -static void gpencil_draw_strokes(GpencilBatchCache *cache, - GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf, - const float opacity, - const float tintcolor[4], - const bool custonion, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - View3D *v3d = draw_ctx->v3d; - bGPDstroke *gps; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const bool playing = stl->storage->is_playing; - const bool is_render = (bool)stl->storage->is_render; - const bool is_mat_preview = (bool)stl->storage->is_mat_preview; - const bool overlay_multiedit = v3d != NULL ? !(v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) : - true; - - /* Get evaluation context */ - /* NOTE: We must check if C is valid, otherwise we get crashes when trying to save files - * (i.e. the thumbnail offscreen rendering fails) - */ - Depsgraph *depsgraph = DRW_context_state_get()->depsgraph; - - /* get parent matrix and save as static data */ - if ((cache_ob != NULL) && (cache_ob->is_dup_ob)) { - copy_m4_m4(gpf->runtime.parent_obmat, cache_ob->obmat); - } - else { - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, gpf->runtime.parent_obmat); - } - - for (gps = gpf->strokes.first; gps; gps = gps->next) { - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - - /* check if stroke can be drawn */ - if (gpencil_can_draw_stroke(gp_style, gps, false, is_mat_preview) == false) { - continue; - } - - /* Copy color to temp fields. */ - if ((is_multiedit) && (gp_style)) { - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - } - - /* be sure recalc all cache in source stroke to avoid recalculation when frame change - * and improve fps */ - gpencil_recalc_geometry_caches( - ob, gpl, gp_style, (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps); - - /* if the fill has any value, it's considered a fill and is not drawn if simplify fill is - * enabled */ - if ((stl->storage->simplify_fill) && - (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_REMOVE_FILL_LINE)) { - if ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || - (gp_style->fill_style > GP_STYLE_FILL_STYLE_SOLID) || - (gpl->blend_mode != eGplBlendMode_Regular)) { - - continue; - } - } - - if ((gpl->actframe && (gpl->actframe->framenum == gpf->framenum)) || (!is_multiedit) || - (overlay_multiedit)) { - /* hide any blend layer */ - if ((!stl->storage->simplify_blend) || (gpl->blend_mode == eGplBlendMode_Regular)) { - /* fill */ - if ((gp_style->flag & GP_STYLE_FILL_SHOW) && (!stl->storage->simplify_fill) && - ((gps->flag & GP_STROKE_NOFILL) == 0)) { - gpencil_add_fill_vertexdata( - cache, ob, gpl, gpf, gps, opacity, tintcolor, false, custonion); - } - /* stroke - * No fill strokes, must show stroke always or if the total points is lower than 3, - * because the stroke cannot be filled and it would be invisible. */ - if (((gp_style->flag & GP_STYLE_STROKE_SHOW) || (gps->flag & GP_STROKE_NOFILL) || - (gps->totpoints < 3)) && - ((gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || - (gpl->blend_mode == eGplBlendMode_Regular))) { - /* recalc strokes uv (geometry can be changed by modifiers) */ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - ED_gpencil_calc_stroke_uv(ob, gps); - } - - gpencil_add_stroke_vertexdata( - cache, ob, gpl, gpf, gps, opacity, tintcolor, false, custonion); - } - } - } - - /* edit points (only in edit mode and not play animation not render) */ - if ((draw_ctx->obact == ob) && (!playing) && (!is_render) && (!cache_ob->is_dup_ob)) { - if ((gpl->flag & GP_LAYER_LOCKED) == 0) { - if (!stl->g_data->shgrps_edit_line) { - stl->g_data->shgrps_edit_line = DRW_shgroup_create(e_data->gpencil_line_sh, - psl->edit_pass); - DRW_shgroup_uniform_mat4(stl->g_data->shgrps_edit_line, "gpModelMatrix", ob->obmat); - } - if (!stl->g_data->shgrps_edit_point) { - stl->g_data->shgrps_edit_point = DRW_shgroup_create(e_data->gpencil_edit_point_sh, - psl->edit_pass); - const float *viewport_size = DRW_viewport_size_get(); - DRW_shgroup_uniform_vec2(stl->g_data->shgrps_edit_point, "Viewport", viewport_size, 1); - DRW_shgroup_uniform_mat4(stl->g_data->shgrps_edit_point, "gpModelMatrix", ob->obmat); - } - - gpencil_add_editpoints_vertexdata(cache, ob, gpd, gpl, gpf, gps); - } - } - } -} - -/* get alpha factor for onion strokes */ -static void gpencil_get_onion_alpha(float color[4], bGPdata *gpd) -{ -#define MIN_ALPHA_VALUE 0.01f - - /* if fade is disabled, opacity is equal in all frames */ - if ((gpd->onion_flag & GP_ONION_FADE) == 0) { - color[3] = gpd->onion_factor; - } - else { - /* add override opacity factor */ - color[3] += gpd->onion_factor - 0.5f; - } - - CLAMP(color[3], MIN_ALPHA_VALUE, 1.0f); -} - -/* function to draw strokes for onion only */ -static void gpencil_draw_onion_strokes(GpencilBatchCache *cache, - void *vedata, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf, - const float opacity, - const float tintcolor[4], - const bool custonion) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - Depsgraph *depsgraph = DRW_context_state_get()->depsgraph; - - /* get parent matrix and save as static data */ - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, gpf->runtime.parent_obmat); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - if (gp_style == NULL) { - continue; - } - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - - int id = stl->storage->shgroup_id; - /* check if stroke can be drawn */ - if (gpencil_can_draw_stroke(gp_style, gps, true, false) == false) { - continue; - } - /* limit the number of shading groups */ - if (id >= GPENCIL_MAX_SHGROUPS) { - continue; - } - - /* stroke */ - gpencil_add_stroke_vertexdata(cache, ob, gpl, gpf, gps, opacity, tintcolor, true, custonion); - - stl->storage->shgroup_id++; - } -} - -/* draw onion-skinning for a layer */ -static void gpencil_draw_onionskins(GpencilBatchCache *cache, - void *vedata, - Object *ob, - bGPdata *gpd, - bGPDlayer *gpl, - bGPDframe *gpf) -{ - - const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; - const float alpha = 1.0f; - float color[4]; - int idx; - float fac = 1.0f; - int step = 0; - bool colflag = false; - const int mode = gpd->onion_mode; - bGPDframe *gpf_loop = ((gpd->onion_flag & GP_ONION_LOOP) && (mode != GP_ONION_MODE_SELECTED)) ? - gpl->frames.first : - NULL; - int last = gpf->framenum; - - colflag = (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) != 0; - const short onion_keytype = gpd->onion_keytype; - /* ------------------------------- - * 1) Draw Previous Frames First - * ------------------------------- */ - step = gpd->gstep; - - if (gpd->onion_flag & GP_ONION_GHOST_PREVCOL) { - copy_v3_v3(color, gpd->gcolor_prev); - } - else { - copy_v3_v3(color, default_color); - } - - idx = 0; - for (bGPDframe *gf = gpf->prev; gf; gf = gf->prev) { - /* only selected frames */ - if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) { - continue; - } - /* verify keyframe type */ - if ((onion_keytype > -1) && (gf->key_type != onion_keytype)) { - continue; - } - /* absolute range */ - if (mode == GP_ONION_MODE_ABSOLUTE) { - if ((gpf->framenum - gf->framenum) > step) { - break; - } - } - /* relative range */ - if (mode == GP_ONION_MODE_RELATIVE) { - idx++; - if (idx > step) { - break; - } - } - /* alpha decreases with distance from curframe index */ - if (mode != GP_ONION_MODE_SELECTED) { - if (mode == GP_ONION_MODE_ABSOLUTE) { - fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(step + 1)); - } - else { - fac = 1.0f - ((float)idx / (float)(step + 1)); - } - color[3] = alpha * fac * 0.66f; - } - else { - idx++; - fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f); - color[3] = fac; - } - - /* if loop option, save the frame to use later */ - if ((mode == GP_ONION_MODE_SELECTED) && (gpd->onion_flag & GP_ONION_LOOP)) { - gpf_loop = gf; - } - - gpencil_get_onion_alpha(color, gpd); - gpencil_draw_onion_strokes(cache, vedata, ob, gpd, gpl, gf, color[3], color, colflag); - } - /* ------------------------------- - * 2) Now draw next frames - * ------------------------------- */ - step = gpd->gstep_next; - - if (gpd->onion_flag & GP_ONION_GHOST_NEXTCOL) { - copy_v3_v3(color, gpd->gcolor_next); - } - else { - copy_v3_v3(color, default_color); - } - - idx = 0; - for (bGPDframe *gf = gpf->next; gf; gf = gf->next) { - /* only selected frames */ - if ((mode == GP_ONION_MODE_SELECTED) && ((gf->flag & GP_FRAME_SELECT) == 0)) { - continue; - } - /* verify keyframe type */ - if ((onion_keytype > -1) && (gf->key_type != onion_keytype)) { - continue; - } - /* absolute range */ - if (mode == GP_ONION_MODE_ABSOLUTE) { - if ((gf->framenum - gpf->framenum) > step) { - break; - } - } - /* relative range */ - if (mode == GP_ONION_MODE_RELATIVE) { - idx++; - if (idx > step) { - break; - } - } - /* alpha decreases with distance from curframe index */ - if (mode != GP_ONION_MODE_SELECTED) { - if (mode == GP_ONION_MODE_ABSOLUTE) { - fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(step + 1)); - } - else { - fac = 1.0f - ((float)idx / (float)(step + 1)); - } - color[3] = alpha * fac * 0.66f; - } - else { - idx++; - fac = alpha - ((1.1f - (1.0f / (float)idx)) * 0.66f); - color[3] = fac; - } - - gpencil_get_onion_alpha(color, gpd); - gpencil_draw_onion_strokes(cache, vedata, ob, gpd, gpl, gf, color[3], color, colflag); - if (last < gf->framenum) { - last = gf->framenum; - } - } - - /* Draw first frame in blue for loop mode */ - if ((gpd->onion_flag & GP_ONION_LOOP) && (gpf_loop != NULL)) { - if ((last == gpf->framenum) || (gpf->next == NULL)) { - gpencil_get_onion_alpha(color, gpd); - gpencil_draw_onion_strokes(cache, vedata, ob, gpd, gpl, gpf_loop, color[3], color, colflag); - } - } -} - -/* Check if stencil is required */ -static bool gpencil_is_stencil_required(MaterialGPencilStyle *gp_style) -{ - return (bool)((gp_style->stroke_style == GP_STYLE_STROKE_STYLE_SOLID) && - ((gp_style->flag & GP_STYLE_DISABLE_STENCIL) == 0)); -} - -/* draw stroke in drawing buffer */ -void gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, - void *vedata, - ToolSettings *ts, - Object *ob) -{ - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true; - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - const bool is_paint_tool = (bool)((brush) && (brush->gpencil_tool == GPAINT_TOOL_DRAW)); - bGPdata *gpd_eval = ob->data; - /* need the original to avoid cow overhead while drawing */ - bGPdata *gpd = (bGPdata *)DEG_get_original_id(&gpd_eval->id); - - MaterialGPencilStyle *gp_style = NULL; - float obscale = mat4_to_scale(ob->obmat); - - /* use the brush material */ - Material *ma = BKE_gpencil_object_material_get_from_brush(ob, brush); - if (ma != NULL) { - gp_style = ma->gp_style; - } - /* this is not common, but avoid any special situations when brush could be without material */ - if (gp_style == NULL) { - gp_style = BKE_gpencil_material_settings(ob, ob->actcol); - } - - static float unit_mat[4][4] = { - {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; - - /* drawing strokes */ - /* Check if may need to draw the active stroke cache, only if this layer is the active layer - * that is being edited. (Stroke buffer is currently stored in gp-data) - */ - if (gpd->runtime.sbuffer_used > 0) { - if ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { - /* It should also be noted that sbuffer contains temporary point types - * i.e. tGPspoints NOT bGPDspoints - */ - short lthick = brush->size * obscale; - - /* save gradient info */ - stl->storage->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(stl->storage->gradient_s, brush->gpencil_settings->gradient_s); - stl->storage->alignment_mode = (gp_style) ? gp_style->alignment_mode : GP_STYLE_FOLLOW_PATH; - - /* if only one point, don't need to draw buffer because the user has no time to see it */ - if (gpd->runtime.sbuffer_used > 1) { - if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) { - stl->g_data->shgrps_drawing_stroke = gpencil_shgroup_stroke_create( - e_data, - vedata, - psl->drawing_pass, - e_data->gpencil_stroke_sh, - NULL, - unit_mat, - gpd, - NULL, - NULL, - gp_style, - -1, - false, - 1.0f, - (const int *)stl->storage->shade_render); - - if (gpencil_is_stencil_required(gp_style)) { - DRW_shgroup_stencil_mask(stl->g_data->shgrps_drawing_stroke, 0x01); - } - else { - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_stroke, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - } - else { - stl->g_data->shgrps_drawing_stroke = gpencil_shgroup_point_create( - e_data, - vedata, - psl->drawing_pass, - e_data->gpencil_point_sh, - NULL, - unit_mat, - gpd, - NULL, - NULL, - gp_style, - -1, - false, - 1.0f, - (const int *)stl->storage->shade_render); - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_stroke, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - - /* use unit matrix because the buffer is in screen space and does not need conversion */ - if (gpd->runtime.mode == GP_STYLE_MODE_LINE) { - stl->g_data->batch_buffer_stroke = gpencil_get_buffer_stroke_geom(gpd, lthick); - } - else { - stl->g_data->batch_buffer_stroke = gpencil_get_buffer_point_geom(gpd, lthick); - } - - /* buffer strokes, must show stroke always */ - DRW_shgroup_call( - stl->g_data->shgrps_drawing_stroke, stl->g_data->batch_buffer_stroke, NULL); - - if ((gpd->runtime.sbuffer_used >= 3) && - (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) && - ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0) && - ((brush->gpencil_settings->flag & GP_BRUSH_DISSABLE_LASSO) == 0) && - (gp_style->flag & GP_STYLE_FILL_SHOW)) { - /* if not solid, fill is simulated with solid color */ - if (gpd->runtime.bfill_style > 0) { - gpd->runtime.sfill[3] = 0.5f; - } - stl->g_data->shgrps_drawing_fill = DRW_shgroup_create(e_data->gpencil_drawing_fill_sh, - psl->drawing_pass); - /* Disable stencil for this type */ - DRW_shgroup_state_disable(stl->g_data->shgrps_drawing_fill, - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - stl->g_data->batch_buffer_fill = gpencil_get_buffer_fill_geom(gpd); - DRW_shgroup_call(stl->g_data->shgrps_drawing_fill, stl->g_data->batch_buffer_fill, NULL); - } - } - } - } - - /* control points for primitives and speed guide */ - const bool is_cppoint = (gpd->runtime.tot_cp_points > 0); - const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && - (draw_ctx->object_mode == OB_MODE_PAINT_GPENCIL)); - const bool is_show_gizmo = (((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) && - ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0)); - - if ((overlay) && (is_paint_tool) && (is_cppoint || is_speed_guide) && (is_show_gizmo) && - ((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0)) { - DRWShadingGroup *shgrp = DRW_shgroup_create(e_data->gpencil_edit_point_sh, psl->drawing_pass); - const float *viewport_size = DRW_viewport_size_get(); - DRW_shgroup_uniform_vec2(shgrp, "Viewport", viewport_size, 1); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", unit_mat); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - stl->g_data->batch_buffer_ctrlpoint = gpencil_get_buffer_ctrlpoint_geom(gpd); - - DRW_shgroup_call(shgrp, stl->g_data->batch_buffer_ctrlpoint, NULL); - } -} - -/* create all missing batches */ -static void gpencil_batches_ensure(GpencilBatchCache *cache) -{ - if ((cache->b_point.vbo) && (cache->b_point.batch == NULL)) { - cache->b_point.batch = GPU_batch_create_ex( - GPU_PRIM_POINTS, cache->b_point.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_stroke.vbo) && (cache->b_stroke.batch == NULL)) { - cache->b_stroke.batch = GPU_batch_create_ex( - GPU_PRIM_LINE_STRIP_ADJ, cache->b_stroke.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_fill.vbo) && (cache->b_fill.batch == NULL)) { - cache->b_fill.batch = GPU_batch_create_ex( - GPU_PRIM_TRIS, cache->b_fill.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_edit.vbo) && (cache->b_edit.batch == NULL)) { - cache->b_edit.batch = GPU_batch_create_ex( - GPU_PRIM_POINTS, cache->b_edit.vbo, NULL, GPU_BATCH_OWNS_VBO); - } - if ((cache->b_edlin.vbo) && (cache->b_edlin.batch == NULL)) { - cache->b_edlin.batch = GPU_batch_create_ex( - GPU_PRIM_LINE_STRIP, cache->b_edlin.vbo, NULL, GPU_BATCH_OWNS_VBO); - } -} - -/* create all shading groups */ -static void gpencil_shgroups_create(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - GpencilBatchCache *cache, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - bGPdata *gpd = (bGPdata *)ob->data; - DRWPass *stroke_pass = GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d; - - GpencilBatchGroup *elm = NULL; - DRWShadingGroup *shgrp = NULL; - tGPencilObjectCache_shgrp *array_elm = NULL; - - bGPDlayer *gpl = NULL; - bGPDlayer *gpl_prev = NULL; - int idx = 0; - bool tag_first = false; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; - - const bool overlay = draw_ctx->v3d != NULL ? - (bool)((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : - true; - const bool screen_onion = v3d != NULL ? (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) : true; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && screen_onion && - overlay && gpencil_onion_active(gpd); - - int start_stroke = 0; - int start_point = 0; - int start_fill = 0; - int start_edit = 0; - int start_edlin = 0; - - uint stencil_id = 1; - /* Flag to determine if the layer is above active layer. */ - stl->storage->is_ontop = false; - for (int i = 0; i < cache->grp_used; i++) { - elm = &cache->grp_cache[i]; - array_elm = &cache_ob->shgrp_array[idx]; - - /* Limit stencil id */ - if (stencil_id > 255) { - stencil_id = 1; - } - - /* save last group when change */ - if (gpl_prev == NULL) { - gpl_prev = elm->gpl; - tag_first = true; - } - else { - if (elm->gpl != gpl_prev) { - /* first layer is always blend Normal */ - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->end_shgrp = shgrp; - gpl_prev = elm->gpl; - tag_first = true; - idx++; - } - } - - gpl = elm->gpl; - if ((!stl->storage->is_ontop) && (gpl->flag & GP_LAYER_ACTIVE)) { - stl->storage->is_ontop = true; - } - - bGPDframe *gpf = elm->gpf; - bGPDstroke *gps = elm->gps; - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - /* if the user switch used material from data to object, - * the material could not be available */ - if (gp_style == NULL) { - break; - } - - /* limit the number of shading groups */ - if (i >= GPENCIL_MAX_SHGROUPS) { - break; - } - - const float scale = (!cache_ob->is_dup_ob) ? mat4_to_scale(gpf->runtime.parent_obmat) : - cache_ob->scale; - float(*obmat)[4] = (!cache_ob->is_dup_ob) ? gpf->runtime.parent_obmat : cache_ob->obmat; - switch (elm->type) { - case eGpencilBatchGroupType_Stroke: { - const int len = elm->vertex_idx - start_stroke; - - shgrp = gpencil_shgroup_stroke_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_stroke_sh, - ob, - obmat, - gpd, - gpl, - gps, - gp_style, - stl->storage->shgroup_id, - elm->onion, - scale, - cache_ob->shading_type); - - /* set stencil mask id */ - if (gpencil_is_stencil_required(gp_style)) { - if (stencil_id == 1) { - /* Clear previous stencils. */ - DRW_shgroup_clear_framebuffer(shgrp, GPU_STENCIL_BIT, 0, 0, 0, 0, 0.0f, 0x0); - } - DRW_shgroup_stencil_mask(shgrp, stencil_id); - stencil_id++; - } - else { - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - } - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_stroke.batch, start_stroke, len); - } - stl->storage->shgroup_id++; - start_stroke = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Point: { - const int len = elm->vertex_idx - start_point; - - shgrp = gpencil_shgroup_point_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_point_sh, - ob, - obmat, - gpd, - gpl, - gps, - gp_style, - stl->storage->shgroup_id, - elm->onion, - scale, - cache_ob->shading_type); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_point.batch, start_point, len); - } - stl->storage->shgroup_id++; - start_point = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Fill: { - const int len = elm->vertex_idx - start_fill; - - shgrp = gpencil_shgroup_fill_create(e_data, - vedata, - stroke_pass, - e_data->gpencil_fill_sh, - ob, - obmat, - gpd, - gpl, - gp_style, - stl->storage->shgroup_id, - cache_ob->shading_type); - - /* Disable stencil for this type */ - DRW_shgroup_state_disable(shgrp, DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - if ((do_onion) || (elm->onion == false)) { - DRW_shgroup_call_range(shgrp, cache->b_fill.batch, start_fill, len); - } - stl->storage->shgroup_id++; - start_fill = elm->vertex_idx; - break; - } - case eGpencilBatchGroupType_Edit: { - if (stl->g_data->shgrps_edit_point) { - const int len = elm->vertex_idx - start_edit; - - shgrp = DRW_shgroup_create_sub(stl->g_data->shgrps_edit_point); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", obmat); - /* use always the same group */ - DRW_shgroup_call_range( - stl->g_data->shgrps_edit_point, cache->b_edit.batch, start_edit, len); - - start_edit = elm->vertex_idx; - } - break; - } - case eGpencilBatchGroupType_Edlin: { - if (stl->g_data->shgrps_edit_line) { - const int len = elm->vertex_idx - start_edlin; - - shgrp = DRW_shgroup_create_sub(stl->g_data->shgrps_edit_line); - DRW_shgroup_uniform_mat4(shgrp, "gpModelMatrix", obmat); - /* use always the same group */ - DRW_shgroup_call_range( - stl->g_data->shgrps_edit_line, cache->b_edlin.batch, start_edlin, len); - - start_edlin = elm->vertex_idx; - } - break; - } - default: { - break; - } - } - /* save first group */ - if ((shgrp != NULL) && (tag_first)) { - array_elm = &cache_ob->shgrp_array[idx]; - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->mask_layer = gpl->flag & GP_LAYER_USE_MASK; - array_elm->blend_opacity = gpl->opacity; - array_elm->init_shgrp = shgrp; - cache_ob->tot_layers++; - - tag_first = false; - } - } - - /* save last group */ - if (shgrp != NULL) { - array_elm->mode = idx == 0 ? eGplBlendMode_Regular : gpl->blend_mode; - array_elm->end_shgrp = shgrp; - } -} -/* populate a datablock for multiedit (no onions, no modifiers) */ -void gpencil_populate_multiedit(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - tGPencilObjectCache *cache_ob) -{ - bGPdata *gpd = (bGPdata *)ob->data; - bGPDframe *gpf = NULL; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); - GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); - - /* check if playing animation */ - const bool playing = stl->storage->is_playing; - - /* calc max size of VBOs */ - gpencil_calc_vertex(stl, cache_ob, cache, gpd); - - /* draw strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* don't draw layer if hidden */ - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } - const float alpha = GPENCIL_SIMPLIFY_TINT(scene, playing) ? 0.0f : gpl->tintcolor[3]; - const float tintcolor[4] = {gpl->tintcolor[0], gpl->tintcolor[1], gpl->tintcolor[2], alpha}; - - /* list of frames to draw */ - if (!playing) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { - gpencil_draw_strokes( - cache, e_data, vedata, ob, gpd, gpl, gpf, gpl->opacity, tintcolor, false, cache_ob); - } - } - } - else { - gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); - if (gpf) { - gpencil_draw_strokes( - cache, e_data, vedata, ob, gpd, gpl, gpf, gpl->opacity, tintcolor, false, cache_ob); - } - } - } - - /* create batchs and shading groups */ - gpencil_batches_ensure(cache); - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - - cache->is_dirty = false; -} - -/* helper for populate a complete grease pencil datablock */ -void gpencil_populate_datablock(GPENCIL_e_data *e_data, - void *vedata, - Object *ob, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - const ViewLayer *view_layer = DEG_get_evaluated_view_layer(draw_ctx->depsgraph); - Scene *scene = draw_ctx->scene; - - /* TODO: Review why is needed this recalc when render cycles + GP object in background. - * We need these lines to keep running the background render, but asap we get an alternative - * solution, we must remove it and keep all logic inside gpencil_modifier module. (antoniov) - */ - if (ob->runtime.gpencil_tot_layers == 0) { - BKE_gpencil_modifiers_calc(draw_ctx->depsgraph, draw_ctx->scene, ob); - } - - bGPdata *gpd = (bGPdata *)ob->data; - - /* If render mode, instead to use view switches, test if the datablock has - * the onion activated for render. */ - const bool render_onion = (gpd && gpd->onion_flag & GP_ONION_GHOST_ALWAYS); - const bool main_onion = (stl->storage->is_render) ? render_onion : stl->storage->is_main_onion; - const bool overlay = (stl->storage->is_render) ? render_onion : stl->storage->is_main_overlay; - const bool playing = stl->storage->is_playing; - const bool do_onion = (bool)((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && overlay && - main_onion && !playing && gpencil_onion_active(gpd); - - View3D *v3d = draw_ctx->v3d; - int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); - - bGPDframe *gpf_eval = NULL; - const bool time_remap = BKE_gpencil_has_time_modifiers(ob); - - float opacity; - bGPDframe *gpf = NULL; - - GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); - - /* if object is duplicate, only create shading groups */ - if (cache_ob->is_dup_ob) { - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - return; - } - - /* calc max size of VBOs */ - gpencil_calc_vertex(stl, cache_ob, cache, gpd); - - /* draw normal strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* don't draw layer if hidden */ - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } - - const bool is_solomode = GPENCIL_PAINT_MODE(gpd) && (!playing) && (!stl->storage->is_render) && - (gpl->flag & GP_LAYER_SOLO_MODE); - - /* filter view layer to gp layers in the same view layer (for compo) */ - if ((stl->storage->is_render) && (gpl->viewlayername[0] != '\0')) { - if (!STREQ(view_layer->name, gpl->viewlayername)) { - continue; - } - } - - /* remap time */ - int remap_cfra = cfra_eval; - if ((time_remap) && (!stl->storage->simplify_modif)) { - remap_cfra = BKE_gpencil_time_modifier( - draw_ctx->depsgraph, scene, ob, gpl, cfra_eval, stl->storage->is_render); - } - - gpf = BKE_gpencil_layer_getframe(gpl, remap_cfra, GP_GETFRAME_USE_PREV); - if (gpf == NULL) { - continue; - } - - /* if solo mode, display only frames with keyframe in the current frame */ - if ((is_solomode) && (gpf->framenum != remap_cfra)) { - continue; - } - - opacity = gpl->opacity; - /* if pose mode, maybe the overlay to fade geometry is enabled */ - if ((draw_ctx->obact) && (draw_ctx->object_mode == OB_MODE_POSE) && - (v3d->overlay.flag & V3D_OVERLAY_BONE_SELECT)) { - opacity = opacity * v3d->overlay.xray_alpha_bone; - } - - /* Get evaluated frames array data */ - int idx_eval = BLI_findindex(&gpd->layers, gpl); - gpf_eval = &ob->runtime.gpencil_evaluated_frames[idx_eval]; - - /* draw onion skins */ - if (!ID_IS_LINKED(&gpd->id)) { - if ((do_onion) && (gpl->onion_flag & GP_LAYER_ONIONSKIN) && - ((!playing) || (gpd->onion_flag & GP_ONION_GHOST_ALWAYS)) && (!cache_ob->is_dup_ob) && - (gpd->id.us <= 1)) { - if ((!stl->storage->is_render) || - ((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS))) { - gpencil_draw_onionskins(cache, vedata, ob, gpd, gpl, gpf); - } - } - } - /* draw normal strokes */ - const float alpha = GPENCIL_SIMPLIFY_TINT(scene, playing) ? 0.0f : gpl->tintcolor[3]; - const float tintcolor[4] = {gpl->tintcolor[0], gpl->tintcolor[1], gpl->tintcolor[2], alpha}; - gpencil_draw_strokes( - cache, e_data, vedata, ob, gpd, gpl, gpf_eval, opacity, tintcolor, false, cache_ob); - } - - /* create batchs and shading groups */ - gpencil_batches_ensure(cache); - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - - cache->is_dirty = false; -} - -void gpencil_populate_particles(GPENCIL_e_data *e_data, GHash *gh_objects, void *vedata) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - - /* add particles */ - for (int i = 0; i < stl->g_data->gp_cache_used; i++) { - tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; - if (cache_ob->is_dup_ob) { - /* Reassign duplicate objects because memory for particles is not available - * and need to use the original data-block and run-time data. */ - Object *ob = (Object *)BLI_ghash_lookup(gh_objects, cache_ob->name); - if (ob) { - cache_ob->ob = ob; - cache_ob->gpd = (bGPdata *)ob->data; - GpencilBatchCache *cache = ob->runtime.gpencil_cache; - if (cache != NULL) { - gpencil_shgroups_create(e_data, vedata, ob, cache, cache_ob); - } - } - } - } -} diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 96560d986a0..80afbb5c7a3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -29,11 +29,19 @@ #include "BKE_paint.h" #include "BKE_shader_fx.h" +#include "BKE_camera.h" +#include "BKE_global.h" /* for G.debug */ + +#include "BLI_link_utils.h" +#include "BLI_memblock.h" + +#include "DNA_camera_types.h" #include "DNA_gpencil_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "GPU_texture.h" +#include "GPU_uniformbuffer.h" #include "gpencil_engine.h" @@ -44,1129 +52,904 @@ #include "UI_resources.h" -extern char datatoc_gpencil_fill_vert_glsl[]; -extern char datatoc_gpencil_fill_frag_glsl[]; -extern char datatoc_gpencil_stroke_vert_glsl[]; -extern char datatoc_gpencil_stroke_geom_glsl[]; -extern char datatoc_gpencil_stroke_frag_glsl[]; -extern char datatoc_gpencil_zdepth_mix_frag_glsl[]; -extern char datatoc_gpencil_simple_mix_frag_glsl[]; -extern char datatoc_gpencil_point_vert_glsl[]; -extern char datatoc_gpencil_point_geom_glsl[]; -extern char datatoc_gpencil_point_frag_glsl[]; -extern char datatoc_gpencil_background_frag_glsl[]; -extern char datatoc_gpencil_paper_frag_glsl[]; -extern char datatoc_gpencil_edit_point_vert_glsl[]; -extern char datatoc_gpencil_edit_point_geom_glsl[]; -extern char datatoc_gpencil_edit_point_frag_glsl[]; -extern char datatoc_gpencil_blend_frag_glsl[]; - -extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; - -extern char datatoc_common_colormanagement_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; - -/* *********** STATIC *********** */ -static GPENCIL_e_data e_data = {NULL}; /* Engine data */ - /* *********** FUNCTIONS *********** */ -/* create a multisample buffer if not present */ -void gpencil_multisample_ensure(GPENCIL_Data *vedata, int rect_w, int rect_h) +void GPENCIL_engine_init(void *ved) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_StorageList *stl = vedata->stl; + GPENCIL_TextureList *txl = vedata->txl; GPENCIL_FramebufferList *fbl = vedata->fbl; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl; - - short samples = stl->storage->multisamples; - - if (samples > 0) { - if (!fbl->multisample_fb) { - fbl->multisample_fb = GPU_framebuffer_create(); - if (fbl->multisample_fb) { - if (txl->multisample_color == NULL) { - txl->multisample_color = GPU_texture_create_2d_multisample( - rect_w, rect_h, GPU_RGBA16F, NULL, samples, NULL); - } - if (txl->multisample_depth == NULL) { - txl->multisample_depth = GPU_texture_create_2d_multisample( - rect_w, rect_h, GPU_DEPTH24_STENCIL8, NULL, samples, NULL); - } - GPU_framebuffer_ensure_config(&fbl->multisample_fb, - {GPU_ATTACHMENT_TEXTURE(txl->multisample_depth), - GPU_ATTACHMENT_TEXTURE(txl->multisample_color)}); - } - } + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + const DRWContextState *ctx = DRW_context_state_get(); + const View3D *v3d = ctx->v3d; + + if (!stl->pd) { + stl->pd = MEM_callocN(sizeof(GPENCIL_PrivateData), "GPENCIL_PrivateData"); + } + + if (txl->dummy_texture == NULL) { + float pixels[1][4] = {{1.0f, 0.0f, 1.0f, 1.0f}}; + txl->dummy_texture = DRW_texture_create_2d(1, 1, GPU_RGBA8, DRW_TEX_WRAP, (float *)pixels); + } + + GPENCIL_ViewLayerData *vldata = GPENCIL_view_layer_data_ensure(); + + /* Resize and reset memblocks. */ + BLI_memblock_clear(vldata->gp_light_pool, gpencil_light_pool_free); + BLI_memblock_clear(vldata->gp_material_pool, gpencil_material_pool_free); + BLI_memblock_clear(vldata->gp_object_pool, NULL); + BLI_memblock_clear(vldata->gp_layer_pool, NULL); + BLI_memblock_clear(vldata->gp_vfx_pool, NULL); + BLI_memblock_clear(vldata->gp_maskbit_pool, NULL); + + stl->pd->gp_light_pool = vldata->gp_light_pool; + stl->pd->gp_material_pool = vldata->gp_material_pool; + stl->pd->gp_maskbit_pool = vldata->gp_maskbit_pool; + stl->pd->gp_object_pool = vldata->gp_object_pool; + stl->pd->gp_layer_pool = vldata->gp_layer_pool; + stl->pd->gp_vfx_pool = vldata->gp_vfx_pool; + stl->pd->scene = ctx->scene; + stl->pd->last_light_pool = NULL; + stl->pd->last_material_pool = NULL; + stl->pd->tobjects.first = NULL; + stl->pd->tobjects.last = NULL; + stl->pd->tobjects_infront.first = NULL; + stl->pd->tobjects_infront.last = NULL; + stl->pd->sbuffer_tobjects.first = NULL; + stl->pd->sbuffer_tobjects.last = NULL; + stl->pd->dummy_tx = txl->dummy_texture; + stl->pd->draw_depth_only = !DRW_state_is_fbo(); + stl->pd->draw_wireframe = (v3d && v3d->shading.type == OB_WIRE) && !stl->pd->draw_depth_only; + stl->pd->scene_depth_tx = stl->pd->draw_depth_only ? txl->dummy_texture : dtxl->depth; + stl->pd->scene_fb = dfbl->default_fb; + stl->pd->is_render = txl->render_depth_tx || (v3d && v3d->shading.type == OB_RENDER); + stl->pd->is_viewport = (v3d != NULL); + stl->pd->global_light_pool = gpencil_light_pool_add(stl->pd); + stl->pd->shadeless_light_pool = gpencil_light_pool_add(stl->pd); + /* Small HACK: we don't want the global pool to be reused, + * so we set the last light pool to NULL. */ + stl->pd->last_light_pool = NULL; + + bool use_scene_lights = false; + bool use_scene_world = false; + + if (v3d) { + use_scene_lights = ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + + use_scene_world = ((v3d->shading.type == OB_MATERIAL) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || + ((v3d->shading.type == OB_RENDER) && + (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + + stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1; + /* Special case: If Vertex Paint mode, use always Vertex mode. */ + if (v3d->shading.type == OB_SOLID && ctx->obact && ctx->obact->type == OB_GPENCIL && + ctx->obact->mode == OB_MODE_VERTEX_GPENCIL) { + stl->pd->v3d_color_type = V3D_SHADING_VERTEX_COLOR; + } + + copy_v3_v3(stl->pd->v3d_single_color, v3d->shading.single_color); + + /* For non active frame, use only lines in multiedit mode. */ + const bool overlays_on = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + stl->pd->use_multiedit_lines_only = !overlays_on || + (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0; + + const bool shmode_xray_support = v3d->shading.type <= OB_SOLID; + stl->pd->xray_alpha = (shmode_xray_support && XRAY_ENABLED(v3d)) ? XRAY_ALPHA(v3d) : 1.0f; + } + else if (stl->pd->is_render) { + use_scene_lights = true; + use_scene_world = true; + stl->pd->use_multiedit_lines_only = false; + stl->pd->xray_alpha = 1.0f; + stl->pd->v3d_color_type = -1; + } + + stl->pd->use_lighting = (v3d && v3d->shading.type > OB_SOLID) || stl->pd->is_render; + stl->pd->use_lights = use_scene_lights; + + if (txl->render_depth_tx != NULL) { + stl->pd->scene_depth_tx = txl->render_depth_tx; + stl->pd->scene_fb = fbl->render_fb; + } + + gpencil_light_ambient_add(stl->pd->shadeless_light_pool, (float[3]){1.0f, 1.0f, 1.0f}); + + World *world = ctx->scene->world; + if (world != NULL && use_scene_world) { + gpencil_light_ambient_add(stl->pd->global_light_pool, &world->horr); + } + else if (v3d) { + float world_light[3]; + copy_v3_fl(world_light, v3d->shading.studiolight_intensity); + gpencil_light_ambient_add(stl->pd->global_light_pool, world_light); + } + + float viewmatinv[4][4]; + DRW_view_viewmat_get(NULL, viewmatinv, true); + copy_v3_v3(stl->pd->camera_z_axis, viewmatinv[2]); + copy_v3_v3(stl->pd->camera_pos, viewmatinv[3]); + stl->pd->camera_z_offset = dot_v3v3(viewmatinv[3], viewmatinv[2]); + + if (ctx && ctx->rv3d && v3d) { + stl->pd->camera = (ctx->rv3d->persp == RV3D_CAMOB) ? v3d->camera : NULL; + } + else { + stl->pd->camera = NULL; } } -static void GPENCIL_create_framebuffers(void *vedata) +void GPENCIL_cache_init(void *ved) { - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl; - - /* Go full 32bits for rendering */ - eGPUTextureFormat fb_format = DRW_state_is_image_render() ? GPU_RGBA32F : GPU_RGBA16F; - - if (DRW_state_is_fbo()) { - const float *viewport_size = DRW_viewport_size_get(); - const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - - /* create multisample framebuffer for AA */ - if ((stl->storage->framebuffer_flag & GP_FRAMEBUFFER_MULTISAMPLE) && - (stl->storage->multisamples > 0)) { - gpencil_multisample_ensure(vedata, size[0], size[1]); - } - - /* Framebufers for basic object drawing */ - if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_BASIC) { - /* temp textures for ping-pong buffers */ - stl->g_data->temp_depth_tx_a = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); - stl->g_data->temp_color_tx_a = DRW_texture_pool_query_2d( - size[0], size[1], fb_format, &draw_engine_gpencil_type); - GPU_framebuffer_ensure_config(&fbl->temp_fb_a, - { - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_a), - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_a), - }); - - stl->g_data->temp_depth_tx_b = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); - stl->g_data->temp_color_tx_b = DRW_texture_pool_query_2d( - size[0], size[1], fb_format, &draw_engine_gpencil_type); - GPU_framebuffer_ensure_config(&fbl->temp_fb_b, - { - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_b), - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_b), - }); + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_TextureList *txl = vedata->txl; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + DRWShadingGroup *grp; - /* used for FX effects and Layer blending */ - stl->g_data->temp_depth_tx_fx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); - stl->g_data->temp_color_tx_fx = DRW_texture_pool_query_2d( - size[0], size[1], fb_format, &draw_engine_gpencil_type); - GPU_framebuffer_ensure_config(&fbl->temp_fb_fx, - { - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_depth_tx_fx), - GPU_ATTACHMENT_TEXTURE(stl->g_data->temp_color_tx_fx), - }); - } + const DRWContextState *draw_ctx = DRW_context_state_get(); + pd->cfra = (int)DEG_get_ctime(draw_ctx->depsgraph); + pd->simplify_antialias = GPENCIL_SIMPLIFY_AA(draw_ctx->scene); + pd->use_layer_fb = false; + pd->use_object_fb = false; + pd->use_mask_fb = false; + pd->use_signed_fb = false; + + if (draw_ctx->v3d) { + const bool hide_overlay = ((draw_ctx->v3d->flag2 & V3D_HIDE_OVERLAYS) != 0); + const bool show_onion = ((draw_ctx->v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) != 0); + const bool playing = (draw_ctx->evil_C != NULL) ? + ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != + NULL : + false; + pd->do_onion = show_onion && !hide_overlay && !playing; + /* Save simplify flags (can change while drawing, so it's better to save). */ + Scene *scene = draw_ctx->scene; + pd->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, playing); + pd->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, playing) || + (draw_ctx->v3d->shading.type < OB_RENDER); + + /* Fade Layer. */ + const bool is_fade_layer = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS)); + pd->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f; + /* Fade GPencil Objects. */ + const bool is_fade_object = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_GPENCIL)); + pd->fade_gp_object_opacity = (is_fade_object) ? draw_ctx->v3d->overlay.gpencil_paper_opacity : + -1.0f; + pd->fade_3d_object_opacity = ((!hide_overlay) && (!pd->is_render) && + (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS)) ? + draw_ctx->v3d->overlay.gpencil_paper_opacity : + -1.0f; + } + else { + pd->do_onion = true; + pd->simplify_fill = false; + pd->simplify_fx = false; + pd->fade_layer_opacity = -1.0f; + } - /* background framebuffer to speed up drawing process */ - if (stl->storage->framebuffer_flag & GP_FRAMEBUFFER_DRAW) { - if (txl->background_color_tx == NULL) { - stl->storage->background_ready = false; + { + pd->sbuffer_stroke = NULL; + pd->sbuffer_gpd = NULL; + pd->sbuffer_layer = NULL; + pd->stroke_batch = NULL; + pd->fill_batch = NULL; + pd->do_fast_drawing = false; + + pd->obact = draw_ctx->obact; + if (pd->obact && pd->obact->type == OB_GPENCIL) { + /* Check if active object has a temp stroke data. */ + bGPdata *gpd = (bGPdata *)pd->obact->data; + if (gpd->runtime.sbuffer_used > 0) { + pd->sbuffer_gpd = gpd; + pd->sbuffer_stroke = DRW_cache_gpencil_sbuffer_stroke_data_get(pd->obact); + pd->sbuffer_layer = BKE_gpencil_layer_active_get(pd->sbuffer_gpd); + pd->do_fast_drawing = false; /* TODO option */ } - DRW_texture_ensure_2d( - &txl->background_depth_tx, size[0], size[1], GPU_DEPTH_COMPONENT24, DRW_TEX_FILTER); - DRW_texture_ensure_2d( - &txl->background_color_tx, size[0], size[1], GPU_RGBA16F, DRW_TEX_FILTER); - GPU_framebuffer_ensure_config(&fbl->background_fb, - { - GPU_ATTACHMENT_TEXTURE(txl->background_depth_tx), - GPU_ATTACHMENT_TEXTURE(txl->background_color_tx), - }); - } - else { - DRW_TEXTURE_FREE_SAFE(txl->background_depth_tx); - DRW_TEXTURE_FREE_SAFE(txl->background_color_tx); } } -} -static void GPENCIL_create_shaders(void) -{ - /* blank texture used if no texture defined for fill shader */ - if (!e_data.gpencil_blank_texture) { - float rect[1][1][4] = {{{0.0f}}}; - e_data.gpencil_blank_texture = DRW_texture_create_2d( - 1, 1, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect); + if (pd->do_fast_drawing) { + pd->snapshot_buffer_dirty = (txl->snapshot_color_tx == NULL); + const float *size = DRW_viewport_size_get(); + DRW_texture_ensure_2d(&txl->snapshot_depth_tx, size[0], size[1], GPU_DEPTH24_STENCIL8, 0); + DRW_texture_ensure_2d(&txl->snapshot_color_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0); + DRW_texture_ensure_2d(&txl->snapshot_reveal_tx, size[0], size[1], GPU_R11F_G11F_B10F, 0); + + GPU_framebuffer_ensure_config(&fbl->snapshot_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->snapshot_depth_tx), + GPU_ATTACHMENT_TEXTURE(txl->snapshot_color_tx), + GPU_ATTACHMENT_TEXTURE(txl->snapshot_reveal_tx), + }); } - /* normal fill shader */ - if (!e_data.gpencil_fill_sh) { - e_data.gpencil_fill_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_fill_vert_glsl, NULL}, - .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl, - datatoc_gpencil_fill_frag_glsl, - NULL}, - }); + else { + /* Free uneeded buffers. */ + GPU_FRAMEBUFFER_FREE_SAFE(fbl->snapshot_fb); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_depth_tx); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_color_tx); + DRW_TEXTURE_FREE_SAFE(txl->snapshot_reveal_tx); } - /* normal stroke shader using geometry to display lines (line mode) */ - if (!e_data.gpencil_stroke_sh) { - e_data.gpencil_stroke_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_stroke_vert_glsl, NULL}, - .geom = (const char *[]){datatoc_gpencil_stroke_geom_glsl, NULL}, - .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl, - datatoc_gpencil_stroke_frag_glsl, - NULL}, - }); - } + { + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + DRW_PASS_CREATE(psl->merge_depth_ps, state); - /* dot/rectangle mode for normal strokes using geometry */ - if (!e_data.gpencil_point_sh) { - e_data.gpencil_point_sh = GPU_shader_create_from_arrays({ - .vert = - (const char *[]){datatoc_common_view_lib_glsl, datatoc_gpencil_point_vert_glsl, NULL}, - .geom = (const char *[]){datatoc_gpencil_point_geom_glsl, NULL}, - .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl, - datatoc_gpencil_point_frag_glsl, - NULL}, - }); - } - /* used for edit points or strokes with one point only */ - if (!e_data.gpencil_edit_point_sh) { - e_data.gpencil_edit_point_sh = DRW_shader_create_with_lib(datatoc_gpencil_edit_point_vert_glsl, - datatoc_gpencil_edit_point_geom_glsl, - datatoc_gpencil_edit_point_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); + GPUShader *sh = GPENCIL_shader_depth_merge_get(); + grp = DRW_shgroup_create(sh, psl->merge_depth_ps); + DRW_shgroup_uniform_texture_ref(grp, "depthBuf", &pd->depth_tx); + DRW_shgroup_uniform_bool(grp, "strokeOrder3d", &pd->is_stroke_order_3d, 1); + DRW_shgroup_uniform_vec4(grp, "gpModelMatrix[0]", pd->object_bound_mat[0], 4); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT; + DRW_PASS_CREATE(psl->mask_invert_ps, state); - /* used for edit lines for edit modes */ - if (!e_data.gpencil_line_sh) { - e_data.gpencil_line_sh = DRW_shader_create_with_lib( - datatoc_gpencil_edit_point_vert_glsl, - NULL, - datatoc_gpu_shader_3D_smooth_color_frag_glsl, - datatoc_common_view_lib_glsl, - NULL); + GPUShader *sh = GPENCIL_shader_mask_invert_get(); + grp = DRW_shgroup_create(sh, psl->mask_invert_ps); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - /* used to filling during drawing */ - if (!e_data.gpencil_drawing_fill_sh) { - e_data.gpencil_drawing_fill_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR); - } + Camera *cam = (pd->camera != NULL) ? pd->camera->data : NULL; - /* full screen for mix zdepth*/ - if (!e_data.gpencil_fullscreen_sh) { - e_data.gpencil_fullscreen_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_zdepth_mix_frag_glsl, NULL); - } - if (!e_data.gpencil_simple_fullscreen_sh) { - e_data.gpencil_simple_fullscreen_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_simple_mix_frag_glsl, NULL); - } + /* Pseudo DOF setup. */ + if (cam && (cam->dof.flag & CAM_DOF_ENABLED)) { + const float *vp_size = DRW_viewport_size_get(); + float fstop = cam->dof.aperture_fstop; + float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); + float focus_dist = BKE_camera_object_dof_distance(pd->camera); + float focal_len = cam->lens; - /* blend */ - if (!e_data.gpencil_blend_fullscreen_sh) { - e_data.gpencil_blend_fullscreen_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_blend_frag_glsl, NULL); - } + const float scale_camera = 0.001f; + /* we want radius here for the aperture number */ + float aperture = 0.5f * scale_camera * focal_len / fstop; + float focal_len_scaled = scale_camera * focal_len; + float sensor_scaled = scale_camera * sensor; - /* shaders for use when drawing */ - if (!e_data.gpencil_background_sh) { - e_data.gpencil_background_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_background_frag_glsl, NULL); + if (draw_ctx->rv3d != NULL) { + sensor_scaled *= draw_ctx->rv3d->viewcamtexcofac[0]; + } + + pd->dof_params[1] = aperture * fabsf(focal_len_scaled / (focus_dist - focal_len_scaled)); + pd->dof_params[1] *= vp_size[0] / sensor_scaled; + pd->dof_params[0] = -focus_dist * pd->dof_params[1]; } - if (!e_data.gpencil_paper_sh) { - e_data.gpencil_paper_sh = DRW_shader_create_fullscreen(datatoc_gpencil_paper_frag_glsl, NULL); + else { + /* Disable DoF blur scalling. */ + pd->camera = NULL; } } -void GPENCIL_engine_init(void *vedata) +#define DRAW_NOW 2 + +typedef struct gpIterPopulateData { + Object *ob; + GPENCIL_tObject *tgp_ob; + GPENCIL_PrivateData *pd; + GPENCIL_MaterialPool *matpool; + DRWShadingGroup *grp; + /* Last material UBO bound. Used to avoid uneeded buffer binding. */ + GPUUniformBuffer *ubo_mat; + GPUUniformBuffer *ubo_lights; + /* Last texture bound. */ + GPUTexture *tex_fill; + GPUTexture *tex_stroke; + /* Offset in the material pool to the first material of this object. */ + int mat_ofs; + /* Is the sbuffer call need to be issued. */ + int do_sbuffer_call; + /* Indices to do correct insertion of the sbuffer stroke. */ + int stroke_index_last; + int stroke_index_offset; + /* Infos for call batching. */ + struct GPUBatch *geom; + bool instancing; + int vfirst, vcount; +} gpIterPopulateData; + +#define DISABLE_BATCHING 0 + +static void gpencil_drawcall_flush(gpIterPopulateData *iter) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - /* init storage */ - if (!stl->storage) { - stl->storage = MEM_callocN(sizeof(GPENCIL_Storage), "GPENCIL_Storage"); - stl->storage->shade_render[0] = OB_RENDER; - stl->storage->shade_render[1] = 0; +#if !DISABLE_BATCHING + if (iter->geom != NULL) { + if (iter->instancing) { + DRW_shgroup_call_instance_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); + } + else { + DRW_shgroup_call_range(iter->grp, iter->ob, iter->geom, iter->vfirst, iter->vcount); + } } +#endif - /* Not supported anymore. */ - stl->storage->multisamples = 0; - - /* create shaders */ - GPENCIL_create_shaders(); - GPENCIL_create_fx_shaders(&e_data); + iter->geom = NULL; + iter->vfirst = -1; + iter->vcount = 0; } -static void GPENCIL_engine_free(void) +/* Group drawcalls that are consecutive and with the same type. Reduces GPU driver overhead. */ +static void gp_drawcall_add( + gpIterPopulateData *iter, struct GPUBatch *geom, bool instancing, int v_first, int v_count) { - /* only free custom shaders, builtin shaders are freed in blender close */ - DRW_SHADER_FREE_SAFE(e_data.gpencil_fill_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_stroke_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_point_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_edit_point_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_line_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_fullscreen_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_simple_fullscreen_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_blend_fullscreen_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_background_sh); - DRW_SHADER_FREE_SAFE(e_data.gpencil_paper_sh); - - DRW_TEXTURE_FREE_SAFE(e_data.gpencil_blank_texture); - - /* effects */ - GPENCIL_delete_fx_shaders(&e_data); +#if DISABLE_BATCHING + if (instancing) { + DRW_shgroup_call_instance_range(iter->grp, iter->ob, geom, v_first, v_count); + } + else { + DRW_shgroup_call_range(iter->grp, iter->ob, geom, v_first, v_count); + } +#endif + + int last = iter->vfirst + iter->vcount; + /* Interupt drawcall grouping if the sequence is not consecutive. */ + if ((geom != iter->geom) || (v_first - last > 3)) { + gpencil_drawcall_flush(iter); + } + iter->geom = geom; + iter->instancing = instancing; + if (iter->vfirst == -1) { + iter->vfirst = v_first; + } + iter->vcount = v_first + v_count - iter->vfirst; } -/* Helper: Check if the main overlay and onion switches are enabled in any screen. - * - * This is required to generate the onion skin and limit the times the cache is updated because the - * cache is generated only in the first screen and if the first screen has the onion disabled the - * cache for onion skin is not generated. The loop adds time, but always is faster than regenerate - * the cache all the times. - */ -static void gpencil_check_screen_switches(const DRWContextState *draw_ctx, - GPENCIL_StorageList *stl) +static void gpencil_stroke_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk); + +static void gp_sbuffer_cache_populate(gpIterPopulateData *iter) { - stl->storage->is_main_overlay = false; - stl->storage->is_main_onion = false; - /* Check if main onion switch is enabled in any screen. */ - Main *bmain = CTX_data_main(draw_ctx->evil_C); - - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { - if (sa && sa->spacetype == SPACE_VIEW3D) { - View3D *v3d = sa->spacedata.first; - if (v3d == NULL) { - continue; - } - if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) { - stl->storage->is_main_overlay = true; - } - if (v3d->gp_flag & V3D_GP_SHOW_ONION_SKIN) { - stl->storage->is_main_onion = true; - } - } - /* If found, don't need loop more. */ - if ((stl->storage->is_main_overlay) && (stl->storage->is_main_onion)) { - return; - } - } - } + iter->do_sbuffer_call = DRAW_NOW; + /* In order to draw the sbuffer stroke correctly mixed with other strokes, + * we need to offset the stroke index of the sbuffer stroke and the subsequent strokes. + * Remember, sbuffer stroke indices start from 0. So we add last index to avoid + * masking issues. */ + iter->grp = DRW_shgroup_create_sub(iter->grp); + DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", iter->ubo_mat); + DRW_shgroup_uniform_float_copy(iter->grp, "strokeIndexOffset", iter->stroke_index_last); + + const DRWContextState *ctx = DRW_context_state_get(); + ToolSettings *ts = ctx->scene->toolsettings; + if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) { + /* In this case we can't do correct projection during stroke. We just disable depth test. */ + DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx); + } + + gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter); + gpencil_drawcall_flush(iter); + + iter->stroke_index_offset = iter->pd->sbuffer_stroke->totpoints + 1; + iter->do_sbuffer_call = 0; } -void GPENCIL_cache_init(void *vedata) +static void gpencil_layer_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *UNUSED(gps), + void *thunk) { - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - View3D *v3d = draw_ctx->v3d; - Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); - const View3DCursor *cursor = &scene->cursor; - - /* Special handling for when active object is GP object (e.g. for draw mode) */ - Object *obact = draw_ctx->obact; - bGPdata *obact_gpd = NULL; - MaterialGPencilStyle *gp_style = NULL; - - if (obact && (obact->type == OB_GPENCIL) && (obact->data)) { - obact_gpd = (bGPdata *)obact->data; - /* use the brush material */ - Material *ma = BKE_gpencil_object_material_get_from_brush(obact, brush); - if (ma != NULL) { - gp_style = ma->gp_style; - } - /* this is not common, but avoid any special situations when brush could be without material */ - if (gp_style == NULL) { - gp_style = BKE_gpencil_material_settings(obact, obact->actcol); - } - } + gpIterPopulateData *iter = (gpIterPopulateData *)thunk; + GPENCIL_PrivateData *pd = iter->pd; + bGPdata *gpd = (bGPdata *)iter->ob->data; - if (!stl->g_data) { - /* Alloc transient pointers */ - stl->g_data = MEM_mallocN(sizeof(g_data), "g_data"); - stl->storage->xray = GP_XRAY_FRONT; /* used for drawing */ - stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; /* used for drawing */ + gpencil_drawcall_flush(iter); + + if (iter->do_sbuffer_call) { + gp_sbuffer_cache_populate(iter); } - stl->storage->tonemapping = 0; - - stl->g_data->shgrps_edit_line = NULL; - stl->g_data->shgrps_edit_point = NULL; - - /* reset textures */ - stl->g_data->batch_buffer_stroke = NULL; - stl->g_data->batch_buffer_fill = NULL; - stl->g_data->batch_buffer_ctrlpoint = NULL; - stl->g_data->batch_grid = NULL; - - if (!stl->shgroups) { - /* Alloc maximum size because count strokes is very slow and can be very complex due onion - * skinning. - */ - stl->shgroups = MEM_mallocN(sizeof(GPENCIL_shgroup) * GPENCIL_MAX_SHGROUPS, "GPENCIL_shgroup"); + else { + iter->do_sbuffer_call = !pd->do_fast_drawing && (gpd == pd->sbuffer_gpd) && + (gpl == pd->sbuffer_layer) && + (gpf == NULL || gpf->runtime.onion_id == 0.0f); } - /* init gp objects cache */ - stl->g_data->gp_cache_used = 0; - stl->g_data->gp_cache_size = 0; - stl->g_data->gp_object_cache = NULL; - stl->g_data->do_instances = false; + GPENCIL_tLayer *tgp_layer = gpencil_layer_cache_add(pd, iter->ob, gpl, gpf, iter->tgp_ob); - { - /* Stroke pass 2D */ - psl->stroke_pass_2d = DRW_pass_create("GPencil Stroke Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - stl->storage->shgroup_id = 0; - /* Stroke pass 3D */ - psl->stroke_pass_3d = DRW_pass_create("GPencil Stroke Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - stl->storage->shgroup_id = 0; - - /* edit pass */ - psl->edit_pass = DRW_pass_create("GPencil Edit Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); - - /* detect if playing animation */ - if (draw_ctx->evil_C) { - - bool playing = ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != NULL; - if (playing != stl->storage->is_playing) { - stl->storage->reset_cache = true; - } - stl->storage->is_playing = playing; + const bool use_lights = pd->use_lighting && ((gpl->flag & GP_LAYER_USE_LIGHTS) != 0) && + (iter->ob->dtx & OB_USE_GPENCIL_LIGHTS); - /* Found if main overlay and onion switches are enabled in any screen. */ - gpencil_check_screen_switches(draw_ctx, stl); - } - else { - stl->storage->is_playing = false; - stl->storage->reset_cache = false; - stl->storage->is_main_overlay = false; - stl->storage->is_main_onion = false; - } - /* save render state */ - stl->storage->is_render = DRW_state_is_scene_render(); - stl->storage->is_mat_preview = (bool)stl->storage->is_render && - STREQ(scene->id.name + 2, "preview"); - - if (obact_gpd) { - /* for some reason, when press play there is a delay in the animation flag check - * and this produces errors. To be sure, we set cache as dirty because the frame - * is changing. - */ - if (stl->storage->is_playing == true) { - obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } - /* if render, set as dirty to update all data */ - else if (stl->storage->is_render == true) { - obact_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } - } + iter->ubo_lights = (use_lights) ? pd->global_light_pool->ubo : pd->shadeless_light_pool->ubo; - /* save simplify flags (can change while drawing, so it's better to save) */ - stl->storage->simplify_fill = GPENCIL_SIMPLIFY_FILL(scene, stl->storage->is_playing); - stl->storage->simplify_modif = GPENCIL_SIMPLIFY_MODIF(scene, stl->storage->is_playing); - stl->storage->simplify_fx = GPENCIL_SIMPLIFY_FX(scene, stl->storage->is_playing); - stl->storage->simplify_blend = GPENCIL_SIMPLIFY_BLEND(scene, stl->storage->is_playing); + gpencil_material_resources_get(iter->matpool, 0, NULL, NULL, &iter->ubo_mat); - /* xray mode */ - if (v3d) { - stl->storage->is_xray = XRAY_ACTIVE(v3d); - } - else { - stl->storage->is_xray = 0; - } + /* Iterator dependent uniforms. */ + DRWShadingGroup *grp = iter->grp = tgp_layer->base_shgrp; + DRW_shgroup_uniform_block_persistent(grp, "gpLightBlock", iter->ubo_lights); + DRW_shgroup_uniform_block(grp, "gpMaterialBlock", iter->ubo_mat); + DRW_shgroup_uniform_texture(grp, "gpFillTexture", iter->tex_fill); + DRW_shgroup_uniform_texture(grp, "gpStrokeTexture", iter->tex_stroke); + DRW_shgroup_uniform_int_copy(grp, "gpMaterialOffset", iter->mat_ofs); + DRW_shgroup_uniform_float_copy(grp, "strokeIndexOffset", iter->stroke_index_offset); +} - /* save pixsize */ - stl->storage->pixsize = DRW_viewport_pixelsize_get(); - if ((!DRW_state_is_opengl_render()) && (stl->storage->is_render)) { - stl->storage->pixsize = &stl->storage->render_pixsize; - } +static void gpencil_stroke_cache_populate(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk) +{ + gpIterPopulateData *iter = (gpIterPopulateData *)thunk; - /* detect if painting session */ - if ((obact_gpd) && (obact_gpd->flag & GP_DATA_STROKE_PAINTMODE) && - (stl->storage->is_playing == false)) { - /* need the original to avoid cow overhead while drawing */ - bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&obact_gpd->id); - if (((gpd_orig->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) && - (gpd_orig->runtime.sbuffer_used > 0) && - ((gpd_orig->flag & GP_DATA_STROKE_POLYGON) == 0) && !DRW_state_is_depth() && - (stl->storage->background_ready == true)) { - stl->g_data->session_flag |= GP_DRW_PAINT_PAINTING; - } - else { - stl->g_data->session_flag = GP_DRW_PAINT_IDLE; - } - } - else { - /* if not drawing mode */ - stl->g_data->session_flag = GP_DRW_PAINT_HOLD; - } + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); - if (gp_style) { - stl->storage->stroke_style = gp_style->stroke_style; - stl->storage->color_type = GPENCIL_COLOR_SOLID; - if (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) { - stl->storage->color_type = GPENCIL_COLOR_TEXTURE; - if (gp_style->flag & GP_STYLE_STROKE_PATTERN) { - stl->storage->color_type = GPENCIL_COLOR_PATTERN; - } - } + bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; + bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; + bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) && + (!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0); + + bool only_lines = gpl && gpf && gpl->actframe != gpf && iter->pd->use_multiedit_lines_only; + + if (hide_material || (!show_stroke && !show_fill) || only_lines) { + return; + } + + GPUUniformBuffer *ubo_mat; + GPUTexture *tex_stroke, *tex_fill; + gpencil_material_resources_get( + iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat); + + bool resource_changed = (iter->ubo_mat != ubo_mat) || + (tex_fill && (iter->tex_fill != tex_fill)) || + (tex_stroke && (iter->tex_stroke != tex_stroke)); + + if (resource_changed) { + gpencil_drawcall_flush(iter); + + iter->grp = DRW_shgroup_create_sub(iter->grp); + if (iter->ubo_mat != ubo_mat) { + DRW_shgroup_uniform_block(iter->grp, "gpMaterialBlock", ubo_mat); + iter->ubo_mat = ubo_mat; } - else { - stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; - stl->storage->color_type = GPENCIL_COLOR_SOLID; + if (tex_fill) { + DRW_shgroup_uniform_texture(iter->grp, "gpFillTexture", tex_fill); + iter->tex_fill = tex_fill; } - - /* drawing buffer pass for drawing the stroke that is being drawing by the user. The data - * is stored in sbuffer - */ - psl->drawing_pass = DRW_pass_create("GPencil Drawing Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | - DRW_STATE_WRITE_STENCIL | DRW_STATE_STENCIL_NEQUAL); - - /* full screen pass to combine the result with default framebuffer */ - struct GPUBatch *quad = DRW_cache_fullscreen_quad_get(); - psl->mix_pass = DRW_pass_create("GPencil Mix Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); - DRWShadingGroup *mix_shgrp = DRW_shgroup_create(e_data.gpencil_fullscreen_sh, psl->mix_pass); - DRW_shgroup_call(mix_shgrp, quad, NULL); - DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeColor", &stl->g_data->input_color_tx); - DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeDepth", &stl->g_data->input_depth_tx); - DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1); - DRW_shgroup_uniform_int(mix_shgrp, "do_select", &stl->storage->do_select_outline, 1); - DRW_shgroup_uniform_vec4(mix_shgrp, "select_color", stl->storage->select_color, 1); - - /* Mix pass no blend used to copy between passes. A separated pass is required - * because if mix_pass is used, the accumulation of blend degrade the colors. - * - * This pass is used too to take the snapshot used for background_pass. This image - * will be used as the background while the user is drawing. - */ - psl->mix_pass_noblend = DRW_pass_create("GPencil Mix Pass no blend", - DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS); - DRWShadingGroup *mix_shgrp_noblend = DRW_shgroup_create(e_data.gpencil_fullscreen_sh, - psl->mix_pass_noblend); - DRW_shgroup_call(mix_shgrp_noblend, quad, NULL); - DRW_shgroup_uniform_texture_ref( - mix_shgrp_noblend, "strokeColor", &stl->g_data->input_color_tx); - DRW_shgroup_uniform_texture_ref( - mix_shgrp_noblend, "strokeDepth", &stl->g_data->input_depth_tx); - DRW_shgroup_uniform_int(mix_shgrp_noblend, "tonemapping", &stl->storage->tonemapping, 1); - DRW_shgroup_uniform_int(mix_shgrp_noblend, "do_select", &stl->storage->do_select_outline, 1); - DRW_shgroup_uniform_vec4(mix_shgrp_noblend, "select_color", stl->storage->select_color, 1); - - /* Painting session pass (used only to speedup while the user is drawing ) - * This pass is used to show the snapshot of the current grease pencil strokes captured - * when the user starts to draw (see comments above). - * In this way, the previous strokes don't need to be redraw and the drawing process - * is far to agile. - */ - psl->background_pass = DRW_pass_create("GPencil Background Painting Session Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); - DRWShadingGroup *background_shgrp = DRW_shgroup_create(e_data.gpencil_background_sh, - psl->background_pass); - DRW_shgroup_call(background_shgrp, quad, NULL); - DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeColor", &txl->background_color_tx); - DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeDepth", &txl->background_depth_tx); - - /* pass for drawing paper (only if viewport) - * In render, the v3d is null so the paper is disabled - * The paper is way to isolate the drawing in complex scene and to have a cleaner - * drawing area. - */ - if (v3d) { - psl->paper_pass = DRW_pass_create("GPencil Paper Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); - DRWShadingGroup *paper_shgrp = DRW_shgroup_create(e_data.gpencil_paper_sh, psl->paper_pass); - DRW_shgroup_call(paper_shgrp, quad, NULL); - DRW_shgroup_uniform_vec3(paper_shgrp, "color", v3d->shading.background_color, 1); - DRW_shgroup_uniform_float(paper_shgrp, "opacity", &v3d->overlay.gpencil_paper_opacity, 1); + if (tex_stroke) { + DRW_shgroup_uniform_texture(iter->grp, "gpStrokeTexture", tex_stroke); + iter->tex_stroke = tex_stroke; } - /* grid pass */ - if ((v3d) && (obact) && (obact->type == OB_GPENCIL)) { - psl->grid_pass = DRW_pass_create("GPencil Grid Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); - stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass); - - /* define grid orientation */ - switch (ts->gp_sculpt.lock_axis) { - case GP_LOCKAXIS_VIEW: { - /* align always to view */ - invert_m4_m4(stl->storage->grid_matrix, draw_ctx->rv3d->viewmat); - /* copy ob location */ - copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]); - break; - } - case GP_LOCKAXIS_CURSOR: { - float scale[3] = {1.0f, 1.0f, 1.0f}; - loc_eul_size_to_mat4( - stl->storage->grid_matrix, cursor->location, cursor->rotation_euler, scale); - break; - } - default: { - copy_m4_m4(stl->storage->grid_matrix, obact->obmat); - break; - } - } + /* TODO(fclem): This is a quick workaround but + * ideally we should have this as a permanent bind. */ + const bool is_masked = iter->tgp_ob->layers.last->mask_bits != NULL; + GPUTexture **mask_tex = (is_masked) ? &iter->pd->mask_tx : &iter->pd->dummy_tx; + DRW_shgroup_uniform_texture_ref(iter->grp, "gpMaskTexture", mask_tex); + } - /* Move the origin to Object or Cursor */ - if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { - copy_v3_v3(stl->storage->grid_matrix[3], cursor->location); - } - else { - copy_v3_v3(stl->storage->grid_matrix[3], obact->obmat[3]); - } - DRW_shgroup_uniform_mat4( - stl->g_data->shgrps_grid, "gpModelMatrix", stl->storage->grid_matrix); - } + bool do_sbuffer = (iter->do_sbuffer_call == DRAW_NOW); - /* blend layers pass */ - psl->blend_pass = DRW_pass_create("GPencil Blend Layers Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); - DRWShadingGroup *blend_shgrp = DRW_shgroup_create(e_data.gpencil_blend_fullscreen_sh, - psl->blend_pass); - DRW_shgroup_call(blend_shgrp, quad, NULL); - DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(blend_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(blend_shgrp, "blendDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_int(blend_shgrp, "mode", &stl->storage->blend_mode, 1); - DRW_shgroup_uniform_int(blend_shgrp, "mask_layer", &stl->storage->mask_layer, 1); - DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1); - - /* create effects passes */ - if (!stl->storage->simplify_fx) { - GPENCIL_create_fx_passes(psl); - } + if (show_fill) { + GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_fill_get(iter->ob) : + DRW_cache_gpencil_fills_get(iter->ob, iter->pd->cfra); + int vfirst = gps->runtime.fill_start * 3; + int vcount = gps->tot_triangles * 3; + gp_drawcall_add(iter, geom, false, vfirst, vcount); } + + if (show_stroke) { + GPUBatch *geom = do_sbuffer ? DRW_cache_gpencil_sbuffer_stroke_get(iter->ob) : + DRW_cache_gpencil_strokes_get(iter->ob, iter->pd->cfra); + /* Start one vert before to have gl_InstanceID > 0 (see shader). */ + int vfirst = gps->runtime.stroke_start - 1; + /* Include "potential" cyclic vertex and start adj vertex (see shader). */ + int vcount = gps->totpoints + 1 + 1; + gp_drawcall_add(iter, geom, true, vfirst, vcount); + } + + iter->stroke_index_last = gps->runtime.stroke_start + gps->totpoints + 1; } -static void gpencil_add_draw_data(void *vedata, Object *ob) +static void gp_sbuffer_cache_populate_fast(GPENCIL_Data *vedata, gpIterPopulateData *iter) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const DRWContextState *draw_ctx = DRW_context_state_get(); - const View3D *v3d = draw_ctx->v3d; + bGPdata *gpd = (bGPdata *)iter->ob->data; + if (gpd != iter->pd->sbuffer_gpd) { + return; + } - int i = stl->g_data->gp_cache_used - 1; - tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; + GPENCIL_TextureList *txl = vedata->txl; + GPUTexture *depth_texture = iter->pd->scene_depth_tx; + GPENCIL_tObject *last_tgp_ob = iter->pd->tobjects.last; + /* Create another temp object that only contain the stroke. */ + iter->tgp_ob = gpencil_object_cache_add(iter->pd, iter->ob); + /* Remove from the main list. */ + iter->pd->tobjects.last = last_tgp_ob; + last_tgp_ob->next = NULL; + /* Add to sbuffer tgpobject list. */ + BLI_LINKS_APPEND(&iter->pd->sbuffer_tobjects, iter->tgp_ob); + /* Remove depth test with scene (avoid self occlusion). */ + iter->pd->scene_depth_tx = txl->dummy_texture; - if (!cache_ob->is_dup_ob) { - /* fill shading groups */ - if ((!is_multiedit) || (stl->storage->is_render)) { - gpencil_populate_datablock(&e_data, vedata, ob, cache_ob); - } - else { - gpencil_populate_multiedit(&e_data, vedata, ob, cache_ob); - } - } + gpencil_layer_cache_populate( + iter->pd->sbuffer_layer, iter->pd->sbuffer_layer->actframe, NULL, iter); - /* FX passses */ - cache_ob->has_fx = false; - if ((!stl->storage->simplify_fx) && - ((!ELEM(cache_ob->shading_type[0], OB_WIRE, OB_SOLID)) || - ((v3d->spacetype != SPACE_VIEW3D))) && - (BKE_shaderfx_has_gpencil(ob))) { - cache_ob->has_fx = true; - if ((!stl->storage->simplify_fx) && (!is_multiedit)) { - gpencil_fx_prepare(&e_data, vedata, cache_ob); - } + const DRWContextState *ctx = DRW_context_state_get(); + ToolSettings *ts = ctx->scene->toolsettings; + if (ts->gpencil_v3d_align & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) { + /* In this case we can't do correct projection during stroke. We just disable depth test. */ + DRW_shgroup_uniform_texture(iter->grp, "gpSceneDepthTexture", iter->pd->dummy_tx); } + + iter->do_sbuffer_call = DRAW_NOW; + gpencil_stroke_cache_populate(NULL, NULL, iter->pd->sbuffer_stroke, iter); + gpencil_drawcall_flush(iter); + + gpencil_vfx_cache_populate(vedata, iter->ob, iter->tgp_ob); + + /* Restore state. */ + iter->do_sbuffer_call = 0; + iter->pd->scene_depth_tx = depth_texture; } -void GPENCIL_cache_populate(void *vedata, Object *ob) +void GPENCIL_cache_populate(void *ved, Object *ob) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_TextureList *txl = vedata->txl; + /* object must be visible */ if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { return; } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - ToolSettings *ts = scene->toolsettings; - View3D *v3d = draw_ctx->v3d; + if (ob->data && (ob->type == OB_GPENCIL) && (ob->dt >= OB_SOLID)) { + gpIterPopulateData iter = {0}; + iter.ob = ob; + iter.pd = pd; + iter.tgp_ob = gpencil_object_cache_add(pd, ob); + iter.matpool = gpencil_material_pool_create(pd, ob, &iter.mat_ofs); + iter.tex_fill = txl->dummy_texture; + iter.tex_stroke = txl->dummy_texture; - if (ob->type == OB_GPENCIL && ob->data) { + /* Special case for rendering onion skin. */ bGPdata *gpd = (bGPdata *)ob->data; + bool do_onion = (!pd->is_render) ? pd->do_onion : (gpd->onion_flag & GP_ONION_GHOST_ALWAYS); - /* enable multisample and basic framebuffer creation */ - stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_MULTISAMPLE; - stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_BASIC; - - /* when start/stop animation the cache must be set as dirty to reset all data */ - if (stl->storage->reset_cache) { - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - stl->storage->reset_cache = false; - } + BKE_gpencil_visible_stroke_iter(ob, + gpencil_layer_cache_populate, + gpencil_stroke_cache_populate, + &iter, + do_onion, + pd->cfra); - if ((stl->g_data->session_flag & GP_DRW_PAINT_READY) == 0) { - /* bound box object are not visible, only external box*/ - if (ob->dt != OB_BOUNDBOX) { - /* save gp objects for drawing later */ - stl->g_data->gp_object_cache = gpencil_object_cache_add(stl->g_data->gp_object_cache, - ob, - &stl->g_data->gp_cache_size, - &stl->g_data->gp_cache_used); - - /* enable instance loop */ - if (!stl->g_data->do_instances) { - tGPencilObjectCache *cache_ob = - &stl->g_data->gp_object_cache[stl->g_data->gp_cache_used - 1]; - stl->g_data->do_instances = cache_ob->is_dup_ob; - } - - /* load drawing data */ - gpencil_add_draw_data(vedata, ob); - } - } + gpencil_drawcall_flush(&iter); - /* draw current painting strokes - * (only if region is equal to originated paint region) - * - * Need to use original data because to use the copy of data, the paint - * operator must update depsgraph and this makes that first events of the - * mouse are missed if the datablock is very big due the time required to - * copy the datablock. The search of the original data is faster than a - * full datablock copy. - * Using the original data doesn't require a copy and the feel when drawing - * is far better. - */ - - bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id); - if ((draw_ctx->obact == ob) && - ((gpd_orig->runtime.ar == NULL) || (gpd_orig->runtime.ar == draw_ctx->region))) { - gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob); + if (iter.do_sbuffer_call) { + gp_sbuffer_cache_populate(&iter); } - /* grid */ - if ((v3d) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID) && - (ob->type == OB_GPENCIL) && (ob == draw_ctx->obact) && - ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0) && - ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) == 0)) { + gpencil_vfx_cache_populate(vedata, ob, iter.tgp_ob); - stl->g_data->batch_grid = gpencil_get_grid(ob); - DRW_shgroup_call(stl->g_data->shgrps_grid, stl->g_data->batch_grid, NULL); + if (pd->do_fast_drawing) { + gp_sbuffer_cache_populate_fast(vedata, &iter); } } + + if (ob->type == OB_LAMP && pd->use_lights) { + gpencil_light_pool_populate(pd->global_light_pool, ob); + } } -void GPENCIL_cache_finish(void *vedata) +void GPENCIL_cache_finish(void *ved) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - tGPencilObjectCache *cache_ob = NULL; - Object *ob = NULL; - - /* create data for instances */ - if (stl->g_data->do_instances) { - GHash *gh_objects = BLI_ghash_str_new(__func__); - /* create hash of real object (non duplicated) */ - for (int i = 0; i < stl->g_data->gp_cache_used; i++) { - cache_ob = &stl->g_data->gp_object_cache[i]; - if (!cache_ob->is_dup_ob) { - ob = cache_ob->ob; - char *name = BKE_id_to_unique_string_key(&ob->id); - BLI_ghash_insert(gh_objects, name, cache_ob->ob); - } - } - - /* draw particles */ - gpencil_populate_particles(&e_data, gh_objects, vedata); + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; - /* free hash */ - BLI_ghash_free(gh_objects, MEM_freeN, NULL); + /* Upload UBO data. */ + BLI_memblock_iter iter; + BLI_memblock_iternew(pd->gp_material_pool, &iter); + GPENCIL_MaterialPool *pool; + while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) { + GPU_uniformbuffer_update(pool->ubo, pool->mat_data); } - if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) { - stl->storage->framebuffer_flag |= GP_FRAMEBUFFER_DRAW; + BLI_memblock_iternew(pd->gp_light_pool, &iter); + GPENCIL_LightPool *lpool; + while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) { + GPU_uniformbuffer_update(lpool->ubo, lpool->light_data); } - /* create framebuffers (only for normal drawing) */ - if (!DRW_state_is_select() || !DRW_state_is_depth()) { - GPENCIL_create_framebuffers(vedata); + /* Sort object by decreasing Z to avoid most of alpha ordering issues. */ + gpencil_object_cache_sort(pd); + + /* Create framebuffers only if needed. */ + if (pd->tobjects.first) { + eGPUTextureFormat format = pd->use_signed_fb ? GPU_RGBA16F : GPU_R11F_G11F_B10F; + + const float *size = DRW_viewport_size_get(); + pd->depth_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); + pd->color_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_tx = DRW_texture_pool_query_2d(size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->gpencil_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_tx), + }); + + if (pd->use_layer_fb) { + pd->color_layer_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_layer_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->layer_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_layer_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_layer_tx), + }); + }; + + if (pd->use_object_fb) { + pd->color_object_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + pd->reveal_object_tx = DRW_texture_pool_query_2d( + size[0], size[1], format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->object_fb, + { + GPU_ATTACHMENT_TEXTURE(pd->depth_tx), + GPU_ATTACHMENT_TEXTURE(pd->color_object_tx), + GPU_ATTACHMENT_TEXTURE(pd->reveal_object_tx), + }); + } + + if (pd->use_mask_fb) { + /* We need an extra depth to not disturb the normal drawing. + * The color_tx is needed for framebuffer cmpleteness. */ + GPUTexture *color_tx, *depth_tx; + depth_tx = DRW_texture_pool_query_2d( + size[0], size[1], GPU_DEPTH24_STENCIL8, &draw_engine_gpencil_type); + color_tx = DRW_texture_pool_query_2d(size[0], size[1], GPU_R8, &draw_engine_gpencil_type); + /* Use high quality format for render. */ + eGPUTextureFormat mask_format = pd->is_render ? GPU_R16 : GPU_R8; + pd->mask_tx = DRW_texture_pool_query_2d( + size[0], size[1], mask_format, &draw_engine_gpencil_type); + + GPU_framebuffer_ensure_config(&fbl->mask_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_TEXTURE(color_tx), + GPU_ATTACHMENT_TEXTURE(pd->mask_tx), + }); + } + + GPENCIL_antialiasing_init(vedata); } } -/* helper function to sort inverse gpencil objects using qsort */ -static int gpencil_object_cache_compare_zdepth(const void *a1, const void *a2) +static void GPENCIL_draw_scene_depth_only(void *ved) { - const tGPencilObjectCache *ps1 = a1, *ps2 = a2; + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - if (ps1->zdepth < ps2->zdepth) { - return 1; - } - else if (ps1->zdepth > ps2->zdepth) { - return -1; + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(dfbl->depth_only_fb); } - return 0; -} - -/* prepare a texture with full viewport screenshot for fast drawing */ -static void gpencil_prepare_fast_drawing(GPENCIL_StorageList *stl, - DefaultFramebufferList *dfbl, - GPENCIL_FramebufferList *fbl, - DRWPass *pass, - const float clearcol[4]) -{ - if (stl->g_data->session_flag & (GP_DRW_PAINT_IDLE | GP_DRW_PAINT_FILLING)) { - GPU_framebuffer_bind(fbl->background_fb); - /* clean only in first loop cycle */ - if (stl->g_data->session_flag & GP_DRW_PAINT_IDLE) { - GPU_framebuffer_clear_color_depth_stencil(fbl->background_fb, clearcol, 1.0f, 0x0); - stl->g_data->session_flag = GP_DRW_PAINT_FILLING; + for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) { + for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) { + DRW_draw_pass(layer->geom_ps); } - /* repeat pass to fill temp texture */ - DRW_draw_pass(pass); - /* set default framebuffer again */ + } + + if (DRW_state_is_fbo()) { GPU_framebuffer_bind(dfbl->default_fb); + } + + pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL; - stl->storage->background_ready = true; + /* Free temp stroke buffers. */ + if (pd->sbuffer_gpd) { + DRW_cache_gpencil_sbuffer_clear(pd->obact); } } -void DRW_gpencil_free_runtime_data(void *ved) +static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL_tLayer *layer) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_col[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + float clear_depth = ob->is_drawmode3d ? 1.0f : 0.0f; + bool inverted = false; + /* OPTI(fclem) we could optimize by only clearing if the new mask_bits does not contain all + * the masks already rendered in the buffer, and drawing only the layers not already drawn. */ + bool cleared = false; - /* free gpu data */ - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_stroke); - MEM_SAFE_FREE(stl->g_data->batch_buffer_stroke); + DRW_stats_group_start("GPencil Mask"); - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_fill); - MEM_SAFE_FREE(stl->g_data->batch_buffer_fill); + GPU_framebuffer_bind(fbl->mask_fb); - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_buffer_ctrlpoint); - MEM_SAFE_FREE(stl->g_data->batch_buffer_ctrlpoint); + for (int i = 0; i < GP_MAX_MASKBITS; i++) { + if (!BLI_BITMAP_TEST(layer->mask_bits, i)) { + continue; + } - GPU_BATCH_DISCARD_SAFE(stl->g_data->batch_grid); - MEM_SAFE_FREE(stl->g_data->batch_grid); + if (BLI_BITMAP_TEST_BOOL(layer->mask_invert_bits, i) != inverted) { + if (cleared) { + DRW_draw_pass(psl->mask_invert_ps); + } + inverted = !inverted; + } - if (stl->g_data->gp_object_cache == NULL) { - return; + if (!cleared) { + cleared = true; + GPU_framebuffer_clear_color_depth(fbl->mask_fb, clear_col, clear_depth); + } + + GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i); + BLI_assert(mask_layer); + + DRW_draw_pass(mask_layer->geom_ps); } - /* reset all cache flags */ - for (int i = 0; i < stl->g_data->gp_cache_used; i++) { - tGPencilObjectCache *cache_ob = &stl->g_data->gp_object_cache[i]; - if (cache_ob) { - bGPdata *gpd = cache_ob->gpd; - gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; - - /* free shgrp array */ - cache_ob->tot_layers = 0; - MEM_SAFE_FREE(cache_ob->name); - MEM_SAFE_FREE(cache_ob->shgrp_array); - } + if (!inverted) { + /* Blend shader expect an opacity mask not a reavealage buffer. */ + DRW_draw_pass(psl->mask_invert_ps); } - /* free the cache itself */ - MEM_SAFE_FREE(stl->g_data->gp_object_cache); + DRW_stats_group_end(); } -static void gpencil_draw_pass_range(GPENCIL_FramebufferList *fbl, - GPENCIL_StorageList *stl, - GPENCIL_PassList *psl, - GPUFrameBuffer *fb, - Object *ob, - bGPdata *gpd, - DRWShadingGroup *init_shgrp, - DRWShadingGroup *end_shgrp, - bool multi) +static void GPENCIL_draw_object(GPENCIL_Data *vedata, GPENCIL_tObject *ob) { - if (init_shgrp == NULL) { - return; + GPENCIL_PassList *psl = vedata->psl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}; + + DRW_stats_group_start("GPencil Object"); + + GPUFrameBuffer *fb_object = (ob->vfx.first) ? fbl->object_fb : fbl->gpencil_fb; + + GPU_framebuffer_bind(fb_object); + GPU_framebuffer_clear_depth_stencil(fb_object, ob->is_drawmode3d ? 1.0f : 0.0f, 0x00); + + if (ob->vfx.first) { + GPU_framebuffer_multi_clear(fb_object, clear_cols); } - const bool do_antialiasing = ((!stl->storage->is_mat_preview) && (multi)); + for (GPENCIL_tLayer *layer = ob->layers.first; layer; layer = layer->next) { + if (layer->mask_bits) { + gpencil_draw_mask(vedata, ob, layer); + } + + if (layer->blend_ps) { + GPU_framebuffer_bind(fbl->layer_fb); + GPU_framebuffer_multi_clear(fbl->layer_fb, clear_cols); + } + else { + GPU_framebuffer_bind(fb_object); + } + + DRW_draw_pass(layer->geom_ps); - if (do_antialiasing) { - MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + if (layer->blend_ps) { + GPU_framebuffer_bind(fb_object); + DRW_draw_pass(layer->blend_ps); + } + } + + for (GPENCIL_tVfx *vfx = ob->vfx.first; vfx; vfx = vfx->next) { + GPU_framebuffer_bind(*(vfx->target_fb)); + DRW_draw_pass(vfx->vfx_ps); } - DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : psl->stroke_pass_2d, - init_shgrp, - end_shgrp); + copy_m4_m4(pd->object_bound_mat, ob->plane_mat); + pd->is_stroke_order_3d = ob->is_drawmode3d; - if (do_antialiasing) { - MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fb, txl); + if (pd->scene_fb) { + GPU_framebuffer_bind(pd->scene_fb); + DRW_draw_pass(psl->merge_depth_ps); } + + DRW_stats_group_end(); } -/* draw strokes to use for selection */ -static void drw_gpencil_select_render(GPENCIL_StorageList *stl, GPENCIL_PassList *psl) +static void GPENCIL_fast_draw_start(GPENCIL_Data *vedata) { - tGPencilObjectCache *cache_ob; - tGPencilObjectCache_shgrp *array_elm = NULL; - DRWShadingGroup *init_shgrp = NULL; - DRWShadingGroup *end_shgrp = NULL; - - /* Draw all pending objects */ - if ((stl->g_data->gp_cache_used > 0) && (stl->g_data->gp_object_cache)) { - /* sort by zdepth */ - qsort(stl->g_data->gp_object_cache, - stl->g_data->gp_cache_used, - sizeof(tGPencilObjectCache), - gpencil_object_cache_compare_zdepth); - - for (int i = 0; i < stl->g_data->gp_cache_used; i++) { - cache_ob = &stl->g_data->gp_object_cache[i]; - if (cache_ob) { - Object *ob = cache_ob->ob; - bGPdata *gpd = cache_ob->gpd; - init_shgrp = NULL; - if (cache_ob->tot_layers > 0) { - for (int e = 0; e < cache_ob->tot_layers; e++) { - array_elm = &cache_ob->shgrp_array[e]; - if (init_shgrp == NULL) { - init_shgrp = array_elm->init_shgrp; - } - end_shgrp = array_elm->end_shgrp; - } - /* draw group */ - DRW_draw_pass_subset(GPENCIL_3D_DRAWMODE(ob, gpd) ? psl->stroke_pass_3d : - psl->stroke_pass_2d, - init_shgrp, - end_shgrp); - } - /* the cache must be dirty for next loop */ - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } - } + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (!pd->snapshot_buffer_dirty) { + /* Copy back cached render. */ + GPU_framebuffer_blit(fbl->snapshot_fb, 0, dfbl->default_fb, 0, GPU_DEPTH_BIT); + GPU_framebuffer_blit(fbl->snapshot_fb, 0, fbl->gpencil_fb, 0, GPU_COLOR_BIT); + GPU_framebuffer_blit(fbl->snapshot_fb, 1, fbl->gpencil_fb, 1, GPU_COLOR_BIT); + /* Bypass drawing. */ + pd->tobjects.first = pd->tobjects.last = NULL; } } -/* draw scene */ -void GPENCIL_draw_scene(void *ved) +static void GPENCIL_fast_draw_end(GPENCIL_Data *vedata) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); - tGPencilObjectCache *cache_ob; - tGPencilObjectCache_shgrp *array_elm = NULL; - DRWShadingGroup *init_shgrp = NULL; - DRWShadingGroup *end_shgrp = NULL; - - const float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - Object *obact = draw_ctx->obact; - const bool playing = stl->storage->is_playing; - const bool is_render = stl->storage->is_render; - bGPdata *gpd_act = (obact) && (obact->type == OB_GPENCIL) ? (bGPdata *)obact->data : NULL; - const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd_act); - const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) : true; - - /* if the draw is for select, do a basic drawing and return */ - if (DRW_state_is_select() || DRW_state_is_depth()) { - drw_gpencil_select_render(stl, psl); - return; + if (pd->snapshot_buffer_dirty) { + /* Save to snapshot buffer. */ + GPU_framebuffer_blit(dfbl->default_fb, 0, fbl->snapshot_fb, 0, GPU_DEPTH_BIT); + GPU_framebuffer_blit(fbl->gpencil_fb, 0, fbl->snapshot_fb, 0, GPU_COLOR_BIT); + GPU_framebuffer_blit(fbl->gpencil_fb, 1, fbl->snapshot_fb, 1, GPU_COLOR_BIT); + pd->snapshot_buffer_dirty = false; } - - /* paper pass to display a comfortable area to draw over complex scenes with geometry */ - if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { - if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_PAPER)) { - DRW_draw_pass(psl->paper_pass); - } + /* Draw the sbuffer stroke(s). */ + for (GPENCIL_tObject *ob = pd->sbuffer_tobjects.first; ob; ob = ob->next) { + GPENCIL_draw_object(vedata, ob); } +} - /* if we have a painting session, we use fast viewport drawing method */ - if ((!is_render) && (stl->g_data->session_flag & GP_DRW_PAINT_PAINTING)) { - GPU_framebuffer_bind(dfbl->default_fb); - - if (obact->dt != OB_BOUNDBOX) { - DRW_draw_pass(psl->background_pass); - } +void GPENCIL_draw_scene(void *ved) +{ + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_PrivateData *pd = vedata->stl->pd; + GPENCIL_FramebufferList *fbl = vedata->fbl; + float clear_cols[2][4] = {{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}; - MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + /* Fade 3D objects. */ + if ((!pd->is_render) && (pd->fade_3d_object_opacity > -1.0f)) { + mul_v4_fl(clear_cols[1], pd->fade_3d_object_opacity); + } - DRW_draw_pass(psl->drawing_pass); + if (pd->draw_depth_only) { + GPENCIL_draw_scene_depth_only(vedata); + return; + } - MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl); + if (pd->tobjects.first == NULL) { + return; + } - /* grid pass */ - if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { - if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) { - DRW_draw_pass(psl->grid_pass); - } - } + if (pd->do_fast_drawing) { + GPENCIL_fast_draw_start(vedata); + } - /* free memory */ - DRW_gpencil_free_runtime_data(ved); + if (pd->tobjects.first) { + GPU_framebuffer_bind(fbl->gpencil_fb); + GPU_framebuffer_multi_clear(fbl->gpencil_fb, clear_cols); + } - return; + for (GPENCIL_tObject *ob = pd->tobjects.first; ob; ob = ob->next) { + GPENCIL_draw_object(vedata, ob); } - if (DRW_state_is_fbo()) { + if (pd->do_fast_drawing) { + GPENCIL_fast_draw_end(vedata); + } - /* Draw all pending objects */ - if (stl->g_data->gp_cache_used > 0) { - /* sort by zdepth */ - qsort(stl->g_data->gp_object_cache, - stl->g_data->gp_cache_used, - sizeof(tGPencilObjectCache), - gpencil_object_cache_compare_zdepth); - - for (int i = 0; i < stl->g_data->gp_cache_used; i++) { - cache_ob = &stl->g_data->gp_object_cache[i]; - Object *ob = cache_ob->ob; - bGPdata *gpd = cache_ob->gpd; - init_shgrp = NULL; - /* Render stroke in separated framebuffer */ - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0); - /* Stroke Pass: - * draw only a subset that usually starts with a fill and ends with stroke - */ - bool use_blend = false; - if (cache_ob->tot_layers > 0) { - for (int e = 0; e < cache_ob->tot_layers; e++) { - bool is_last = (e == cache_ob->tot_layers - 1) ? true : false; - array_elm = &cache_ob->shgrp_array[e]; - - if (((array_elm->mode == eGplBlendMode_Regular) && (!use_blend) && - (!array_elm->mask_layer)) || - (e == 0)) { - if (init_shgrp == NULL) { - init_shgrp = array_elm->init_shgrp; - } - end_shgrp = array_elm->end_shgrp; - } - else { - use_blend = true; - /* draw pending groups */ - gpencil_draw_pass_range( - fbl, stl, psl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, is_last); - - /* Draw current group in separated texture to blend later */ - init_shgrp = array_elm->init_shgrp; - end_shgrp = array_elm->end_shgrp; - - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_fx, clearcol, 1.0f, 0x0); - gpencil_draw_pass_range( - fbl, stl, psl, fbl->temp_fb_fx, ob, gpd, init_shgrp, end_shgrp, is_last); - - /* Blend A texture and FX texture */ - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_b, clearcol, 1.0f, 0x0); - stl->storage->blend_mode = array_elm->mode; - stl->storage->mask_layer = (int)array_elm->mask_layer; - stl->storage->tonemapping = 1; - DRW_draw_pass(psl->blend_pass); - stl->storage->tonemapping = 0; - - /* Copy B texture to A texture to follow loop */ - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; - - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth_stencil(fbl->temp_fb_a, clearcol, 1.0f, 0x0); - DRW_draw_pass(psl->mix_pass_noblend); - - /* prepare next group */ - init_shgrp = NULL; - } - } - /* last group */ - gpencil_draw_pass_range( - fbl, stl, psl, fbl->temp_fb_a, ob, gpd, init_shgrp, end_shgrp, true); - } - - /* Current buffer drawing */ - if ((!is_render) && (cache_ob->is_dup_ob == false)) { - DRW_draw_pass(psl->drawing_pass); - } - /* fx passes */ - if (cache_ob->has_fx == true) { - stl->storage->tonemapping = 0; - gpencil_fx_draw(&e_data, vedata, cache_ob); - } - - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_a; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_a; - - /* Combine with scene buffer */ - if ((!is_render) || (fbl->main == NULL)) { - GPU_framebuffer_bind(dfbl->default_fb); - } - else { - GPU_framebuffer_bind(fbl->main); - } - /* tonemapping */ - stl->storage->tonemapping = 1; - - /* active select flag and selection color */ - if (!is_render) { - UI_GetThemeColorShadeAlpha4fv( - (ob == draw_ctx->obact) ? TH_ACTIVE : TH_SELECT, 0, -40, stl->storage->select_color); - } - stl->storage->do_select_outline = ((overlay) && (ob->base_flag & BASE_SELECTED) && - (ob->mode == OB_MODE_OBJECT) && (!is_render) && - (!playing) && (v3d->flag & V3D_SELECT_OUTLINE)); - - /* if active object is not object mode, disable for all objects */ - if ((stl->storage->do_select_outline) && (draw_ctx->obact) && - (draw_ctx->obact->mode != OB_MODE_OBJECT)) { - stl->storage->do_select_outline = 0; - } - - /* draw mix pass */ - DRW_draw_pass(psl->mix_pass); - - /* disable select flag */ - stl->storage->do_select_outline = 0; - - /* prepare for fast drawing */ - if (!is_render) { - if (!playing) { - gpencil_prepare_fast_drawing(stl, dfbl, fbl, psl->mix_pass_noblend, clearcol); - } - } - else { - /* if render, the cache must be dirty for next loop */ - gpd->flag |= GP_DATA_CACHE_IS_DIRTY; - } - } - /* edit points */ - if ((!is_render) && (!playing) && (is_edit)) { - DRW_draw_pass(psl->edit_pass); - } - } - /* grid pass */ - if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { - if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (v3d->gp_flag & V3D_GP_SHOW_GRID)) { - DRW_draw_pass(psl->grid_pass); - } - } + if (pd->scene_fb) { + GPENCIL_antialiasing_draw(vedata); } - /* free memory */ - DRW_gpencil_free_runtime_data(ved); - /* reset */ - if (DRW_state_is_fbo()) { - /* attach again default framebuffer */ - if (!is_render) { - GPU_framebuffer_bind(dfbl->default_fb); - } + pd->gp_object_pool = pd->gp_layer_pool = pd->gp_vfx_pool = pd->gp_maskbit_pool = NULL; - /* the temp texture is ready. Now we can use fast screen drawing */ - if (stl->g_data->session_flag & GP_DRW_PAINT_FILLING) { - stl->g_data->session_flag = GP_DRW_PAINT_READY; - } + /* Free temp stroke buffers. */ + if (pd->sbuffer_gpd) { + DRW_cache_gpencil_sbuffer_clear(pd->obact); } } +static void GPENCIL_engine_free(void) +{ + GPENCIL_shader_free(); +} + static const DrawEngineDataSize GPENCIL_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_Data); DrawEngineType draw_engine_gpencil_type = { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 44a30260343..4b6059f1474 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -23,6 +23,10 @@ #ifndef __GPENCIL_ENGINE_H__ #define __GPENCIL_ENGINE_H__ +#include "DNA_gpencil_types.h" + +#include "BLI_bitmap.h" + #include "GPU_batch.h" extern DrawEngineType draw_engine_gpencil_type; @@ -34,204 +38,201 @@ struct Object; struct RenderEngine; struct RenderLayer; struct bGPDstroke; - +struct View3D; +struct GpencilBatchCache; struct GPUBatch; struct GPUVertBuf; struct GPUVertFormat; -#define GPENCIL_MAX_SHGROUPS 65536 -#define GPENCIL_GROUPS_BLOCK_SIZE 1024 +/* used to convert pixel scale. */ +#define GPENCIL_PIXEL_FACTOR 2000.0f /* used to expand VBOs. Size has a big impact in the speed */ #define GPENCIL_VBO_BLOCK_SIZE 128 -#define GPENCIL_COLOR_SOLID 0 -#define GPENCIL_COLOR_TEXTURE 1 -#define GPENCIL_COLOR_PATTERN 2 - -/* *********** OBJECTS CACHE *********** */ -typedef struct tGPencilObjectCache_shgrp { - /** type of blend (regular, add, mult, etc...) */ - int mode; - /** flag to enable the layer clamping */ - bool mask_layer; - /** factor to define the opacity of the layer */ - float blend_opacity; - DRWShadingGroup *init_shgrp; - DRWShadingGroup *end_shgrp; -} tGPencilObjectCache_shgrp; - -/* used to save gpencil object data for drawing */ -typedef struct tGPencilObjectCache { - struct Object *ob; - struct bGPdata *gpd; - int idx; /*original index, can change after sort */ - char *name; - - /* effects */ - bool has_fx; - ListBase shader_fx; - float pixfactor; - DRWShadingGroup *fx_wave_sh; - DRWShadingGroup *fx_blur_sh; - DRWShadingGroup *fx_colorize_sh; - DRWShadingGroup *fx_pixel_sh; - DRWShadingGroup *fx_rim_sh; - DRWShadingGroup *fx_shadow_sh; - DRWShadingGroup *fx_glow_sh; - DRWShadingGroup *fx_swirl_sh; - DRWShadingGroup *fx_flip_sh; - DRWShadingGroup *fx_light_sh; - - float loc[3]; - float obmat[4][4]; - float zdepth; /* z-depth value to sort gp object */ - bool is_dup_ob; /* flag to tag duplicate objects */ - float scale; - - /* shading type */ - int shading_type[2]; - - /* GPU data size */ - int tot_vertex; - int tot_triangles; - - /* Save shader groups by layer */ - int tot_layers; - tGPencilObjectCache_shgrp *shgrp_array; - -} tGPencilObjectCache; +#define GP_MAX_MASKBITS 256 + +/* UBO structure. Watch out for padding. Must match GLSL declaration. */ +typedef struct gpMaterial { + float stroke_color[4]; + float fill_color[4]; + float fill_mix_color[4]; + float fill_uv_transform[3][2], _pad0[2]; + float stroke_texture_mix; + float stroke_u_scale; + float fill_texture_mix; + int flag; +} gpMaterial; + +/* gpMaterial->flag */ +/* WATCH Keep in sync with GLSL declaration. */ +#define GP_STROKE_ALIGNMENT_STROKE 1 +#define GP_STROKE_ALIGNMENT_OBJECT 2 +#define GP_STROKE_ALIGNMENT_FIXED 3 +#define GP_STROKE_ALIGNMENT 0x3 +#define GP_STROKE_OVERLAP (1 << 2) +#define GP_STROKE_TEXTURE_USE (1 << 3) +#define GP_STROKE_TEXTURE_STENCIL (1 << 4) +#define GP_STROKE_TEXTURE_PREMUL (1 << 5) +#define GP_STROKE_DOTS (1 << 6) +#define GP_FILL_TEXTURE_USE (1 << 10) +#define GP_FILL_TEXTURE_PREMUL (1 << 11) +#define GP_FILL_TEXTURE_CLIP (1 << 12) +#define GP_FILL_GRADIENT_USE (1 << 13) +#define GP_FILL_GRADIENT_RADIAL (1 << 14) + +#define GPENCIL_LIGHT_BUFFER_LEN 128 + +/* UBO structure. Watch out for padding. Must match GLSL declaration. */ +typedef struct gpLight { + float color[3], type; + float right[3], spotsize; + float up[3], spotblend; + float forward[4]; + float position[4]; +} gpLight; + +/* gpLight->type */ +/* WATCH Keep in sync with GLSL declaration. */ +#define GP_LIGHT_TYPE_POINT 0.0 +#define GP_LIGHT_TYPE_SPOT 1.0 +#define GP_LIGHT_TYPE_SUN 2.0 +#define GP_LIGHT_TYPE_AMBIENT 3.0 + +BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16) +BLI_STATIC_ASSERT_ALIGN(gpLight, 16) + +/* *********** Draw Datas *********** */ +typedef struct GPENCIL_MaterialPool { + /* Linklist. */ + struct GPENCIL_MaterialPool *next; + /* GPU representatin of materials. */ + gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN]; + /* Matching ubo. */ + struct GPUUniformBuffer *ubo; + /* Texture per material. NULL means none. */ + struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN]; + struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN]; + /* Number of material used in this pool. */ + int used_count; +} GPENCIL_MaterialPool; + +typedef struct GPENCIL_LightPool { + /* GPU representatin of materials. */ + gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN]; + /* Matching ubo. */ + struct GPUUniformBuffer *ubo; + /* Number of light in the pool. */ + int light_used; +} GPENCIL_LightPool; + +typedef struct GPENCIL_ViewLayerData { + /* GPENCIL_tObject */ + struct BLI_memblock *gp_object_pool; + /* GPENCIL_tLayer */ + struct BLI_memblock *gp_layer_pool; + /* GPENCIL_tVfx */ + struct BLI_memblock *gp_vfx_pool; + /* GPENCIL_MaterialPool */ + struct BLI_memblock *gp_material_pool; + /* GPENCIL_LightPool */ + struct BLI_memblock *gp_light_pool; + /* BLI_bitmap */ + struct BLI_memblock *gp_maskbit_pool; +} GPENCIL_ViewLayerData; + +/* *********** GPencil *********** */ + +typedef struct GPENCIL_tVfx { + /** Linklist */ + struct GPENCIL_tVfx *next; + DRWPass *vfx_ps; + /* Framebuffer reference since it may not be allocated yet. */ + GPUFrameBuffer **target_fb; +} GPENCIL_tVfx; + +typedef struct GPENCIL_tLayer { + /** Linklist */ + struct GPENCIL_tLayer *next; + /** Geometry pass (draw all strokes). */ + DRWPass *geom_ps; + /** Blend pass to composite onto the target buffer (blends modes). NULL if not needed. */ + DRWPass *blend_ps; + /** First shading group created for this layer. Contains all uniforms. */ + DRWShadingGroup *base_shgrp; + /** Layer id of the mask. */ + BLI_bitmap *mask_bits; + BLI_bitmap *mask_invert_bits; + /** Index in the layer list. Used as id for masking. */ + int layer_id; +} GPENCIL_tLayer; + +typedef struct GPENCIL_tObject { + /** Linklist */ + struct GPENCIL_tObject *next; + + struct { + GPENCIL_tLayer *first, *last; + } layers; + + struct { + GPENCIL_tVfx *first, *last; + } vfx; + + /* Distance to camera. Used for sorting. */ + float camera_z; + /* Used for stroke thickness scaling. */ + float object_scale; + /* Normal used for shading. Based on view angle. */ + float plane_normal[3]; + /* Used for drawing depth merge pass. */ + float plane_mat[4][4]; + + bool is_drawmode3d; +} GPENCIL_tObject; /* *********** LISTS *********** */ -typedef struct GPENCIL_shgroup { - int s_clamp; - int stroke_style; - int color_type; - int mode; - int texture_mix; - int texture_flip; - int texture_clamp; - int fill_style; - int keep_size; - int caps_mode[2]; - float obj_scale; - int xray_mode; - int alignment_mode; - - float gradient_f; - float gradient_s[2]; - - float mix_stroke_factor; - - /* color of the wireframe */ - float wire_color[4]; - /* shading type and mode */ - int shading_type[2]; - int is_xray; -} GPENCIL_shgroup; - -typedef struct GPENCIL_Storage { - int shgroup_id; /* total elements */ - int stroke_style; - int color_type; - int mode; - int xray; - int keep_size; - float obj_scale; - float pixfactor; - bool is_playing; - bool is_render; - bool is_mat_preview; - bool is_main_overlay; - bool is_main_onion; - bool background_ready; - int is_xray; - bool is_ontop; - bool reset_cache; - const float *pixsize; - float render_pixsize; - int tonemapping; - int do_select_outline; - float select_color[4]; - short multisamples; - float grid_matrix[4][4]; - - short framebuffer_flag; /* flag what framebuffer need to create */ - - int blend_mode; - int mask_layer; - - /* simplify settings*/ - bool simplify_fill; - bool simplify_modif; - bool simplify_fx; - bool simplify_blend; - - float gradient_f; - float gradient_s[2]; - int alignment_mode; - - float mix_stroke_factor; - - /* Render Matrices and data */ - float view_vecs[2][4]; /* vec4[2] */ - - int shade_render[2]; - - Object *camera; /* camera pointer for render mode */ -} GPENCIL_Storage; - -typedef enum eGpencilFramebuffer_Flag { - GP_FRAMEBUFFER_MULTISAMPLE = (1 << 0), - GP_FRAMEBUFFER_BASIC = (1 << 1), - GP_FRAMEBUFFER_DRAW = (1 << 2), -} eGpencilFramebuffer_Flag; - typedef struct GPENCIL_StorageList { - struct GPENCIL_Storage *storage; - struct g_data *g_data; - struct GPENCIL_shgroup *shgroups; + struct GPENCIL_PrivateData *pd; } GPENCIL_StorageList; typedef struct GPENCIL_PassList { - struct DRWPass *stroke_pass_2d; - struct DRWPass *stroke_pass_3d; - struct DRWPass *edit_pass; - struct DRWPass *drawing_pass; - struct DRWPass *mix_pass; - struct DRWPass *mix_pass_noblend; - struct DRWPass *background_pass; - struct DRWPass *paper_pass; - struct DRWPass *grid_pass; - struct DRWPass *blend_pass; - - /* effects */ - struct DRWPass *fx_shader_pass; - struct DRWPass *fx_shader_pass_blend; - + /* Composite the main GPencil buffer onto the rendered image. */ + struct DRWPass *composite_ps; + /* Composite the object depth to the default depth buffer to occlude overlays. */ + struct DRWPass *merge_depth_ps; + /* Invert mask buffer content. */ + struct DRWPass *mask_invert_ps; + /* Anti-Aliasing. */ + struct DRWPass *smaa_edge_ps; + struct DRWPass *smaa_weight_ps; + struct DRWPass *smaa_resolve_ps; } GPENCIL_PassList; typedef struct GPENCIL_FramebufferList { - struct GPUFrameBuffer *main; - struct GPUFrameBuffer *temp_fb_a; - struct GPUFrameBuffer *temp_fb_b; - struct GPUFrameBuffer *temp_fb_fx; - struct GPUFrameBuffer *background_fb; - - struct GPUFrameBuffer *multisample_fb; + struct GPUFrameBuffer *render_fb; + struct GPUFrameBuffer *gpencil_fb; + struct GPUFrameBuffer *snapshot_fb; + struct GPUFrameBuffer *layer_fb; + struct GPUFrameBuffer *object_fb; + struct GPUFrameBuffer *mask_fb; + struct GPUFrameBuffer *smaa_edge_fb; + struct GPUFrameBuffer *smaa_weight_fb; } GPENCIL_FramebufferList; typedef struct GPENCIL_TextureList { - struct GPUTexture *texture; - - /* multisample textures */ - struct GPUTexture *multisample_color; - struct GPUTexture *multisample_depth; - - /* Background textures for speed-up drawing. */ - struct GPUTexture *background_depth_tx; - struct GPUTexture *background_color_tx; - + /* Dummy texture to avoid errors cause by empty sampler. */ + struct GPUTexture *dummy_texture; + /* Snapshot for smoother drawing. */ + struct GPUTexture *snapshot_depth_tx; + struct GPUTexture *snapshot_color_tx; + struct GPUTexture *snapshot_reveal_tx; + /* Textures used by Antialiasing. */ + struct GPUTexture *smaa_area_tx; + struct GPUTexture *smaa_search_tx; + /* Textures used during render. Containing underlying rendered scene. */ + struct GPUTexture *render_depth_tx; + struct GPUTexture *render_color_tx; } GPENCIL_TextureList; typedef struct GPENCIL_Data { @@ -240,248 +241,175 @@ typedef struct GPENCIL_Data { struct GPENCIL_TextureList *txl; struct GPENCIL_PassList *psl; struct GPENCIL_StorageList *stl; - - /* render textures */ - struct GPUTexture *render_depth_tx; - struct GPUTexture *render_color_tx; - } GPENCIL_Data; /* *********** STATIC *********** */ -typedef struct g_data { - struct DRWShadingGroup *shgrps_edit_point; - struct DRWShadingGroup *shgrps_edit_line; - struct DRWShadingGroup *shgrps_drawing_stroke; - struct DRWShadingGroup *shgrps_drawing_fill; - struct DRWShadingGroup *shgrps_grid; - - int gp_cache_used; /* total objects in cache */ - int gp_cache_size; /* size of the cache */ - struct tGPencilObjectCache *gp_object_cache; - - /* for buffer only one batch is nedeed because the drawing is only of one stroke */ - GPUBatch *batch_buffer_stroke; - GPUBatch *batch_buffer_fill; - GPUBatch *batch_buffer_ctrlpoint; - - /* grid geometry */ - GPUBatch *batch_grid; - - /* runtime pointers texture */ - struct GPUTexture *input_depth_tx; - struct GPUTexture *input_color_tx; - - /* working textures */ - struct GPUTexture *temp_color_tx_a; - struct GPUTexture *temp_depth_tx_a; - - struct GPUTexture *temp_color_tx_b; - struct GPUTexture *temp_depth_tx_b; - - struct GPUTexture *temp_color_tx_fx; - struct GPUTexture *temp_depth_tx_fx; - - int session_flag; - bool do_instances; - -} g_data; /* Transient data */ - -/* flags for fast drawing support */ -typedef enum eGPsession_Flag { - GP_DRW_PAINT_HOLD = (1 << 0), - GP_DRW_PAINT_IDLE = (1 << 1), - GP_DRW_PAINT_FILLING = (1 << 2), - GP_DRW_PAINT_READY = (1 << 3), - GP_DRW_PAINT_PAINTING = (1 << 4), -} eGPsession_Flag; - -typedef struct GPENCIL_e_data { - /* textures */ - struct GPUTexture *gpencil_blank_texture; - - /* general drawing shaders */ - struct GPUShader *gpencil_fill_sh; - struct GPUShader *gpencil_stroke_sh; - struct GPUShader *gpencil_point_sh; - struct GPUShader *gpencil_edit_point_sh; - struct GPUShader *gpencil_line_sh; - struct GPUShader *gpencil_drawing_fill_sh; - struct GPUShader *gpencil_fullscreen_sh; - struct GPUShader *gpencil_simple_fullscreen_sh; - struct GPUShader *gpencil_blend_fullscreen_sh; - struct GPUShader *gpencil_background_sh; - struct GPUShader *gpencil_paper_sh; - - /* effects */ - struct GPUShader *gpencil_fx_blur_sh; - struct GPUShader *gpencil_fx_colorize_sh; - struct GPUShader *gpencil_fx_flip_sh; - struct GPUShader *gpencil_fx_glow_prepare_sh; - struct GPUShader *gpencil_fx_glow_resolve_sh; - struct GPUShader *gpencil_fx_light_sh; - struct GPUShader *gpencil_fx_pixel_sh; - struct GPUShader *gpencil_fx_rim_prepare_sh; - struct GPUShader *gpencil_fx_rim_resolve_sh; - struct GPUShader *gpencil_fx_shadow_prepare_sh; - struct GPUShader *gpencil_fx_shadow_resolve_sh; - struct GPUShader *gpencil_fx_swirl_sh; - struct GPUShader *gpencil_fx_wave_sh; - -} GPENCIL_e_data; /* Engine data */ - -/* GPUBatch Cache Element */ -typedef struct GpencilBatchCacheElem { - GPUBatch *batch; - GPUVertBuf *vbo; - int vbo_len; - /* attr ids */ - GPUVertFormat *format; - uint pos_id; - uint color_id; - uint thickness_id; - uint uvdata_id; - uint prev_pos_id; - - /* size for VBO alloc */ - int tot_vertex; -} GpencilBatchCacheElem; - -/* Defines each batch group to define later the shgroup */ -typedef struct GpencilBatchGroup { - struct bGPDlayer *gpl; /* reference to original layer */ - struct bGPDframe *gpf; /* reference to original frame */ - struct bGPDstroke *gps; /* reference to original stroke */ - short type; /* type of element */ - bool onion; /* the group is part of onion skin */ - int vertex_idx; /* index of vertex data */ -} GpencilBatchGroup; - -typedef enum GpencilBatchGroup_Type { - eGpencilBatchGroupType_Stroke = 1, - eGpencilBatchGroupType_Point = 2, - eGpencilBatchGroupType_Fill = 3, - eGpencilBatchGroupType_Edit = 4, - eGpencilBatchGroupType_Edlin = 5, -} GpencilBatchGroup_Type; - -/* Runtime data for GPU and evaluated frames after applying modifiers */ -typedef struct GpencilBatchCache { - GpencilBatchCacheElem b_stroke; - GpencilBatchCacheElem b_point; - GpencilBatchCacheElem b_fill; - GpencilBatchCacheElem b_edit; - GpencilBatchCacheElem b_edlin; - - /** Cache is dirty */ - bool is_dirty; - /** Edit mode flag */ - bool is_editmode; - /** Last cache frame */ - int cache_frame; - - /** Total groups in arrays */ - int grp_used; - /** Max size of the array */ - int grp_size; - /** Array of cache elements */ - struct GpencilBatchGroup *grp_cache; -} GpencilBatchCache; - -/* general drawing functions */ -struct DRWShadingGroup *gpencil_shgroup_stroke_create(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct DRWPass *pass, - struct GPUShader *shader, - struct Object *ob, - float (*obmat)[4], - struct bGPdata *gpd, - struct bGPDlayer *gpl, - struct bGPDstroke *gps, - struct MaterialGPencilStyle *gp_style, - int id, - bool onion, - const float scale, - const int shading_type[2]); -void gpencil_populate_datablock(struct GPENCIL_e_data *e_data, - void *vedata, - struct Object *ob, - struct tGPencilObjectCache *cache_ob); -void gpencil_populate_buffer_strokes(struct GPENCIL_e_data *e_data, - void *vedata, - struct ToolSettings *ts, - struct Object *ob); -void gpencil_populate_multiedit(struct GPENCIL_e_data *e_data, - void *vedata, - struct Object *ob, - struct tGPencilObjectCache *cache_ob); -void gpencil_populate_particles(struct GPENCIL_e_data *e_data, - struct GHash *gh_objects, - void *vedata); - -void gpencil_multisample_ensure(struct GPENCIL_Data *vedata, int rect_w, int rect_h); - -/* create geometry functions */ -void gpencil_get_point_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - short thickness, - const float ink[4], - const int follow_mode); -void gpencil_get_stroke_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - short thickness, - const float ink[4]); -void gpencil_get_fill_geom(struct GpencilBatchCacheElem *be, - struct Object *ob, - struct bGPDstroke *gps, - const float color[4]); -void gpencil_get_edit_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - float alpha, - short dflag); -void gpencil_get_edlin_geom(struct GpencilBatchCacheElem *be, - struct bGPDstroke *gps, - float alpha, - const bool hide_select); - -struct GPUBatch *gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, short thickness); -struct GPUBatch *gpencil_get_buffer_fill_geom(struct bGPdata *gpd); -struct GPUBatch *gpencil_get_buffer_point_geom(struct bGPdata *gpd, short thickness); -struct GPUBatch *gpencil_get_buffer_ctrlpoint_geom(struct bGPdata *gpd); -struct GPUBatch *gpencil_get_grid(Object *ob); - -/* object cache functions */ -struct tGPencilObjectCache *gpencil_object_cache_add(struct tGPencilObjectCache *cache_array, - struct Object *ob, - int *gp_cache_size, - int *gp_cache_used); - -bool gpencil_onion_active(struct bGPdata *gpd); - -/* shading groups cache functions */ -struct GpencilBatchGroup *gpencil_group_cache_add(struct GpencilBatchGroup *cache_array, - struct bGPDlayer *gpl, - struct bGPDframe *gpf, - struct bGPDstroke *gps, - const short type, - const bool onion, - const int vertex_idx, - int *grp_size, - int *grp_used); +typedef struct GPENCIL_PrivateData { + /* Pointers copied from GPENCIL_ViewLayerData. */ + struct BLI_memblock *gp_object_pool; + struct BLI_memblock *gp_layer_pool; + struct BLI_memblock *gp_vfx_pool; + struct BLI_memblock *gp_material_pool; + struct BLI_memblock *gp_light_pool; + struct BLI_memblock *gp_maskbit_pool; + /* Last used material pool. */ + GPENCIL_MaterialPool *last_material_pool; + /* Last used light pool. */ + GPENCIL_LightPool *last_light_pool; + /* Common lightpool containing all lights in the scene. */ + GPENCIL_LightPool *global_light_pool; + /* Common lightpool containing one ambient white light. */ + GPENCIL_LightPool *shadeless_light_pool; + /* Linked list of tObjects. */ + struct { + GPENCIL_tObject *first, *last; + } tobjects, tobjects_infront; + /* Temp Textures (shared with other engines). */ + GPUTexture *depth_tx; + GPUTexture *color_tx; + GPUTexture *color_layer_tx; + GPUTexture *color_object_tx; + /* Revealage is 1 - alpha */ + GPUTexture *reveal_tx; + GPUTexture *reveal_layer_tx; + GPUTexture *reveal_object_tx; + /* Mask texture */ + GPUTexture *mask_tx; + /* Anti-Aliasing. */ + GPUTexture *smaa_edge_tx; + GPUTexture *smaa_weight_tx; + /* Pointer to dtxl->depth */ + GPUTexture *scene_depth_tx; + GPUFrameBuffer *scene_fb; + /* Copy of txl->dummy_tx */ + GPUTexture *dummy_tx; + /* Copy of v3d->shading.single_color. */ + float v3d_single_color[3]; + /* Copy of v3d->shading.color_type or -1 to ignore. */ + int v3d_color_type; + /* Current frame */ + int cfra; + /* If we are rendering for final render (F12). */ + bool is_render; + /* If we are in viewport display (used for VFX). */ + bool is_viewport; + /* True in selection and auto_depth drawing */ + bool draw_depth_only; + /* Is shading set to wireframe. */ + bool draw_wireframe; + /* Used by the depth merge step. */ + int is_stroke_order_3d; + float object_bound_mat[4][4]; + /* Used for computing object distance to camera. */ + float camera_z_axis[3], camera_z_offset; + float camera_pos[3]; + /* Pseudo depth of field parameter. Used to scale blur radius. */ + float dof_params[2]; + /* Used for DoF Setup. */ + Object *camera; + /* Copy of draw_ctx->scene for convenience. */ + struct Scene *scene; + + /* Active object. */ + Object *obact; + /* Object being in draw mode. */ + struct bGPdata *sbuffer_gpd; + /* Layer to append the temp stroke to. */ + struct bGPDlayer *sbuffer_layer; + /* Temporary stroke currently being drawn. */ + struct bGPDstroke *sbuffer_stroke; + /* List of temp objects containing the stroke. */ + struct { + GPENCIL_tObject *first, *last; + } sbuffer_tobjects; + /* Batches containing the temp stroke. */ + GPUBatch *stroke_batch; + GPUBatch *fill_batch; + bool do_fast_drawing; + bool snapshot_buffer_dirty; + + /* Display onion skinning */ + bool do_onion; + /* simplify settings */ + bool simplify_fill; + bool simplify_fx; + bool simplify_antialias; + /* Use scene lighting or flat shading (global setting). */ + bool use_lighting; + /* Use physical lights or just ambient lighting. */ + bool use_lights; + /* Do we need additional framebuffers? */ + bool use_layer_fb; + bool use_object_fb; + bool use_mask_fb; + /* Some blend mode needs to add negative values. + * This is only supported if target texture is signed. */ + bool use_signed_fb; + /* Use only lines for multiedit and not active frame. */ + bool use_multiedit_lines_only; + /* Layer opacity for fading. */ + float fade_layer_opacity; + /* Opacity for fading gpencil objects. */ + float fade_gp_object_opacity; + /* Opacity for fading 3D objects. */ + float fade_3d_object_opacity; + /* Mask opacity uniform. */ + float mask_opacity; + /* Xray transparency in solid mode. */ + float xray_alpha; + /* Mask invert uniform. */ + int mask_invert; +} GPENCIL_PrivateData; /* geometry batch cache functions */ struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra); -/* effects */ -void GPENCIL_create_fx_shaders(struct GPENCIL_e_data *e_data); -void GPENCIL_delete_fx_shaders(struct GPENCIL_e_data *e_data); -void GPENCIL_create_fx_passes(struct GPENCIL_PassList *psl); +GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob); +void gpencil_object_cache_sort(GPENCIL_PrivateData *pd); -void gpencil_fx_prepare(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct tGPencilObjectCache *cache_ob); -void gpencil_fx_draw(struct GPENCIL_e_data *e_data, - struct GPENCIL_Data *vedata, - struct tGPencilObjectCache *cache_ob); +GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, + const Object *ob, + const bGPDlayer *gpl, + const bGPDframe *gpf, + GPENCIL_tObject *tgp_ob); +GPENCIL_tLayer *gpencil_layer_cache_get(GPENCIL_tObject *tgp_ob, int number); + +GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Object *ob, int *ofs); +void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, + int mat_id, + struct GPUTexture **r_tex_stroke, + struct GPUTexture **r_tex_fill, + struct GPUUniformBuffer **r_ubo_mat); + +void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]); +void gpencil_light_pool_populate(GPENCIL_LightPool *matpool, Object *ob); +GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd); +GPENCIL_LightPool *gpencil_light_pool_create(GPENCIL_PrivateData *pd, Object *ob); + +/* effects */ +void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob); + +/* Shaders */ +struct GPUShader *GPENCIL_shader_antialiasing(int stage); +struct GPUShader *GPENCIL_shader_geometry_get(void); +struct GPUShader *GPENCIL_shader_composite_get(void); +struct GPUShader *GPENCIL_shader_layer_blend_get(void); +struct GPUShader *GPENCIL_shader_mask_invert_get(void); +struct GPUShader *GPENCIL_shader_depth_merge_get(void); +struct GPUShader *GPENCIL_shader_fx_blur_get(void); +struct GPUShader *GPENCIL_shader_fx_colorize_get(void); +struct GPUShader *GPENCIL_shader_fx_composite_get(void); +struct GPUShader *GPENCIL_shader_fx_transform_get(void); +struct GPUShader *GPENCIL_shader_fx_glow_get(void); +struct GPUShader *GPENCIL_shader_fx_pixelize_get(void); +struct GPUShader *GPENCIL_shader_fx_rim_get(void); +struct GPUShader *GPENCIL_shader_fx_shadow_get(void); + +void GPENCIL_shader_free(void); + +/* Antialiasing */ +void GPENCIL_antialiasing_init(struct GPENCIL_Data *vedata); +void GPENCIL_antialiasing_draw(struct GPENCIL_Data *vedata); /* main functions */ void GPENCIL_engine_init(void *vedata); @@ -493,43 +421,17 @@ void GPENCIL_draw_scene(void *vedata); /* render */ void GPENCIL_render_init(struct GPENCIL_Data *ved, struct RenderEngine *engine, - struct Depsgraph *depsgraph); + struct RenderLayer *render_layer, + const struct Depsgraph *depsgraph, + const rcti *rect); void GPENCIL_render_to_image(void *vedata, struct RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect); -/* TODO: GPXX workaround function to call free memory from draw manager while draw manager support - * scene finish callback. */ -void DRW_gpencil_free_runtime_data(void *ved); - -/* Use of multisample framebuffers. */ -#define MULTISAMPLE_GP_SYNC_ENABLE(lvl, fbl) \ - { \ - if ((lvl > 0) && (fbl->multisample_fb != NULL) && (DRW_state_is_fbo())) { \ - DRW_stats_query_start("GP Multisample Blit"); \ - GPU_framebuffer_bind(fbl->multisample_fb); \ - GPU_framebuffer_clear_color_depth_stencil( \ - fbl->multisample_fb, (const float[4]){0.0f}, 1.0f, 0x0); \ - DRW_stats_query_end(); \ - } \ - } \ - ((void)0) - -#define MULTISAMPLE_GP_SYNC_DISABLE(lvl, fbl, fb, txl) \ - { \ - if ((lvl > 0) && (fbl->multisample_fb != NULL) && (DRW_state_is_fbo())) { \ - DRW_stats_query_start("GP Multisample Resolve"); \ - GPU_framebuffer_bind(fb); \ - DRW_stats_query_end(); \ - } \ - } \ - ((void)0) - -#define GPENCIL_3D_DRAWMODE(ob, gpd) \ - ((gpd) && (gpd->draw_mode == GP_DRAWMODE_3D) && ((ob->dtx & OB_DRAWXRAY) == 0)) - -#define GPENCIL_USE_SOLID(stl) \ - ((stl) && ((stl->storage->is_render) || (stl->storage->is_mat_preview))) +/* Draw Data. */ +void gpencil_light_pool_free(void *storage); +void gpencil_material_pool_free(void *storage); +GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void); #endif /* __GPENCIL_ENGINE_H__ */ diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 8c126310ea2..083b04c0600 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -33,66 +33,22 @@ #include "gpencil_engine.h" -/* Get pixel size for render - * This function uses the same calculation used for viewport, because if use - * camera pixelsize, the result is not correct. - */ -static float get_render_pixelsize(float persmat[4][4], int winx, int winy) -{ - float v1[3], v2[3]; - float len_px, len_sc; - - v1[0] = persmat[0][0]; - v1[1] = persmat[1][0]; - v1[2] = persmat[2][0]; - - v2[0] = persmat[0][1]; - v2[1] = persmat[1][1]; - v2[2] = persmat[2][1]; - - len_px = 2.0f / sqrtf(min_ff(len_squared_v3(v1), len_squared_v3(v2))); - len_sc = (float)MAX2(winx, winy); - - return len_px / len_sc; -} - /* init render data */ -void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgraph *depsgraph) +void GPENCIL_render_init(GPENCIL_Data *vedata, + RenderEngine *engine, + struct RenderLayer *render_layer, + const Depsgraph *depsgraph, + const rcti *rect) { - GPENCIL_Data *vedata = (GPENCIL_Data *)ved; - GPENCIL_StorageList *stl = vedata->stl; GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_TextureList *txl = vedata->txl; Scene *scene = DEG_get_evaluated_scene(depsgraph); const float *viewport_size = DRW_viewport_size_get(); const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - /* In render mode the default framebuffer is not generated - * because there is no viewport. So we need to manually create one - * NOTE : use 32 bit format for precision in render mode. - */ - /* create multisample framebuffer for AA */ - if (U.gpencil_multisamples > 0) { - int rect_w = (int)viewport_size[0]; - int rect_h = (int)viewport_size[1]; - gpencil_multisample_ensure(vedata, rect_w, rect_h); - } - - vedata->render_depth_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH_COMPONENT24, &draw_engine_gpencil_type); - vedata->render_color_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_RGBA32F, &draw_engine_gpencil_type); - GPU_framebuffer_ensure_config(&fbl->main, - {GPU_ATTACHMENT_TEXTURE(vedata->render_depth_tx), - GPU_ATTACHMENT_TEXTURE(vedata->render_color_tx)}); - - /* Alloc transient data. */ - if (!stl->g_data) { - stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); - } - /* Set the pers & view matrix. */ - float winmat[4][4], viewmat[4][4], viewinv[4][4], persmat[4][4]; + float winmat[4][4], viewmat[4][4], viewinv[4][4]; struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); float frame = BKE_scene_frame_get(scene); @@ -105,85 +61,101 @@ void GPENCIL_render_init(GPENCIL_Data *ved, RenderEngine *engine, struct Depsgra DRW_view_default_set(view); DRW_view_set_active(view); - DRW_view_persmat_get(NULL, persmat, false); + /* Create depth texture & color texture from render result. */ + const char *viewname = RE_GetActiveRenderView(engine->re); + RenderPass *rpass_z_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); + RenderPass *rpass_col_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - /* calculate pixel size for render */ - stl->storage->render_pixsize = get_render_pixelsize(persmat, viewport_size[0], viewport_size[1]); + float *pix_z = (rpass_z_src) ? rpass_z_src->rect : NULL; + float *pix_col = (rpass_col_src) ? rpass_col_src->rect : NULL; - /* INIT CACHE */ - GPENCIL_cache_init(vedata); + if (!pix_z || !pix_col) { + RE_engine_set_error_message(engine, + "Warning: To render grease pencil, enable Combined and Z passes."); + } + + if (pix_z) { + /* Depth need to be remapped to [0..1] range. */ + pix_z = MEM_dupallocN(pix_z); + + int pix_ct = rpass_z_src->rectx * rpass_z_src->recty; + + if (DRW_view_is_persp_get(view)) { + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (-winmat[3][2] / -pix_z[i]) - winmat[2][2]; + pix_z[i] = clamp_f(pix_z[i] * 0.5f + 0.5f, 0.0f, 1.0f); + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(view); + float far = DRW_view_far_distance_get(view); + float range_inv = 1.0f / fabsf(far - near); + for (int i = 0; i < pix_ct; i++) { + pix_z[i] = (pix_z[i] + near) * range_inv; + pix_z[i] = clamp_f(pix_z[i], 0.0f, 1.0f); + } + } + } + + const bool do_region = (scene->r.mode & R_BORDER) != 0; + const bool do_clear_z = !pix_z || do_region; + const bool do_clear_col = !pix_col || do_region; + + /* FIXME(fclem): we have a precision loss in the depth buffer because of this reupload. + * Find where it comes from! */ + txl->render_depth_tx = DRW_texture_create_2d( + size[0], size[1], GPU_DEPTH_COMPONENT24, 0, do_region ? NULL : pix_z); + txl->render_color_tx = DRW_texture_create_2d( + size[0], size[1], GPU_RGBA16F, 0, do_region ? NULL : pix_col); + + GPU_framebuffer_ensure_config(&fbl->render_fb, + { + GPU_ATTACHMENT_TEXTURE(txl->render_depth_tx), + GPU_ATTACHMENT_TEXTURE(txl->render_color_tx), + }); + + if (do_clear_z || do_clear_col) { + /* To avoid unpredictable result, clear buffers that have not be initialized. */ + GPU_framebuffer_bind(fbl->render_fb); + if (do_clear_col) { + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_clear_color(fbl->render_fb, clear_col); + } + if (do_clear_z) { + GPU_framebuffer_clear_depth(fbl->render_fb, 1.0f); + } + } + + if (do_region) { + int x = rect->xmin; + int y = rect->ymin; + int w = BLI_rcti_size_x(rect); + int h = BLI_rcti_size_y(rect); + if (pix_col) { + GPU_texture_update_sub(txl->render_color_tx, GPU_DATA_FLOAT, pix_col, x, y, 0, w, h, 0); + } + if (pix_z) { + GPU_texture_update_sub(txl->render_depth_tx, GPU_DATA_FLOAT, pix_z, x, y, 0, w, h, 0); + } + } + + MEM_SAFE_FREE(pix_z); } /* render all objects and select only grease pencil */ static void GPENCIL_render_cache(void *vedata, struct Object *ob, struct RenderEngine *UNUSED(engine), - struct Depsgraph *UNUSED(depsgraph)) + Depsgraph *UNUSED(depsgraph)) { - if (ob && ob->type == OB_GPENCIL) { + if (ob && ELEM(ob->type, OB_GPENCIL, OB_LAMP)) { if (DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF) { GPENCIL_cache_populate(vedata, ob); } } } -/* TODO: Reuse Eevee code in shared module instead to duplicate here */ -static void GPENCIL_render_update_viewvecs(float invproj[4][4], - const float winmat[4][4], - float (*r_viewvecs)[4]) -{ - /* view vectors for the corners of the view frustum. - * Can be used to recreate the world space position easily */ - float view_vecs[4][4] = { - {-1.0f, -1.0f, -1.0f, 1.0f}, - {1.0f, -1.0f, -1.0f, 1.0f}, - {-1.0f, 1.0f, -1.0f, 1.0f}, - {-1.0f, -1.0f, 1.0f, 1.0f}, - }; - - /* convert the view vectors to view space */ - const bool is_persp = (winmat[3][3] == 0.0f); - for (int i = 0; i < 4; i++) { - mul_project_m4_v3(invproj, view_vecs[i]); - /* normalized trick see: - * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */ - if (is_persp) { - /* Divide XY by Z. */ - mul_v2_fl(view_vecs[i], 1.0f / view_vecs[i][2]); - } - } - - /** - * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and - * view_vecs[1] is the vector going from the near-bottom-left corner to - * the far-top-right corner. - * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner - * when Z = 1, and top-left corner if Z = 1. - * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) - * distance from the near plane to the far clip plane. - */ - copy_v4_v4(r_viewvecs[0], view_vecs[0]); - - /* we need to store the differences */ - r_viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0]; - r_viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1]; - r_viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2]; -} - -/* Update view_vecs */ -static void GPENCIL_render_update_vecs(GPENCIL_Data *vedata) -{ - GPENCIL_StorageList *stl = vedata->stl; - - float invproj[4][4], winmat[4][4]; - DRW_view_winmat_get(NULL, winmat, false); - DRW_view_winmat_get(NULL, invproj, true); - - /* this is separated to keep function equal to Eevee for future reuse of same code */ - GPENCIL_render_update_viewvecs(invproj, winmat, stl->storage->view_vecs); -} - -/* read z-depth render result */ static void GPENCIL_render_result_z(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, @@ -191,45 +163,52 @@ static void GPENCIL_render_result_z(struct RenderLayer *rl, { const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; - GPENCIL_StorageList *stl = vedata->stl; if ((view_layer->passflag & SCE_PASS_Z) != 0) { RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname); - GPU_framebuffer_read_depth(vedata->fbl->main, + GPU_framebuffer_read_depth(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect); - bool is_persp = DRW_view_is_persp_get(NULL); - - GPENCIL_render_update_vecs(vedata); - float winmat[4][4]; DRW_view_winmat_get(NULL, winmat, false); + int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + /* Convert ogl depth [0..1] to view Z [near..far] */ - for (int i = 0; i < BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); i++) { - if (rp->rect[i] == 1.0f) { - rp->rect[i] = 1e10f; /* Background */ - } - else { - if (is_persp) { + if (DRW_view_is_persp_get(NULL)) { + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; rp->rect[i] = winmat[3][2] / (rp->rect[i] + winmat[2][2]); } + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); + float range = fabsf(far - near); + + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } else { - rp->rect[i] = -stl->storage->view_vecs[0][2] + - rp->rect[i] * -stl->storage->view_vecs[1][2]; + rp->rect[i] = -rp->rect[i] * range + near; } } } } } -/* read combined render result */ static void GPENCIL_render_result_combined(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, @@ -238,8 +217,8 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, viewname); GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - GPU_framebuffer_bind(fbl->main); - GPU_framebuffer_read_color(vedata->fbl->main, + GPU_framebuffer_bind(fbl->render_fb); + GPU_framebuffer_read_color(vedata->fbl->render_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -249,135 +228,31 @@ static void GPENCIL_render_result_combined(struct RenderLayer *rl, rp->rect); } -/* helper to blend pixels */ -static void blend_pixel(float top_color[4], float bottom_color[4], float dst_color[4]) -{ - float alpha = top_color[3]; - - /* use blend: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA */ - dst_color[0] = (top_color[0] * alpha) + (bottom_color[0] * (1.0f - alpha)); - dst_color[1] = (top_color[1] * alpha) + (bottom_color[1] * (1.0f - alpha)); - dst_color[2] = (top_color[2] * alpha) + (bottom_color[2] * (1.0f - alpha)); -} - -/* render grease pencil to image */ -void GPENCIL_render_to_image(void *vedata, +void GPENCIL_render_to_image(void *ved, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect) { + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; const char *viewname = RE_GetActiveRenderView(engine->re); const DRWContextState *draw_ctx = DRW_context_state_get(); - int imgsize = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); - - /* save previous render data */ - RenderPass *rpass_color_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - RenderPass *rpass_depth_src = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); - float *src_rect_color_data = NULL; - float *src_rect_depth_data = NULL; - if ((rpass_color_src) && (rpass_depth_src) && (rpass_color_src->rect) && - (rpass_depth_src->rect)) { - src_rect_color_data = MEM_dupallocN(rpass_color_src->rect); - src_rect_depth_data = MEM_dupallocN(rpass_depth_src->rect); - } - else { - /* TODO: put this message in a better place */ - printf("Warning: To render grease pencil, enable Combined and Z passes.\n"); - } + Depsgraph *depsgraph = draw_ctx->depsgraph; + GPENCIL_render_init(vedata, engine, render_layer, depsgraph, rect); GPENCIL_engine_init(vedata); - GPENCIL_render_init(vedata, engine, draw_ctx->depsgraph); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - Object *camera = DEG_get_evaluated_object(draw_ctx->depsgraph, RE_GetCamera(engine->re)); - stl->storage->camera = camera; /* save current camera */ - - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - if (fbl->main) { - GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx, 0, 0); - GPU_framebuffer_texture_attach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx, 0, 0); - /* clean first time the buffer */ - float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_bind(fbl->main); - GPU_framebuffer_clear_color_depth(fbl->main, clearcol, 1.0f); - } - - /* loop all objects and draw */ - DRW_render_object_iter(vedata, engine, draw_ctx->depsgraph, GPENCIL_render_cache); + vedata->stl->pd->camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); + /* Loop over all objects and create draw structure. */ + GPENCIL_cache_init(vedata); + DRW_render_object_iter(vedata, engine, depsgraph, GPENCIL_render_cache); GPENCIL_cache_finish(vedata); DRW_render_instance_buffer_finish(); + /* Render the gpencil object and merge the result to the underlying render. */ GPENCIL_draw_scene(vedata); - /* combined data */ GPENCIL_render_result_combined(render_layer, viewname, vedata, rect); - /* z-depth data */ GPENCIL_render_result_z(render_layer, viewname, vedata, rect); - - /* detach textures */ - if (fbl->main) { - GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_depth_tx); - GPU_framebuffer_texture_detach(fbl->main, ((GPENCIL_Data *)vedata)->render_color_tx); - } - - /* merge previous render image with new GP image */ - if (src_rect_color_data) { - RenderPass *rpass_color_gp = RE_pass_find_by_name( - render_layer, RE_PASSNAME_COMBINED, viewname); - RenderPass *rpass_depth_gp = RE_pass_find_by_name(render_layer, RE_PASSNAME_Z, viewname); - float *gp_rect_color_data = rpass_color_gp->rect; - float *gp_rect_depth_data = rpass_depth_gp->rect; - float *gp_pixel_rgba; - float *gp_pixel_depth; - float *src_pixel_rgba; - float *src_pixel_depth; - - for (int i = 0; i < imgsize; i++) { - gp_pixel_rgba = &gp_rect_color_data[i * 4]; - gp_pixel_depth = &gp_rect_depth_data[i]; - - src_pixel_rgba = &src_rect_color_data[i * 4]; - src_pixel_depth = &src_rect_depth_data[i]; - - /* check grease pencil render transparency */ - if (gp_pixel_rgba[3] > 0.0f) { - if (src_pixel_rgba[3] > 0.0f) { - /* check z-depth */ - if (gp_pixel_depth[0] > src_pixel_depth[0]) { - /* copy source z-depth */ - gp_pixel_depth[0] = src_pixel_depth[0]; - /* blend object on top */ - if (src_pixel_rgba[3] < 1.0f) { - blend_pixel(src_pixel_rgba, gp_pixel_rgba, gp_pixel_rgba); - } - else { - copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); - } - } - else { - /* blend gp render */ - if (gp_pixel_rgba[3] < 1.0f) { - /* premult alpha factor to remove double blend effects */ - mul_v3_fl(gp_pixel_rgba, 1.0f / gp_pixel_rgba[3]); - - blend_pixel(gp_pixel_rgba, src_pixel_rgba, gp_pixel_rgba); - - gp_pixel_rgba[3] = gp_pixel_rgba[3] > src_pixel_rgba[3] ? gp_pixel_rgba[3] : - src_pixel_rgba[3]; - } - } - } - } - else { - copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); - gp_pixel_depth[0] = src_pixel_depth[0]; - } - } - - /* free memory */ - MEM_SAFE_FREE(src_rect_color_data); - MEM_SAFE_FREE(src_rect_depth_data); - } } diff --git a/source/blender/draw/engines/gpencil/gpencil_shader.c b/source/blender/draw/engines/gpencil/gpencil_shader.c new file mode 100644 index 00000000000..8c7ba42a70e --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_shader.c @@ -0,0 +1,311 @@ +/* + * 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. + * + * Copyright 2019, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ +#include "DRW_render.h" + +#include "gpencil_engine.h" + +extern char datatoc_gpencil_common_lib_glsl[]; +extern char datatoc_gpencil_frag_glsl[]; +extern char datatoc_gpencil_vert_glsl[]; +extern char datatoc_gpencil_antialiasing_frag_glsl[]; +extern char datatoc_gpencil_antialiasing_vert_glsl[]; +extern char datatoc_gpencil_layer_blend_frag_glsl[]; +extern char datatoc_gpencil_mask_invert_frag_glsl[]; +extern char datatoc_gpencil_depth_merge_frag_glsl[]; +extern char datatoc_gpencil_depth_merge_vert_glsl[]; +extern char datatoc_gpencil_vfx_frag_glsl[]; + +extern char datatoc_common_colormanagement_lib_glsl[]; +extern char datatoc_common_fullscreen_vert_glsl[]; +extern char datatoc_common_smaa_lib_glsl[]; +extern char datatoc_common_view_lib_glsl[]; + +static struct { + /* SMAA antialiasing */ + GPUShader *antialiasing_sh[3]; + /* GPencil Object rendering */ + GPUShader *gpencil_sh; + /* Final Compositing over rendered background. */ + GPUShader *composite_sh; + /* All layer blend types in one shader! */ + GPUShader *layer_blend_sh; + /* Merge the final object depth to the depth buffer. */ + GPUShader *depth_merge_sh; + /* Invert the content of the mask buffer. */ + GPUShader *mask_invert_sh; + /* Effects. */ + GPUShader *fx_composite_sh; + GPUShader *fx_colorize_sh; + GPUShader *fx_blur_sh; + GPUShader *fx_glow_sh; + GPUShader *fx_pixel_sh; + GPUShader *fx_rim_sh; + GPUShader *fx_shadow_sh; + GPUShader *fx_transform_sh; + /* general drawing shaders */ + GPUShader *gpencil_fill_sh; + GPUShader *gpencil_stroke_sh; + GPUShader *gpencil_point_sh; + GPUShader *gpencil_edit_point_sh; + GPUShader *gpencil_line_sh; + GPUShader *gpencil_drawing_fill_sh; + GPUShader *gpencil_fullscreen_sh; + GPUShader *gpencil_simple_fullscreen_sh; + GPUShader *gpencil_blend_fullscreen_sh; + GPUShader *gpencil_background_sh; + GPUShader *gpencil_paper_sh; +} g_shaders = {{NULL}}; + +void GPENCIL_shader_free(void) +{ + GPUShader **sh_data_as_array = (GPUShader **)&g_shaders; + for (int i = 0; i < (sizeof(g_shaders) / sizeof(GPUShader *)); i++) { + DRW_SHADER_FREE_SAFE(sh_data_as_array[i]); + } +} + +GPUShader *GPENCIL_shader_antialiasing(int stage) +{ + BLI_assert(stage < 3); + + if (!g_shaders.antialiasing_sh[stage]) { + char stage_define[32]; + BLI_snprintf(stage_define, sizeof(stage_define), "#define SMAA_STAGE %d\n", stage); + + g_shaders.antialiasing_sh[stage] = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + "#define SMAA_INCLUDE_VS 1\n", + "#define SMAA_INCLUDE_PS 0\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_gpencil_antialiasing_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + "#define SMAA_INCLUDE_VS 0\n", + "#define SMAA_INCLUDE_PS 1\n", + "uniform vec4 viewportMetrics;\n", + datatoc_common_smaa_lib_glsl, + datatoc_gpencil_antialiasing_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define SMAA_GLSL_3\n", + "#define SMAA_RT_METRICS viewportMetrics\n", + "#define SMAA_PRESET_HIGH\n", + "#define SMAA_LUMA_WEIGHT float4(1.0, 1.0, 1.0, 0.0)\n", + "#define SMAA_NO_DISCARD\n", + stage_define, + NULL, + }, + }); + } + return g_shaders.antialiasing_sh[stage]; +} + +GPUShader *GPENCIL_shader_geometry_get(void) +{ + if (!g_shaders.gpencil_sh) { + g_shaders.gpencil_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_common_colormanagement_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define GP_MATERIAL_BUFFER_LEN " STRINGIFY(GP_MATERIAL_BUFFER_LEN) "\n", + "#define GPENCIL_LIGHT_BUFFER_LEN " STRINGIFY(GPENCIL_LIGHT_BUFFER_LEN) "\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL, + }, + }); + } + return g_shaders.gpencil_sh; +} + +GPUShader *GPENCIL_shader_layer_blend_get(void) +{ + if (!g_shaders.layer_blend_sh) { + g_shaders.layer_blend_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_layer_blend_frag_glsl, + NULL, + }, + }); + } + return g_shaders.layer_blend_sh; +} + +GPUShader *GPENCIL_shader_mask_invert_get(void) +{ + if (!g_shaders.mask_invert_sh) { + g_shaders.mask_invert_sh = DRW_shader_create_fullscreen(datatoc_gpencil_mask_invert_frag_glsl, + NULL); + } + return g_shaders.mask_invert_sh; +} + +GPUShader *GPENCIL_shader_depth_merge_get(void) +{ + if (!g_shaders.depth_merge_sh) { + g_shaders.depth_merge_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_view_lib_glsl, + datatoc_gpencil_depth_merge_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_depth_merge_frag_glsl, + NULL, + }, + }); + } + return g_shaders.depth_merge_sh; +} + +/* ------- FX Shaders --------- */ + +GPUShader *GPENCIL_shader_fx_blur_get(void) +{ + if (!g_shaders.fx_blur_sh) { + g_shaders.fx_blur_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define BLUR\n"); + } + return g_shaders.fx_blur_sh; +} + +GPUShader *GPENCIL_shader_fx_colorize_get(void) +{ + if (!g_shaders.fx_colorize_sh) { + g_shaders.fx_colorize_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define COLORIZE\n"); + } + return g_shaders.fx_colorize_sh; +} + +GPUShader *GPENCIL_shader_fx_composite_get(void) +{ + if (!g_shaders.fx_composite_sh) { + g_shaders.fx_composite_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define COMPOSITE\n"); + } + return g_shaders.fx_composite_sh; +} + +GPUShader *GPENCIL_shader_fx_glow_get(void) +{ + if (!g_shaders.fx_glow_sh) { + g_shaders.fx_glow_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vfx_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define GLOW\n", + NULL, + }, + }); + } + return g_shaders.fx_glow_sh; +} + +GPUShader *GPENCIL_shader_fx_pixelize_get(void) +{ + if (!g_shaders.fx_pixel_sh) { + g_shaders.fx_pixel_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define PIXELIZE\n"); + } + return g_shaders.fx_pixel_sh; +} + +GPUShader *GPENCIL_shader_fx_rim_get(void) +{ + if (!g_shaders.fx_rim_sh) { + g_shaders.fx_rim_sh = GPU_shader_create_from_arrays({ + .vert = + (const char *[]){ + datatoc_common_fullscreen_vert_glsl, + NULL, + }, + .frag = + (const char *[]){ + datatoc_gpencil_common_lib_glsl, + datatoc_gpencil_vfx_frag_glsl, + NULL, + }, + .defs = + (const char *[]){ + "#define RIM\n", + NULL, + }, + }); + } + return g_shaders.fx_rim_sh; +} + +GPUShader *GPENCIL_shader_fx_shadow_get(void) +{ + if (!g_shaders.fx_shadow_sh) { + g_shaders.fx_shadow_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define SHADOW\n"); + } + return g_shaders.fx_shadow_sh; +} + +GPUShader *GPENCIL_shader_fx_transform_get(void) +{ + if (!g_shaders.fx_transform_sh) { + g_shaders.fx_transform_sh = DRW_shader_create_fullscreen(datatoc_gpencil_vfx_frag_glsl, + "#define TRANSFORM\n"); + } + return g_shaders.fx_transform_sh; +} diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c index f6a62e0d472..9467efcd18c 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -26,28 +26,17 @@ #include "BKE_gpencil.h" +#include "BLI_link_utils.h" +#include "BLI_memblock.h" + #include "DRW_render.h" #include "BKE_camera.h" #include "gpencil_engine.h" -extern char datatoc_gpencil_fx_blur_frag_glsl[]; -extern char datatoc_gpencil_fx_colorize_frag_glsl[]; -extern char datatoc_gpencil_fx_flip_frag_glsl[]; -extern char datatoc_gpencil_fx_light_frag_glsl[]; -extern char datatoc_gpencil_fx_pixel_frag_glsl[]; -extern char datatoc_gpencil_fx_rim_prepare_frag_glsl[]; -extern char datatoc_gpencil_fx_rim_resolve_frag_glsl[]; -extern char datatoc_gpencil_fx_shadow_prepare_frag_glsl[]; -extern char datatoc_gpencil_fx_shadow_resolve_frag_glsl[]; -extern char datatoc_gpencil_fx_glow_prepare_frag_glsl[]; -extern char datatoc_gpencil_fx_glow_resolve_frag_glsl[]; -extern char datatoc_gpencil_fx_swirl_frag_glsl[]; -extern char datatoc_gpencil_fx_wave_frag_glsl[]; - /* verify if this fx is active */ -static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_render) +static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_viewport) { if (fx == NULL) { return false; @@ -58,1007 +47,613 @@ static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_render) } bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); - if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (!is_render)) { + if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit) && (is_viewport)) { return false; } - if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) || - ((fx->mode & eShaderFxMode_Render) && (is_render == true))) { + if (((fx->mode & eShaderFxMode_Realtime) && (is_viewport == true)) || + ((fx->mode & eShaderFxMode_Render) && (is_viewport == false))) { return true; } return false; } -/** - * Get normal of draw using one stroke of visible layer - * \param gpd: GP datablock - * \param r_point: Point on plane - * \param r_normal: Normal vector - */ -static bool get_normal_vector(bGPdata *gpd, float r_point[3], float r_normal[3]) +typedef struct gpIterVfxData { + GPENCIL_PrivateData *pd; + GPENCIL_tObject *tgp_ob; + GPUFrameBuffer **target_fb; + GPUFrameBuffer **source_fb; + GPUTexture **target_color_tx; + GPUTexture **source_color_tx; + GPUTexture **target_reveal_tx; + GPUTexture **source_reveal_tx; +} gpIterVfxData; + +static DRWShadingGroup *gpencil_vfx_pass_create(const char *name, + DRWState state, + gpIterVfxData *iter, + GPUShader *sh) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } + DRWPass *pass = DRW_pass_create(name, state); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_texture_ref(grp, "colorBuf", iter->source_color_tx); + DRW_shgroup_uniform_texture_ref(grp, "revealBuf", iter->source_reveal_tx); - /* get frame */ - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - if (gps->totpoints >= 3) { - bGPDspoint *pt = &gps->points[0]; - BKE_gpencil_stroke_normal(gps, r_normal); - /* in some weird situations, the normal cannot be calculated, so try next stroke */ - if ((r_normal[0] != 0.0f) || (r_normal[1] != 0.0f) || (r_normal[2] != 0.0f)) { - copy_v3_v3(r_point, &pt->x); - return true; - } - } - } - } - - return false; -} - -/* helper to get near and far depth of field values */ -static void GPENCIL_dof_nearfar(Object *camera, float coc, float nearfar[2]) -{ - if (camera == NULL) { - return; - } - Camera *cam = camera->data; + GPENCIL_tVfx *tgp_vfx = BLI_memblock_alloc(iter->pd->gp_vfx_pool); + tgp_vfx->target_fb = iter->target_fb; + tgp_vfx->vfx_ps = pass; - float fstop = cam->dof.aperture_fstop; - float focus_dist = BKE_camera_object_dof_distance(camera); - float focal_len = cam->lens; + SWAP(GPUFrameBuffer **, iter->target_fb, iter->source_fb); + SWAP(GPUTexture **, iter->target_color_tx, iter->source_color_tx); + SWAP(GPUTexture **, iter->target_reveal_tx, iter->source_reveal_tx); - const float scale_camera = 0.001f; - /* we want radius here for the aperture number */ - float aperture_scaled = 0.5f * scale_camera * focal_len / fstop; - float focal_len_scaled = scale_camera * focal_len; + BLI_LINKS_APPEND(&iter->tgp_ob->vfx, tgp_vfx); - float hyperfocal = (focal_len_scaled * focal_len_scaled) / (aperture_scaled * coc); - nearfar[0] = (hyperfocal * focus_dist) / (hyperfocal + focal_len); - nearfar[1] = (hyperfocal * focus_dist) / (hyperfocal - focal_len); + return grp; } -/* **************** Shader Effects ***************************** */ - -/* Gaussian Blur FX - * The effect is done using two shading groups because is faster to apply horizontal - * and vertical in different operations. - */ -static void gpencil_fx_blur(ShaderFxData *fx, - int ob_idx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_blur(BlurShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { + if (fx->radius[0] == 0.0f && fx->radius[1] == 0.0f) { return; } - BlurShaderFxData *fxd = (BlurShaderFxData *)fx; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - RegionView3D *rv3d = draw_ctx->rv3d; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - fxd->blur[0] = fxd->radius[0]; - fxd->blur[1] = fxd->radius[1]; - - /* init weight */ - if (fxd->flag & FX_BLUR_DOF_MODE) { - /* viewport and opengl render */ - Object *camera = NULL; - if (rv3d) { - if (rv3d->persp == RV3D_CAMOB) { - camera = v3d->camera; - } - } - else { - camera = stl->storage->camera; - } - - if (camera) { - float nearfar[2]; - GPENCIL_dof_nearfar(camera, fxd->coc, nearfar); - float zdepth = stl->g_data->gp_object_cache[ob_idx].zdepth; - /* the object is on focus area */ - if ((zdepth >= nearfar[0]) && (zdepth <= nearfar[1])) { - fxd->blur[0] = 0; - fxd->blur[1] = 0; - } - else { - float f; - if (zdepth < nearfar[0]) { - f = nearfar[0] - zdepth; - } - else { - f = zdepth - nearfar[1]; - } - fxd->blur[0] = f; - fxd->blur[1] = f; - CLAMP2(&fxd->blur[0], 0, fxd->radius[0]); - } - } - else { - /* if not camera view, the blur is disabled */ - fxd->blur[0] = 0; - fxd->blur[1] = 0; - } - } - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); + DRWShadingGroup *grp; + const float s = sin(fx->rotation); + const float c = cos(fx->rotation); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; -} + float winmat[4][4], persmat[4][4]; + float blur_size[2] = {fx->radius[0], fx->radius[1]}; + DRW_view_persmat_get(NULL, persmat, false); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); -/* Colorize FX */ -static void gpencil_fx_colorize(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) -{ - if (fx == NULL) { - return; + if ((fx->flag & FX_BLUR_DOF_MODE) && iter->pd->camera != NULL) { + /* Compute circle of confusion size. */ + float coc = (iter->pd->dof_params[0] / -w) - iter->pd->dof_params[1]; + copy_v2_fl(blur_size, fabsf(coc)); + } + else { + /* Modify by distance to camera and object scale. */ + DRW_view_winmat_get(NULL, winmat, false); + const float *vp_size = DRW_viewport_size_get(); + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = world_pixel_scale * scale * winmat[1][1] * vp_size[1] / w; + mul_v2_fl(blur_size, distance_factor); + } + + GPUShader *sh = GPENCIL_shader_fx_blur_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + if (blur_size[0] > 0.0f) { + grp = gpencil_vfx_pass_create("Fx Blur H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){blur_size[0] * c, blur_size[0] * s}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + if (blur_size[1] > 0.0f) { + grp = gpencil_vfx_pass_create("Fx Blur V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-blur_size[1] * s, blur_size[1] * c}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_colorize_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_vec4(fx_shgrp, "low_color", &fxd->low_color[0], 1); - DRW_shgroup_uniform_vec4(fx_shgrp, "high_color", &fxd->high_color[0], 1); - DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1); - DRW_shgroup_uniform_float(fx_shgrp, "factor", &fxd->factor, 1); - - fxd->runtime.fx_sh = fx_shgrp; } -/* Flip FX */ -static void gpencil_fx_flip(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +static void gpencil_vfx_colorize(ColorizeShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - FlipShaderFxData *fxd = (FlipShaderFxData *)fx; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - fxd->flipmode = 100; - /* the number works as bit flag */ - if (fxd->flag & FX_FLIP_HORIZONTAL) { - fxd->flipmode += 10; - } - if (fxd->flag & FX_FLIP_VERTICAL) { - fxd->flipmode += 1; - } + DRWShadingGroup *grp; - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_flip_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1); + GPUShader *sh = GPENCIL_shader_fx_colorize_get(); - DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1); - - fxd->runtime.fx_sh = fx_shgrp; + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Colorize", state, iter, sh); + DRW_shgroup_uniform_vec3_copy(grp, "lowColor", fx->low_color); + DRW_shgroup_uniform_vec3_copy(grp, "highColor", fx->high_color); + DRW_shgroup_uniform_float_copy(grp, "factor", fx->factor); + DRW_shgroup_uniform_int_copy(grp, "mode", fx->mode); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Light FX */ -static void gpencil_fx_light(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_flip(FlipShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - LightShaderFxData *fxd = (LightShaderFxData *)fx; - - if (fxd->object == NULL) { - return; - } - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_light_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - - /* location of the light using obj location as origin */ - copy_v3_v3(fxd->loc, fxd->object->obmat[3]); - - /* Calc distance to strokes plane - * The w component of location is used to transfer the distance to drawing plane - */ - float r_point[3], r_normal[3]; - float r_plane[4]; - bGPdata *gpd = cache->gpd; - if (!get_normal_vector(gpd, r_point, r_normal)) { - return; - } - mul_mat3_m4_v3(cache->obmat, r_normal); /* only rotation component */ - plane_from_point_normal_v3(r_plane, r_point, r_normal); - float dt = dist_to_plane_v3(fxd->object->obmat[3], r_plane); - fxd->loc[3] = dt; /* use last element to save it */ + DRWShadingGroup *grp; - DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1); + float axis_flip[2]; + axis_flip[0] = (fx->flag & FX_FLIP_HORIZONTAL) ? -1.0f : 1.0f; + axis_flip[1] = (fx->flag & FX_FLIP_VERTICAL) ? -1.0f : 1.0f; - DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1); - DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1); + GPUShader *sh = GPENCIL_shader_fx_transform_get(); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", axis_flip); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", (float[2]){0.0f, 0.0f}); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", 0.0f); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Pixelate FX */ -static void gpencil_fx_pixel(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_rim(RimShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; + DRWShadingGroup *grp; + + float winmat[4][4], persmat[4][4]; + float offset[2] = {fx->offset[0], fx->offset[1]}; + float blur_size[2] = {fx->blur[0], fx->blur[1]}; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; + mul_v2_fl(offset, distance_factor); + mul_v2_v2(offset, vp_size_inv); + mul_v2_fl(blur_size, distance_factor); + + GPUShader *sh = GPENCIL_shader_fx_rim_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Rim H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", (float[2]){blur_size[0] * vp_size_inv[0], 0.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", offset); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "maskColor", fx->mask_rgb); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + switch (fx->mode) { + case eShaderFxRimMode_Normal: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eShaderFxRimMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eShaderFxRimMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eShaderFxRimMode_Multiply: + case eShaderFxRimMode_Divide: + case eShaderFxRimMode_Overlay: + state |= DRW_STATE_BLEND_MUL; + break; + } + + zero_v2(offset); + + grp = gpencil_vfx_pass_create("Fx Rim V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", (float[2]){0.0f, blur_size[1] * vp_size_inv[1]}); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", offset); + DRW_shgroup_uniform_vec3_copy(grp, "rimColor", fx->rim_rgb); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->mode); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (fx->mode == eShaderFxRimMode_Overlay) { + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "blendMode", 999); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - PixelShaderFxData *fxd = (PixelShaderFxData *)fx; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_pixel_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_int(fx_shgrp, "size", &fxd->size[0], 3); - DRW_shgroup_uniform_vec4(fx_shgrp, "color", &fxd->rgba[0], 1); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; } -/* Rim FX */ -static void gpencil_fx_rim(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_pixelize(PixelShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; + DRWShadingGroup *grp; + + float persmat[4][4], winmat[4][4], ob_center[3], pixsize_uniform[2]; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + float pixel_size[2] = {fx->size[0], fx->size[1]}; + mul_v2_v2(pixel_size, vp_size_inv); + + /* Fixed pixelisation center from object center. */ + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + mul_v3_m4v3(ob_center, persmat, ob->obmat[3]); + mul_v3_fl(ob_center, 1.0f / w); + + /* Convert to uvs. */ + mul_v2_fl(ob_center, 0.5f); + add_v2_fl(ob_center, 0.5f); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + mul_v2_fl(pixel_size, (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w); + + /* Center to texel */ + madd_v2_v2fl(ob_center, pixel_size, -0.5f); + + GPUShader *sh = GPENCIL_shader_fx_pixelize_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + + /* Only if pixelated effect is bigger than 1px. */ + if (pixel_size[0] > vp_size_inv[0]) { + copy_v2_fl2(pixsize_uniform, pixel_size[0], vp_size_inv[1]); + grp = gpencil_vfx_pass_create("Fx Pixelize X", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelOffset", ob_center); + DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){pixel_size[0], 0.0f}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[0] / vp_size_inv[0] > 3.0) ? 2 : 1); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + + if (pixel_size[1] > vp_size_inv[1]) { + copy_v2_fl2(pixsize_uniform, vp_size_inv[0], pixel_size[1]); + grp = gpencil_vfx_pass_create("Fx Pixelize Y", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "targetPixelSize", pixsize_uniform); + DRW_shgroup_uniform_vec2_copy(grp, "accumOffset", (float[2]){0.0f, pixel_size[1]}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", (pixel_size[1] / vp_size_inv[1] > 3.0) ? 2 : 1); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } - RimShaderFxData *fxd = (RimShaderFxData *)fx; - bGPdata *gpd = cache->gpd; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - copy_v3_v3(fxd->runtime.loc, cache->loc); - - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - - DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2); - DRW_shgroup_uniform_vec3(fx_shgrp, "rim_color", &fxd->rim_rgb[0], 1); - DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; - - /* blur pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh_b = fx_shgrp; - - /* resolve pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_resolve_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1); - DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1); - - fxd->runtime.fx_sh_c = fx_shgrp; } -/* Shadow FX */ -static void gpencil_fx_shadow(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_shadow(ShadowShaderFxData *fx, Object *ob, gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx; - if ((!fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) { - fxd->runtime.fx_sh = NULL; - fxd->runtime.fx_sh_b = NULL; - fxd->runtime.fx_sh_c = NULL; - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_shadow_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - - DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2); - DRW_shgroup_uniform_float(fx_shgrp, "scale", &fxd->scale[0], 2); - DRW_shgroup_uniform_float(fx_shgrp, "rotation", &fxd->rotation, 1); - DRW_shgroup_uniform_vec4(fx_shgrp, "shadow_color", &fxd->shadow_rgba[0], 1); - - if ((fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) { - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1); - } - else { - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - } - - if (fxd->flag & FX_SHADOW_USE_WAVE) { - DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1); + DRWShadingGroup *grp; + + const bool use_obj_pivot = (fx->flag & FX_SHADOW_USE_OBJECT) != 0; + const bool use_wave = (fx->flag & FX_SHADOW_USE_WAVE) != 0; + + float uv_mat[4][4], winmat[4][4], persmat[4][4], rot_center[3]; + float wave_ofs[3], wave_dir[3], wave_phase, blur_dir[2], tmp[2]; + float offset[2] = {fx->offset[0], fx->offset[1]}; + float blur_size[2] = {fx->blur[0], fx->blur[1]}; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); + const float ratio = vp_size_inv[1] / vp_size_inv[0]; + + copy_v3_v3(rot_center, (use_obj_pivot && fx->object) ? fx->object->obmat[3] : ob->obmat[3]); + + const float w = fabsf(mul_project_m4_v3_zfac(persmat, rot_center)); + mul_v3_m4v3(rot_center, persmat, rot_center); + mul_v3_fl(rot_center, 1.0f / w); + + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; + mul_v2_fl(offset, distance_factor); + mul_v2_v2(offset, vp_size_inv); + mul_v2_fl(blur_size, distance_factor); + + rot_center[0] = rot_center[0] * 0.5f + 0.5f; + rot_center[1] = rot_center[1] * 0.5f + 0.5f; + + /* UV transform matrix. (loc, rot, scale) Sent to shader as 2x3 matrix. */ + unit_m4(uv_mat); + translate_m4(uv_mat, rot_center[0], rot_center[1], 0.0f); + rescale_m4(uv_mat, (float[3]){1.0f / fx->scale[0], 1.0f / fx->scale[1], 1.0f}); + translate_m4(uv_mat, -offset[0], -offset[1], 0.0f); + rescale_m4(uv_mat, (float[3]){1.0f / ratio, 1.0f, 1.0f}); + rotate_m4(uv_mat, 'Z', fx->rotation); + rescale_m4(uv_mat, (float[3]){ratio, 1.0f, 1.0f}); + translate_m4(uv_mat, -rot_center[0], -rot_center[1], 0.0f); + + if (use_wave) { + float dir[2]; + if (fx->orientation == 0) { + /* Horizontal */ + copy_v2_fl2(dir, 1.0f, 0.0f); + } + else { + /* Vertical */ + copy_v2_fl2(dir, 0.0f, 1.0f); + } + /* This is applied after rotation. Counter the rotation to keep aligned with global axis. */ + rotate_v2_v2fl(wave_dir, dir, fx->rotation); + /* Rotate 90°. */ + copy_v2_v2(wave_ofs, wave_dir); + SWAP(float, wave_ofs[0], wave_ofs[1]); + wave_ofs[1] *= -1.0f; + /* Keep world space scalling and aspect ratio. */ + mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); + mul_v2_v2(wave_dir, vp_size); + mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); + mul_v2_v2(wave_ofs, vp_size_inv); + /* Phase start at shadow center. */ + wave_phase = fx->phase - dot_v2v2(rot_center, wave_dir); } else { - DRW_shgroup_uniform_int_copy(fx_shgrp, "orientation", -1); - } - DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1); - DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1); - DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1); - - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh = fx_shgrp; - - /* blur pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh_b = fx_shgrp; - - /* resolve pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_shadow_resolve_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowDepth", &stl->g_data->temp_depth_tx_fx); - - fxd->runtime.fx_sh_c = fx_shgrp; + zero_v2(wave_dir); + zero_v2(wave_ofs); + wave_phase = 0.0f; + } + + GPUShader *sh = GPENCIL_shader_fx_shadow_get(); + + copy_v2_fl2(blur_dir, blur_size[0] * vp_size_inv[0], 0.0f); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Shadow H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", blur_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveDir", wave_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_float_copy(grp, "wavePhase", wave_phase); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotX", uv_mat[0]); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotY", uv_mat[1]); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", uv_mat[3]); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[0]))); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + unit_m4(uv_mat); + zero_v2(wave_ofs); + + /* We reseted the uv_mat so we need to accound for the rotation in the */ + copy_v2_fl2(tmp, 0.0f, blur_size[1]); + rotate_v2_v2fl(blur_dir, tmp, -fx->rotation); + mul_v2_v2(blur_dir, vp_size_inv); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; + grp = gpencil_vfx_pass_create("Fx Shadow V", state, iter, sh); + DRW_shgroup_uniform_vec4_copy(grp, "shadowColor", fx->shadow_rgba); + DRW_shgroup_uniform_vec2_copy(grp, "blurDir", blur_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotX", uv_mat[0]); + DRW_shgroup_uniform_vec2_copy(grp, "uvRotY", uv_mat[1]); + DRW_shgroup_uniform_vec2_copy(grp, "uvOffset", uv_mat[3]); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, blur_size[1]))); + DRW_shgroup_uniform_bool_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Glow FX */ -static void gpencil_fx_glow(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) +static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { - return; - } - GlowShaderFxData *fxd = (GlowShaderFxData *)fx; - bGPdata *gpd = cache->gpd; - copy_v3_v3(fxd->runtime.loc, cache->loc); - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - /* prepare pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_glow_prepare_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - - DRW_shgroup_uniform_vec3(fx_shgrp, "glow_color", &fxd->glow_color[0], 1); - DRW_shgroup_uniform_vec3(fx_shgrp, "select_color", &fxd->select_color[0], 1); - DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1); - DRW_shgroup_uniform_float(fx_shgrp, "threshold", &fxd->threshold, 1); - - fxd->runtime.fx_sh = fx_shgrp; - - /* blur pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_fx); - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); - DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); - - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->runtime.loc, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); - - fxd->runtime.fx_sh_b = fx_shgrp; - - /* resolve pass */ - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_glow_resolve_sh, psl->fx_shader_pass_blend); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowColor", &stl->g_data->temp_color_tx_fx); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowDepth", &stl->g_data->temp_depth_tx_fx); - - /* reuse field */ - DRW_shgroup_uniform_int(fx_shgrp, "alpha_mode", &fxd->blur[1], 1); - - fxd->runtime.fx_sh_c = fx_shgrp; -} + const bool use_glow_under = (fx->flag & FX_GLOW_USE_ALPHA) != 0; + DRWShadingGroup *grp; + const float s = sin(fx->rotation); + const float c = cos(fx->rotation); -/* Swirl FX */ -static void gpencil_fx_swirl(ShaderFxData *fx, - GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache) -{ - if (fx == NULL) { - return; - } - SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; - if (fxd->object == NULL) { - return; - } + GPUShader *sh = GPENCIL_shader_fx_glow_get(); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - DRWShadingGroup *fx_shgrp; - bGPdata *gpd = cache->gpd; + float ref_col[3]; - fxd->transparent = (int)fxd->flag & FX_SWIRL_MAKE_TRANSPARENT; + if (fx->mode == eShaderFxGlowMode_Luminance) { + ref_col[0] = fx->threshold; + ref_col[1] = -1.0f; + ref_col[2] = -1.0f; + } + else { + copy_v3_v3(ref_col, fx->select_color); + } + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col); + DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color); + DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under); + DRW_shgroup_uniform_bool_copy(grp, "firstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + state = DRW_STATE_WRITE_COLOR; + /* Blending: Force blending. */ + switch (fx->blend_mode) { + case eGplBlendMode_Regular: + state |= DRW_STATE_BLEND_ALPHA_PREMUL; + break; + case eGplBlendMode_Add: + state |= DRW_STATE_BLEND_ADD_FULL; + break; + case eGplBlendMode_Subtract: + state |= DRW_STATE_BLEND_SUB; + break; + case eGplBlendMode_Multiply: + case eGplBlendMode_Divide: + state |= DRW_STATE_BLEND_MUL; + break; + } + + /* Small Hack: We ask for RGBA16F buffer if using use_glow_under to store original + * revealage in alpha channel. */ + if (fx->blend_mode == eGplBlendMode_Subtract || use_glow_under) { + /* For this effect to propagate, we need a signed floating point buffer. */ + iter->pd->use_signed_fb = true; + } + + grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c}); + DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0]))); + DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f}); + DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]}); + DRW_shgroup_uniform_bool_copy(grp, "firstPass", false); + DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); +} - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_swirl_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); +static void gpencil_vfx_wave(WaveShaderFxData *fx, Object *ob, gpIterVfxData *iter) +{ + DRWShadingGroup *grp; - DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); + float winmat[4][4], persmat[4][4], wave_center[3]; + float wave_ofs[3], wave_dir[3], wave_phase; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); + const float *vp_size_inv = DRW_viewport_invert_size_get(); - DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, ob->obmat[3])); + mul_v3_m4v3(wave_center, persmat, ob->obmat[3]); + mul_v3_fl(wave_center, 1.0f / w); - DRW_shgroup_uniform_int(fx_shgrp, "radius", &fxd->radius, 1); - DRW_shgroup_uniform_float(fx_shgrp, "angle", &fxd->angle, 1); - DRW_shgroup_uniform_int(fx_shgrp, "transparent", &fxd->transparent, 1); + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(ob->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; - DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); - DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + wave_center[0] = wave_center[0] * 0.5f + 0.5f; + wave_center[1] = wave_center[1] * 0.5f + 0.5f; - fxd->runtime.fx_sh = fx_shgrp; + if (fx->orientation == 0) { + /* Horizontal */ + copy_v2_fl2(wave_dir, 1.0f, 0.0f); + } + else { + /* Vertical */ + copy_v2_fl2(wave_dir, 0.0f, 1.0f); + } + /* Rotate 90°. */ + copy_v2_v2(wave_ofs, wave_dir); + SWAP(float, wave_ofs[0], wave_ofs[1]); + wave_ofs[1] *= -1.0f; + /* Keep world space scalling and aspect ratio. */ + mul_v2_fl(wave_dir, 1.0f / (max_ff(1e-8f, fx->period) * distance_factor)); + mul_v2_v2(wave_dir, vp_size); + mul_v2_fl(wave_ofs, fx->amplitude * distance_factor); + mul_v2_v2(wave_ofs, vp_size_inv); + /* Phase start at shadow center. */ + wave_phase = fx->phase - dot_v2v2(wave_center, wave_dir); + + GPUShader *sh = GPENCIL_shader_fx_transform_get(); + + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Wave", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", (float[2]){1.0f, 1.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "waveDir", wave_dir); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", wave_ofs); + DRW_shgroup_uniform_float_copy(grp, "wavePhase", wave_phase); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", 0.0f); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* Wave Distortion FX */ -static void gpencil_fx_wave(ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +static void gpencil_vfx_swirl(SwirlShaderFxData *fx, Object *UNUSED(ob), gpIterVfxData *iter) { - if (fx == NULL) { + DRWShadingGroup *grp; + + if (fx->object == NULL) { return; } - WaveShaderFxData *fxd = (WaveShaderFxData *)fx; - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); - - DRWShadingGroup *fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_wave_sh, psl->fx_shader_pass); - DRW_shgroup_call(fx_shgrp, fxquad, NULL); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &stl->g_data->temp_color_tx_a); - DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &stl->g_data->temp_depth_tx_a); - DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1); - DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1); - DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1); - DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1); - DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1); + float winmat[4][4], persmat[4][4], swirl_center[3]; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_persmat_get(NULL, persmat, false); + const float *vp_size = DRW_viewport_size_get(); - fxd->runtime.fx_sh = fx_shgrp; -} - -/* ************************************************************** */ + copy_v3_v3(swirl_center, fx->object->obmat[3]); -/* create all FX shaders */ -void GPENCIL_create_fx_shaders(GPENCIL_e_data *e_data) -{ - /* fx shaders (all in screen space) */ - if (!e_data->gpencil_fx_blur_sh) { - e_data->gpencil_fx_blur_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_blur_frag_glsl, - NULL); - } - if (!e_data->gpencil_fx_colorize_sh) { - e_data->gpencil_fx_colorize_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_colorize_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_flip_sh) { - e_data->gpencil_fx_flip_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_flip_frag_glsl, - NULL); - } - if (!e_data->gpencil_fx_light_sh) { - e_data->gpencil_fx_light_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_light_frag_glsl, - NULL); - } - if (!e_data->gpencil_fx_pixel_sh) { - e_data->gpencil_fx_pixel_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_pixel_frag_glsl, - NULL); - } - if (!e_data->gpencil_fx_rim_prepare_sh) { - e_data->gpencil_fx_rim_prepare_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_rim_prepare_frag_glsl, NULL); + const float w = fabsf(mul_project_m4_v3_zfac(persmat, swirl_center)); + mul_v3_m4v3(swirl_center, persmat, swirl_center); + mul_v3_fl(swirl_center, 1.0f / w); - e_data->gpencil_fx_rim_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_rim_resolve_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_shadow_prepare_sh) { - e_data->gpencil_fx_shadow_prepare_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_shadow_prepare_frag_glsl, NULL); + /* Modify by distance to camera and object scale. */ + float world_pixel_scale = 1.0f / GPENCIL_PIXEL_FACTOR; + float scale = mat4_to_scale(fx->object->obmat); + float distance_factor = (world_pixel_scale * scale * winmat[1][1] * vp_size[1]) / w; - e_data->gpencil_fx_shadow_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_shadow_resolve_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_glow_prepare_sh) { - e_data->gpencil_fx_glow_prepare_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_glow_prepare_frag_glsl, NULL); + mul_v2_fl(swirl_center, 0.5f); + add_v2_fl(swirl_center, 0.5f); + mul_v2_v2(swirl_center, vp_size); - e_data->gpencil_fx_glow_resolve_sh = DRW_shader_create_fullscreen( - datatoc_gpencil_fx_glow_resolve_frag_glsl, NULL); - } - if (!e_data->gpencil_fx_swirl_sh) { - e_data->gpencil_fx_swirl_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_swirl_frag_glsl, - NULL); - } - if (!e_data->gpencil_fx_wave_sh) { - e_data->gpencil_fx_wave_sh = DRW_shader_create_fullscreen(datatoc_gpencil_fx_wave_frag_glsl, - NULL); + float radius = fx->radius * distance_factor; + if (radius < 1.0f) { + return; } -} -/* free FX shaders */ -void GPENCIL_delete_fx_shaders(GPENCIL_e_data *e_data) -{ - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_blur_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_colorize_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_flip_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_light_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_pixel_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_shadow_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_shadow_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_prepare_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_resolve_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_swirl_sh); - DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_wave_sh); -} + GPUShader *sh = GPENCIL_shader_fx_transform_get(); -/* create all passes used by FX */ -void GPENCIL_create_fx_passes(GPENCIL_PassList *psl) -{ - psl->fx_shader_pass = DRW_pass_create("GPencil Shader FX Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | - DRW_STATE_DEPTH_LESS); - psl->fx_shader_pass_blend = DRW_pass_create("GPencil Shader FX Pass", - DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + DRWState state = DRW_STATE_WRITE_COLOR; + grp = gpencil_vfx_pass_create("Fx Flip", state, iter, sh); + DRW_shgroup_uniform_vec2_copy(grp, "axisFlip", (float[2]){1.0f, 1.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "waveOffset", (float[2]){0.0f, 0.0f}); + DRW_shgroup_uniform_vec2_copy(grp, "swirlCenter", swirl_center); + DRW_shgroup_uniform_float_copy(grp, "swirlAngle", fx->angle); + DRW_shgroup_uniform_float_copy(grp, "swirlRadius", radius); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } -/* prepare fx shading groups */ -void gpencil_fx_prepare(GPENCIL_e_data *e_data, - GPENCIL_Data *vedata, - tGPencilObjectCache *cache_ob) +void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObject *tgp_ob) { - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - const bool wiremode = (bool)(cache_ob->shading_type[0] == OB_WIRE); - - int ob_idx = cache_ob->idx; - - if ((wiremode) || (cache_ob->shader_fx.first == NULL)) { + bGPdata *gpd = (bGPdata *)ob->data; + GPENCIL_FramebufferList *fbl = vedata->fbl; + GPENCIL_PrivateData *pd = vedata->stl->pd; + /* If simplify enabled, nothing more to do. */ + if (pd->simplify_fx) { return; } - /* loop FX */ - for (ShaderFxData *fx = cache_ob->shader_fx.first; fx; fx = fx->next) { - if (effect_is_active(cache_ob->gpd, fx, stl->storage->is_render)) { + + /* These may not be allocated yet, use adress of future pointer. */ + gpIterVfxData iter = { + .pd = pd, + .tgp_ob = tgp_ob, + .target_fb = &fbl->layer_fb, + .source_fb = &fbl->object_fb, + .target_color_tx = &pd->color_layer_tx, + .source_color_tx = &pd->color_object_tx, + .target_reveal_tx = &pd->reveal_layer_tx, + .source_reveal_tx = &pd->reveal_object_tx, + }; + + LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) { + if (effect_is_active(gpd, fx, pd->is_viewport)) { switch (fx->type) { case eShaderFxType_Blur: - gpencil_fx_blur(fx, ob_idx, e_data, vedata, cache_ob); + gpencil_vfx_blur((BlurShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Colorize: - gpencil_fx_colorize(fx, e_data, vedata); + gpencil_vfx_colorize((ColorizeShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Flip: - gpencil_fx_flip(fx, e_data, vedata); - break; - case eShaderFxType_Light: - gpencil_fx_light(fx, e_data, vedata, cache_ob); + gpencil_vfx_flip((FlipShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Pixel: - gpencil_fx_pixel(fx, e_data, vedata, cache_ob); + gpencil_vfx_pixelize((PixelShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Rim: - gpencil_fx_rim(fx, e_data, vedata, cache_ob); + gpencil_vfx_rim((RimShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Shadow: - gpencil_fx_shadow(fx, e_data, vedata, cache_ob); + gpencil_vfx_shadow((ShadowShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Glow: - gpencil_fx_glow(fx, e_data, vedata, cache_ob); + gpencil_vfx_glow((GlowShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Swirl: - gpencil_fx_swirl(fx, e_data, vedata, cache_ob); + gpencil_vfx_swirl((SwirlShaderFxData *)fx, ob, &iter); break; case eShaderFxType_Wave: - gpencil_fx_wave(fx, e_data, vedata); + gpencil_vfx_wave((WaveShaderFxData *)fx, ob, &iter); break; default: break; } } } -} - -/* helper to draw one FX pass and do ping-pong copy */ -static void gpencil_draw_fx_pass(GPENCIL_Data *vedata, DRWShadingGroup *shgrp, bool blend) -{ - if (shgrp == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {0.0f}; - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - - /* draw effect pass in temp texture (B) using as source the previous image - * existing in the other temp texture (A) */ - if (!blend) { - DRW_draw_pass_subset(psl->fx_shader_pass, shgrp, shgrp); - } - else { - DRW_draw_pass_subset(psl->fx_shader_pass_blend, shgrp, shgrp); - } - - /* copy pass from b to a for ping-pong frame buffers */ - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; - - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* helper to manage gaussian blur passes */ -static void draw_gpencil_blur_passes(GPENCIL_Data *vedata, BlurShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh == NULL) { - return; - } - - DRWShadingGroup *shgrp = fxd->runtime.fx_sh; - int samples = fxd->samples; - - float bx = fxd->blur[0]; - float by = fxd->blur[1]; - - /* the blur is done in two steps (Hor/Ver) because is faster and - * gets better result - * - * samples could be 0 and disable de blur effects because sometimes - * is easier animate the number of samples only, instead to animate the - * hide/unhide and the number of samples to make some effects. - */ - for (int b = 0; b < samples; b++) { - /* horizontal */ - if (bx > 0) { - fxd->blur[0] = bx; - fxd->blur[1] = 0; - gpencil_draw_fx_pass(vedata, shgrp, true); - } - /* vertical */ - if (by > 0) { - fxd->blur[0] = 0; - fxd->blur[1] = by; - gpencil_draw_fx_pass(vedata, shgrp, true); - } - } -} - -/* blur intermediate pass */ -static void draw_gpencil_midpass_blur(GPENCIL_Data *vedata, ShaderFxData_Runtime *runtime) -{ - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - const float clearcol[4] = {0.0f}; - - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, runtime->fx_sh_b, runtime->fx_sh_b); - - /* copy pass from b for ping-pong frame buffers */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* do blur of mid passes */ -static void draw_gpencil_do_blur( - GPENCIL_Data *vedata, ShaderFxData_Runtime *runtime, int samples, int bx, int by, int blur[2]) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; + if (tgp_ob->vfx.first != NULL) { + /* We need an extra pass to combine result to main buffer. */ + iter.target_fb = &fbl->gpencil_fb; - if ((samples > 0) && ((bx > 0) || (by > 0))) { - for (int x = 0; x < samples; x++) { + GPUShader *sh = GPENCIL_shader_fx_composite_get(); - /* horizontal */ - blur[0] = bx; - blur[1] = 0; - draw_gpencil_midpass_blur(vedata, runtime); + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL; + DRWShadingGroup *grp = gpencil_vfx_pass_create("GPencil Object Compose", state, &iter, sh); + DRW_shgroup_uniform_int_copy(grp, "isFirstPass", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - /* Vertical */ - blur[0] = 0; - blur[1] = by; - draw_gpencil_midpass_blur(vedata, runtime); - - blur[0] = bx; - blur[1] = by; - } - } -} - -/* helper to draw RIM passes */ -static void draw_gpencil_rim_passes(GPENCIL_Data *vedata, RimShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } + /* We cannot do custom blending on MultiTarget framebuffers. + * Workaround by doing 2 passes. */ + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_disable(grp, DRW_STATE_BLEND_MUL); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ADD_FULL); + DRW_shgroup_uniform_int_copy(grp, "isFirstPass", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {0.0f}; - - /* prepare mask */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur rim */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[1], &fxd->blur[0]); - - /* resolve */ - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c); - - /* copy pass from b to a for ping-pong frame buffers */ - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; - - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* helper to draw SHADOW passes */ -static void draw_gpencil_shadow_passes(GPENCIL_Data *vedata, ShadowShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - const float clearcol[4] = {0.0f}; - - /* prepare shadow */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur shadow */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[1], &fxd->blur[0]); - - /* resolve */ - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c); - - /* copy pass from b to a for ping-pong frame buffers */ - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; - - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* helper to draw GLOW passes */ -static void draw_gpencil_glow_passes(GPENCIL_Data *vedata, GlowShaderFxData *fxd) -{ - if (fxd->runtime.fx_sh_b == NULL) { - return; - } - - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; - GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; - - const float clearcol[4] = {0.0f}; - - /* prepare glow */ - GPU_framebuffer_bind(fbl->temp_fb_fx); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f); - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh, fxd->runtime.fx_sh); - - /* blur glow */ - draw_gpencil_do_blur( - vedata, &fxd->runtime, fxd->samples, fxd->blur[0], fxd->blur[0], &fxd->blur[0]); - - /* resolve */ - GPU_framebuffer_bind(fbl->temp_fb_b); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f); - - /* reuses blur field to keep alpha mode */ - fxd->blur[1] = (fxd->flag & FX_GLOW_USE_ALPHA) ? 1 : 0; - - DRW_draw_pass_subset(psl->fx_shader_pass_blend, fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c); - - /* copy pass from b to a for ping-pong frame buffers */ - stl->g_data->input_depth_tx = stl->g_data->temp_depth_tx_b; - stl->g_data->input_color_tx = stl->g_data->temp_color_tx_b; - - GPU_framebuffer_bind(fbl->temp_fb_a); - GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); - DRW_draw_pass(psl->mix_pass_noblend); -} - -/* apply all object fx effects */ -void gpencil_fx_draw(GPENCIL_e_data *UNUSED(e_data), - GPENCIL_Data *vedata, - tGPencilObjectCache *cache_ob) -{ - GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; - - /* loop FX modifiers */ - for (ShaderFxData *fx = cache_ob->shader_fx.first; fx; fx = fx->next) { - if (effect_is_active(cache_ob->gpd, fx, stl->storage->is_render)) { - switch (fx->type) { - - case eShaderFxType_Blur: { - BlurShaderFxData *fxd = (BlurShaderFxData *)fx; - draw_gpencil_blur_passes(vedata, fxd); - break; - } - case eShaderFxType_Colorize: { - ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Flip: { - FlipShaderFxData *fxd = (FlipShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Light: { - LightShaderFxData *fxd = (LightShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Pixel: { - PixelShaderFxData *fxd = (PixelShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Rim: { - RimShaderFxData *fxd = (RimShaderFxData *)fx; - draw_gpencil_rim_passes(vedata, fxd); - break; - } - case eShaderFxType_Shadow: { - ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx; - draw_gpencil_shadow_passes(vedata, fxd); - break; - } - case eShaderFxType_Glow: { - GlowShaderFxData *fxd = (GlowShaderFxData *)fx; - draw_gpencil_glow_passes(vedata, fxd); - break; - } - case eShaderFxType_Swirl: { - SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - case eShaderFxType_Wave: { - WaveShaderFxData *fxd = (WaveShaderFxData *)fx; - gpencil_draw_fx_pass(vedata, fxd->runtime.fx_sh, false); - break; - } - default: - break; - } - } + pd->use_object_fb = true; + pd->use_layer_fb = true; } } diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl deleted file mode 100644 index 0f64f54c67b..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl +++ /dev/null @@ -1,85 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; - -uniform int blur[2]; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 noffset = vec2(blur[0], blur[1]); - -out vec4 FragColor; - -float get_zdepth(ivec2 poxy) -{ - /* if outside viewport set as infinite depth */ - if ((poxy.x < 0) || (poxy.x > Viewport.x)) { - return 1.0f; - } - if ((poxy.y < 0) || (poxy.y > Viewport.y)) { - return 1.0f; - } - - float zdepth = texelFetch(strokeDepth, poxy, 0).r; - return zdepth; -} - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - - float dx = (ProjectionMatrix[3][3] == 0.0) ? (noffset[0] / (nloc.z * defaultpixsize)) : - (noffset[0] / defaultpixsize); - float dy = (ProjectionMatrix[3][3] == 0.0) ? (noffset[1] / (nloc.z * defaultpixsize)) : - (noffset[1] / defaultpixsize); - - /* round to avoid shift when add more samples */ - dx = floor(dx) + 1.0; - dy = floor(dy) + 1.0; - - /* apply blurring, using a 9-tap filter with predefined gaussian weights */ - /* depth (get the value of the surrounding pixels) */ - float outdepth = get_zdepth(ivec2(uv.x, uv.y)); - for (int x = -1; x < 2; x++) { - for (int y = -1; y < 2; y++) { - float depth = get_zdepth(ivec2(uv.x + x * dx, uv.y + y * dy)); - if (depth < outdepth) { - outdepth = depth; - break; - } - } - } - gl_FragDepth = outdepth; - - /* color */ - vec4 outcolor = vec4(0.0); - outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y + 1.0 * dy), 0) * 0.0947416; - outcolor += texelFetch(strokeColor, ivec2(uv.x - 0.0 * dx, uv.y + 1.0 * dy), 0) * 0.118318; - outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y + 1.0 * dy), 0) * 0.0947416; - outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y + 0.0 * dy), 0) * 0.118318; - - outcolor += texelFetch(strokeColor, ivec2(uv.x, uv.y), 0) * 0.147761; - - outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y + 0.0 * dy), 0) * 0.118318; - outcolor += texelFetch(strokeColor, ivec2(uv.x - 1.0 * dx, uv.y - 1.0 * dy), 0) * 0.0947416; - outcolor += texelFetch(strokeColor, ivec2(uv.x + 0.0 * dx, uv.y - 1.0 * dy), 0) * 0.118318; - outcolor += texelFetch(strokeColor, ivec2(uv.x + 1.0 * dx, uv.y - 1.0 * dy), 0) * 0.0947416; - - FragColor = clamp(outcolor, 0, 1.0); - - /* discar extreme values */ - if (outcolor.a < 0.02f) { - discard; - } - if ((outdepth <= 0.000001) || (outdepth >= 0.999999)) { - discard; - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl deleted file mode 100644 index 52f42d30d06..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl +++ /dev/null @@ -1,82 +0,0 @@ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform vec4 low_color; -uniform vec4 high_color; -uniform int mode; -uniform float factor; - -out vec4 FragColor; - -#define MODE_GRAYSCALE 0 -#define MODE_SEPIA 1 -#define MODE_DUOTONE 2 -#define MODE_CUSTOM 3 -#define MODE_TRANSPARENT 4 - -float get_luminance(vec4 color) -{ - float lum = (color.r * 0.2126) + (color.g * 0.7152) + (color.b * 0.723); - return lum; -} - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - vec4 src_pixel = texelFetch(strokeColor, uv.xy, 0); - float luminance = get_luminance(src_pixel); - vec4 outcolor; - - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - - switch (mode) { - case MODE_GRAYSCALE: { - outcolor = vec4(luminance, luminance, luminance, src_pixel.a); - break; - } - case MODE_SEPIA: { - float Red = (src_pixel.r * 0.393) + (src_pixel.g * 0.769) + (src_pixel.b * 0.189); - float Green = (src_pixel.r * 0.349) + (src_pixel.g * 0.686) + (src_pixel.b * 0.168); - float Blue = (src_pixel.r * 0.272) + (src_pixel.g * 0.534) + (src_pixel.b * 0.131); - outcolor = vec4(Red, Green, Blue, src_pixel.a); - break; - } - case MODE_DUOTONE: { - if (luminance <= factor) { - outcolor = low_color; - } - else { - outcolor = high_color; - } - break; - } - case MODE_CUSTOM: { - /* if below umbral, force custom color */ - if (luminance <= factor) { - outcolor = low_color; - } - else { - outcolor = vec4(luminance * low_color.r, - luminance * low_color.b, - luminance * low_color.b, - src_pixel.a); - } - break; - } - case MODE_TRANSPARENT: { - outcolor = vec4(src_pixel.rgb, src_pixel.a * factor); - break; - } - default: { - outcolor = src_pixel; - } - } - - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl deleted file mode 100644 index 2cd77007b36..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl +++ /dev/null @@ -1,37 +0,0 @@ -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 wsize; -uniform int flipmode; - -void main() -{ - vec2 mode = vec2(0, 0); - /* horz. */ - if (flipmode >= 110) { - mode[0] = 1; - } - /* vert. */ - if ((flipmode == 101) || (flipmode == 111)) { - mode[1] = 1; - } - - vec2 uv = vec2(gl_FragCoord.xy); - float stroke_depth; - vec4 outcolor; - - if (mode[0] > 0) { - uv.x = wsize.x - uv.x; - } - if (mode[1] > 0) { - uv.y = wsize.y - uv.y; - } - - ivec2 iuv = ivec2(uv.x, uv.y); - stroke_depth = texelFetch(strokeDepth, iuv, 0).r; - outcolor = texelFetch(strokeColor, iuv, 0); - - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl deleted file mode 100644 index 676b9b05db9..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_prepare_frag.glsl +++ /dev/null @@ -1,68 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -/* ******************************************************************* */ -/* create glow mask */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform vec3 glow_color; -uniform vec3 select_color; -uniform float threshold; -uniform int mode; - -out vec4 FragColor; - -#define MODE_LUMINANCE 0 -#define MODE_COLOR 1 - -/* calc luminance */ -float luma(vec3 color) -{ - /* the color is linear, so do not apply tonemapping */ - return (color.r + color.g + color.b) / 3.0; -} - -bool check_color(vec3 color_a, vec3 color_b) -{ - /* need round the number to avoid precision errors */ - if ((floor(color_a.r * 100) == floor(color_b.r * 100)) && - (floor(color_a.g * 100) == floor(color_b.g * 100)) && - (floor(color_a.b * 100) == floor(color_b.b * 100))) { - return true; - } - - return false; -} - -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, ivec2(uv.xy), 0).r; - vec4 src_pixel = texelFetch(strokeColor, ivec2(uv.xy), 0); - vec4 outcolor; - - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - - if (mode == MODE_LUMINANCE) { - if (luma(src_pixel.rgb) < threshold) { - discard; - } - } - else if (mode == MODE_COLOR) { - if (!check_color(src_pixel.rgb, select_color.rgb)) { - discard; - } - } - else { - discard; - } - - gl_FragDepth = stroke_depth; - FragColor = vec4(glow_color.rgb, 1.0); -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl deleted file mode 100644 index e2aceb9eefe..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_glow_resolve_frag.glsl +++ /dev/null @@ -1,46 +0,0 @@ -/* ******************************************************************* */ -/* Resolve GLOW pass */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D glowColor; -uniform sampler2D glowDepth; -uniform int alpha_mode; - -out vec4 FragColor; - -void main() -{ - vec4 outcolor; - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - vec4 src_pixel = texelFetch(strokeColor, uv.xy, 0); - vec4 glow_pixel = texelFetch(glowColor, uv.xy, 0); - float glow_depth = texelFetch(glowDepth, uv.xy, 0).r; - - if (alpha_mode == 0) { - outcolor = src_pixel + glow_pixel; - } - else { - if ((src_pixel.a < 0.1) || (glow_pixel.a < 0.1)) { - outcolor = src_pixel + glow_pixel; - } - else { - outcolor = src_pixel; - } - } - - if (src_pixel.a < glow_pixel.a) { - gl_FragDepth = glow_depth; - } - else { - gl_FragDepth = stroke_depth; - } - - if (outcolor.a < 0.001) { - discard; - } - - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl deleted file mode 100644 index a5c321c20c1..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl +++ /dev/null @@ -1,70 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; -uniform vec4 loc; -uniform float energy; -uniform float ambient; - -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -out vec4 FragColor; - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -#define height loc.w - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - /* need to calculate ndc because this is not done by vertex shader */ - vec3 ndc = vec3(vertex).xyz / vertex.w; - - vec2 sc; - sc.x = ((ndc.x + 1.0) / 2.0) * Viewport.x; - sc.y = ((ndc.y + 1.0) / 2.0) * Viewport.y; - - return sc; -} - -void main() -{ - float stroke_depth; - vec4 objcolor; - - vec4 light_loc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - vec2 light2d = toScreenSpace(light_loc); - - /* calc pixel scale */ - float pxscale = (ProjectionMatrix[3][3] == 0.0) ? (10.0 / (light_loc.z * defaultpixsize)) : - (10.0 / defaultpixsize); - pxscale = max(pxscale, 0.000001); - - /* the height over plane is received in the w component of the loc - * and needs a factor to adapt to pixels - */ - float peak = height * 10.0 * pxscale; - vec3 light3d = vec3(light2d.x, light2d.y, peak); - - vec2 uv = vec2(gl_FragCoord.xy); - vec3 frag_loc = vec3(uv.x, uv.y, 0); - vec3 norm = vec3(0, 0, 1.0); /* always z-up */ - - ivec2 iuv = ivec2(uv.x, uv.y); - stroke_depth = texelFetch(strokeDepth, iuv, 0).r; - objcolor = texelFetch(strokeColor, iuv, 0); - - /* diffuse light */ - vec3 lightdir = normalize(light3d - frag_loc); - float diff = max(dot(norm, lightdir), 0.0); - float dist = length(light3d - frag_loc) / pxscale; - float factor = diff * ((energy * 100.0) / (dist * dist)); - - vec3 result = factor * max(ambient, 0.1) * vec3(objcolor); - - gl_FragDepth = stroke_depth; - FragColor = vec4(result.r, result.g, result.b, objcolor.a); -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl deleted file mode 100644 index 46b3c4286b4..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl +++ /dev/null @@ -1,51 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform int size[3]; -uniform vec4 color; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -out vec4 FragColor; - -int uselines = size[2]; -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 nsize = max(vec2(size[0], size[1]), 3.0); - -/* This pixelation shader is a modified version of original Geeks3d.com code */ -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - - float dx = (ProjectionMatrix[3][3] == 0.0) ? (nsize[0] / (nloc.z * defaultpixsize)) : - (nsize[0] / defaultpixsize); - float dy = (ProjectionMatrix[3][3] == 0.0) ? (nsize[1] / (nloc.z * defaultpixsize)) : - (nsize[1] / defaultpixsize); - - dx = max(abs(dx), 3.0); - dy = max(abs(dy), 3.0); - - vec2 coord = vec2(dx * floor(uv.x / dx), dy * floor(uv.y / dy)); - - float stroke_depth = texelFetch(strokeDepth, ivec2(coord), 0).r; - vec4 outcolor = texelFetch(strokeColor, ivec2(coord), 0); - - if (uselines == 1) { - float difx = uv.x - (floor(uv.x / nsize[0]) * nsize[0]); - if ((difx == 0.5) && (outcolor.a > 0)) { - outcolor = color; - } - float dify = uv.y - (floor(uv.y / nsize[1]) * nsize[1]); - if ((dify == 0.5) && (outcolor.a > 0)) { - outcolor = color; - } - } - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl deleted file mode 100644 index 2a17e573978..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl +++ /dev/null @@ -1,65 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -/* ******************************************************************* */ -/* create rim and mask */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; - -uniform int offset[2]; -uniform vec3 rim_color; -uniform vec3 mask_color; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 noffset = vec2(offset[0], offset[1]); - -out vec4 FragColor; - -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - - float dx = (ProjectionMatrix[3][3] == 0.0) ? (noffset[0] / (nloc.z * defaultpixsize)) : - (noffset[0] / defaultpixsize); - float dy = (ProjectionMatrix[3][3] == 0.0) ? (noffset[1] / (nloc.z * defaultpixsize)) : - (noffset[1] / defaultpixsize); - - float stroke_depth = texelFetch(strokeDepth, ivec2(uv.xy), 0).r; - vec4 src_pixel = texelFetch(strokeColor, ivec2(uv.xy), 0); - vec4 offset_pixel = texelFetch(strokeColor, ivec2(uv.x - dx, uv.y - dy), 0); - vec4 outcolor; - - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - /* check inside viewport */ - else if ((uv.x - dx < 0) || (uv.x - dx > Viewport[0])) { - discard; - } - else if ((uv.y - dy < 0) || (uv.y - dy > Viewport[1])) { - discard; - } - /* pixel is equal to mask color, keep */ - else if (src_pixel.rgb == mask_color.rgb) { - discard; - } - else { - if ((src_pixel.a > 0) && (offset_pixel.a > 0)) { - discard; - } - else { - outcolor = vec4(rim_color, 1.0); - } - } - - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl deleted file mode 100644 index fa010baa32f..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl +++ /dev/null @@ -1,98 +0,0 @@ -/* ******************************************************************* */ -/* Resolve RIM pass and add blur if needed */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D strokeRim; - -uniform vec3 mask_color; -uniform int mode; - -out vec4 FragColor; - -#define MODE_NORMAL 0 -#define MODE_OVERLAY 1 -#define MODE_ADD 2 -#define MODE_SUB 3 -#define MODE_MULTIPLY 4 -#define MODE_DIVIDE 5 - -float overlay_color(float a, float b) -{ - float rtn; - if (a < 0.5) { - rtn = 2.0 * a * b; - } - else { - rtn = 1.0 - 2.0 * (1.0 - a) * (1.0 - b); - } - - return rtn; -} - -vec4 get_blend_color(int mode, vec4 src_color, vec4 mix_color) -{ - vec4 outcolor; - if (mode == MODE_NORMAL) { - outcolor = mix_color; - } - else if (mode == MODE_OVERLAY) { - outcolor.r = overlay_color(src_color.r, mix_color.r); - outcolor.g = overlay_color(src_color.g, mix_color.g); - outcolor.b = overlay_color(src_color.b, mix_color.b); - } - else if (mode == MODE_ADD) { - outcolor = src_color + mix_color; - } - else if (mode == MODE_SUB) { - outcolor = src_color - mix_color; - } - else if (mode == MODE_MULTIPLY) { - outcolor = src_color * mix_color; - } - else if (mode == MODE_DIVIDE) { - outcolor = src_color / mix_color; - } - else { - outcolor = mix_color; - } - - /* use always the alpha of source color */ - - outcolor.a = src_color.a; - /* use alpha to calculate the weight of the mixed color */ - outcolor = mix(src_color, outcolor, mix_color.a); - - return outcolor; -} - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - vec4 src_pixel = texelFetch(strokeColor, uv.xy, 0); - vec4 rim_pixel = texelFetch(strokeRim, uv.xy, 0); - - vec4 outcolor = src_pixel; - - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - /* pixel is equal to mask color, keep */ - else if (src_pixel.rgb == mask_color.rgb) { - outcolor = src_pixel; - } - else { - if (rim_pixel.a == 0.0f) { - outcolor = src_pixel; - } - else { - outcolor = get_blend_color(mode, src_pixel, rim_pixel); - } - } - - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl deleted file mode 100644 index d2e20feae18..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_prepare_frag.glsl +++ /dev/null @@ -1,98 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -/* ******************************************************************* */ -/* create shadow */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform vec2 Viewport; - -uniform int offset[2]; -uniform float scale[2]; -uniform float rotation; -uniform vec4 shadow_color; - -uniform float amplitude; -uniform float period; -uniform float phase; -uniform int orientation; - -uniform vec3 loc; -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -#define M_PI 3.1415926535897932384626433832795 - -#define HORIZONTAL 0 -#define VERTICAL 1 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); -vec2 noffset = vec2(offset[0], offset[1]); -float cosv = cos(rotation); -float sinv = sin(rotation); - -out vec4 FragColor; - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - /* need to calculate ndc because this is not done by vertex shader */ - vec3 ndc = vec3(vertex).xyz / vertex.w; - - vec2 sc; - sc.x = ((ndc.x + 1.0) / 2.0) * Viewport.x; - sc.y = ((ndc.y + 1.0) / 2.0) * Viewport.y; - - return sc; -} - -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - vec4 nloc = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - vec2 loc2d = toScreenSpace(nloc); - - float dx = (ProjectionMatrix[3][3] == 0.0) ? (noffset[0] / (nloc.z * defaultpixsize)) : - (noffset[0] / defaultpixsize); - float dy = (ProjectionMatrix[3][3] == 0.0) ? (noffset[1] / (nloc.z * defaultpixsize)) : - (noffset[1] / defaultpixsize); - - /* move point to new coords system */ - vec2 tpos = vec2(uv.x, uv.y) - loc2d; - - /* rotation */ - if (rotation != 0) { - vec2 rotpoint = vec2((tpos.x * cosv) - (tpos.y * sinv), (tpos.x * sinv) + (tpos.y * cosv)); - tpos = rotpoint; - } - - /* apply offset */ - tpos = vec2(tpos.x - dx, tpos.y - dy); - - /* apply scale */ - tpos.x *= 1.0 / scale[0]; - tpos.y *= 1.0 / scale[1]; - - /* back to original coords system */ - vec2 texpos = tpos + loc2d; - - /* wave */ - if (orientation == HORIZONTAL) { - float pval = (uv.x * M_PI) / Viewport[0]; - texpos.y += amplitude * sin((period * pval) + phase); - } - else if (orientation == VERTICAL) { - float pval = (uv.y * M_PI) / Viewport[1]; - texpos.x += amplitude * sin((period * pval) + phase); - } - - vec4 src_pixel = texelFetch(strokeColor, ivec2(texpos.x, texpos.y), 0); - /* is transparent */ - if (src_pixel.a == 0.0f) { - discard; - } - - gl_FragDepth = texelFetch(strokeDepth, ivec2(texpos.x, texpos.y), 0).r; - FragColor = shadow_color; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl deleted file mode 100644 index 3ef11008adf..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_shadow_resolve_frag.glsl +++ /dev/null @@ -1,32 +0,0 @@ -/* ******************************************************************* */ -/* Resolve Shadow pass */ -/* ******************************************************************* */ -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D shadowColor; -uniform sampler2D shadowDepth; - -out vec4 FragColor; - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - float stroke_depth = texelFetch(strokeDepth, uv.xy, 0).r; - float shadow_depth = texelFetch(shadowDepth, uv.xy, 0).r; - vec4 stroke_pixel = texelFetch(strokeColor, uv.xy, 0); - vec4 shadow_pixel = texelFetch(shadowColor, uv.xy, 0); - - /* copy original pixel */ - vec4 outcolor = stroke_pixel; - float outdepth = stroke_depth; - - /* if stroke is not on top, copy shadow */ - if ((stroke_pixel.a <= 0.2) && (shadow_pixel.a > 0.0)) { - outcolor = shadow_pixel; - outdepth = shadow_depth; - } - - gl_FragDepth = outdepth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl deleted file mode 100644 index 01d4fe40195..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl +++ /dev/null @@ -1,74 +0,0 @@ -uniform mat4 ProjectionMatrix; -uniform mat4 ViewMatrix; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform vec2 Viewport; -uniform vec3 loc; -uniform int radius; -uniform float angle; -uniform int transparent; - -uniform float pixsize; /* rv3d->pixsize */ -uniform float pixfactor; - -out vec4 FragColor; - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - /* need to calculate ndc because this is not done by vertex shader */ - vec3 ndc = vec3(vertex).xyz / vertex.w; - - vec2 sc; - sc.x = ((ndc.x + 1.0) / 2.0) * Viewport.x; - sc.y = ((ndc.y + 1.0) / 2.0) * Viewport.y; - - return sc; -} - -/* This swirl shader is a modified version of original Geeks3d.com code */ -void main() -{ - vec2 uv = vec2(gl_FragCoord.xy); - float stroke_depth; - vec4 outcolor; - - vec4 center3d = ProjectionMatrix * ViewMatrix * vec4(loc.xyz, 1.0); - vec2 center = toScreenSpace(center3d); - vec2 tc = uv - center; - - float dist = length(tc); - float locpixsize = abs((loc.z * defaultpixsize)); - if (locpixsize == 0) { - locpixsize = 1; - } - float pxradius = (ProjectionMatrix[3][3] == 0.0) ? (radius / locpixsize) : - (radius / defaultpixsize); - pxradius = max(pxradius, 1); - - if (dist <= pxradius) { - float percent = (pxradius - dist) / pxradius; - float theta = percent * percent * angle * 8.0; - float s = sin(theta); - float c = cos(theta); - tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c))); - tc += center; - - stroke_depth = texelFetch(strokeDepth, ivec2(tc), 0).r; - outcolor = texelFetch(strokeColor, ivec2(tc), 0); - } - else { - if (transparent == 1) { - discard; - } - stroke_depth = texelFetch(strokeDepth, ivec2(uv), 0).r; - outcolor = texelFetch(strokeColor, ivec2(uv), 0); - } - - gl_FragDepth = stroke_depth; - FragColor = outcolor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl deleted file mode 100644 index 0a5df9f6d77..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl +++ /dev/null @@ -1,44 +0,0 @@ - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -uniform float amplitude; -uniform float period; -uniform float phase; -uniform int orientation; -uniform vec2 wsize; - -#define M_PI 3.1415926535897932384626433832795 - -#define HORIZONTAL 0 -#define VERTICAL 1 - -void main() -{ - vec4 outcolor; - ivec2 uv = ivec2(gl_FragCoord.xy); - float stroke_depth; - - float value; - if (orientation == HORIZONTAL) { - float pval = (uv.x * M_PI) / wsize[0]; - value = amplitude * sin((period * pval) + phase); - outcolor = texelFetch(strokeColor, ivec2(uv.x, uv.y + value), 0); - stroke_depth = texelFetch(strokeDepth, ivec2(uv.x, uv.y + value), 0).r; - } - else { - float pval = (uv.y * M_PI) / wsize[1]; - value = amplitude * sin((period * pval) + phase); - outcolor = texelFetch(strokeColor, ivec2(uv.x + value, uv.y), 0); - stroke_depth = texelFetch(strokeDepth, ivec2(uv.x + value, uv.y), 0).r; - } - - FragColor = outcolor; - gl_FragDepth = stroke_depth; - - if (outcolor.a < 0.02f) { - discard; - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl new file mode 100644 index 00000000000..b512b54e392 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_frag.glsl @@ -0,0 +1,64 @@ + +uniform sampler2D edgesTex; +uniform sampler2D areaTex; +uniform sampler2D searchTex; +uniform sampler2D blendTex; +uniform sampler2D colorTex; +uniform sampler2D revealTex; +uniform bool onlyAlpha; +uniform bool doAntiAliasing; + +in vec2 uvs; +in vec2 pixcoord; +in vec4 offset[3]; + +#if SMAA_STAGE == 0 +out vec2 fragColor; +#elif SMAA_STAGE == 1 +out vec4 fragColor; +#elif SMAA_STAGE == 2 +/* Reminder: Blending func is fragRevealage * DST + fragColor .*/ +layout(location = 0, index = 0) out vec4 outColor; +layout(location = 0, index = 1) out vec4 outReveal; +#endif + +void main() +{ +#if SMAA_STAGE == 0 + /* Detect edges in color and revealage buffer. */ + fragColor = SMAALumaEdgeDetectionPS(uvs, offset, colorTex); + fragColor = max(fragColor, SMAALumaEdgeDetectionPS(uvs, offset, revealTex)); + /* Discard if there is no edge. */ + if (dot(fragColor, float2(1.0, 1.0)) == 0.0) { + discard; + } + +#elif SMAA_STAGE == 1 + fragColor = SMAABlendingWeightCalculationPS( + uvs, pixcoord, offset, edgesTex, areaTex, searchTex, vec4(0)); + +#elif SMAA_STAGE == 2 + /* Resolve both buffers. */ + if (doAntiAliasing) { + outColor = SMAANeighborhoodBlendingPS(uvs, offset[0], colorTex, blendTex); + outReveal = SMAANeighborhoodBlendingPS(uvs, offset[0], revealTex, blendTex); + } + else { + outColor = texture(colorTex, uvs); + outReveal = texture(revealTex, uvs); + } + + /* Revealage, how much light passes through. */ + /* Average for alpha channel. */ + outReveal.a = clamp(dot(outReveal.rgb, vec3(0.333334)), 0.0, 1.0); + /* Color buf is already premultiplied. Just add it to the color. */ + /* Add the alpha. */ + outColor.a = 1.0 - outReveal.a; + + if (onlyAlpha) { + /* Special case in wireframe xray mode. */ + outColor = vec4(0.0); + outReveal.rgb = outReveal.aaa; + } +#endif +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl new file mode 100644 index 00000000000..07734d19972 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_antialiasing_vert.glsl @@ -0,0 +1,21 @@ + +out vec2 uvs; +out vec2 pixcoord; +out vec4 offset[3]; + +void main() +{ + int v = gl_VertexID % 3; + float x = -1.0 + float((v & 1) << 2); + float y = -1.0 + float((v & 2) << 1); + gl_Position = vec4(x, y, 1.0, 1.0); + uvs = (gl_Position.xy + 1.0) * 0.5; + +#if SMAA_STAGE == 0 + SMAAEdgeDetectionVS(uvs, offset); +#elif SMAA_STAGE == 1 + SMAABlendingWeightCalculationVS(uvs, pixcoord, offset); +#elif SMAA_STAGE == 2 + SMAANeighborhoodBlendingVS(uvs, offset[0]); +#endif +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl deleted file mode 100644 index 18803bfa3fa..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl +++ /dev/null @@ -1,12 +0,0 @@ -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - - gl_FragDepth = texelFetch(strokeDepth, uv, 0).r; - FragColor = texelFetch(strokeColor, uv, 0); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl deleted file mode 100644 index 85dee4390a5..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_blend_frag.glsl +++ /dev/null @@ -1,157 +0,0 @@ -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform sampler2D blendColor; -uniform sampler2D blendDepth; -uniform int mode; -uniform int mask_layer; -uniform int tonemapping; - -#define ON 1 -#define OFF 0 - -#define MODE_REGULAR 0 -#define MODE_OVERLAY 1 -#define MODE_ADD 2 -#define MODE_SUB 3 -#define MODE_MULTIPLY 4 -#define MODE_DIVIDE 5 - -float overlay_color(float a, float b) -{ - float rtn; - if (a < 0.5) { - rtn = 2.0 * a * b; - } - else { - rtn = 1.0 - 2.0 * (1.0 - a) * (1.0 - b); - } - - return rtn; -} - -vec4 get_blend_color(int mode, vec4 src_color, vec4 mix_color) -{ - vec4 outcolor; - - if (mix_color.a == 0) { - return src_color; - } - - switch (mode) { - case MODE_REGULAR: { - /* premult */ - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - outcolor = vec4(mix(src_color.rgb, mix_color.rgb, mix_color.a), src_color.a); - break; - } - case MODE_OVERLAY: { - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor.r = overlay_color(src_color.r, mix_color.r); - outcolor.g = overlay_color(src_color.g, mix_color.g); - outcolor.b = overlay_color(src_color.b, mix_color.b); - outcolor.a = src_color.a; - break; - } - case MODE_ADD: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color + mix_color; - outcolor.a = src_color.a; - break; - } - case MODE_SUB: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color - mix_color; - outcolor.a = clamp(src_color.a - mix_color.a, 0.0, 1.0); - break; - } - case MODE_MULTIPLY: { - src_color = vec4(vec3(src_color.rgb / src_color.a), src_color.a); - mix_color = vec4(vec3(mix_color.rgb / mix_color.a), mix_color.a); - - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color * mix_color; - outcolor.a = src_color.a; - break; - } - case MODE_DIVIDE: { - mix_color.rgb = mix(src_color.rgb, mix_color.rgb, mix_color.a); - outcolor = src_color / mix_color; - outcolor.a = src_color.a; - break; - } - default: { - outcolor = mix_color; - outcolor.a = src_color.a; - break; - } - } - return clamp(outcolor, 0.0, 1.0); -} - -float linearrgb_to_srgb(float c) -{ - if (c < 0.0031308) { - return (c < 0.0) ? 0.0 : c * 12.92; - } - else { - return 1.055 * pow(c, 1.0 / 2.4) - 0.055; - } -} - -vec4 tone(vec4 stroke_color) -{ - if (tonemapping == 1) { - vec4 color = vec4(0, 0, 0, stroke_color.a); - color.r = linearrgb_to_srgb(stroke_color.r); - color.g = linearrgb_to_srgb(stroke_color.g); - color.b = linearrgb_to_srgb(stroke_color.b); - return color; - } - else { - return stroke_color; - } -} - -void main() -{ - vec4 outcolor; - ivec2 uv = ivec2(gl_FragCoord.xy); - vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba; - float stroke_depth = texelFetch(strokeDepth, uv, 0).r; - - vec4 mix_color = texelFetch(blendColor, uv, 0).rgba; - float mix_depth = texelFetch(blendDepth, uv, 0).r; - - if (stroke_color.a > 0) { - if (mix_color.a > 0) { - /* apply blend mode */ - FragColor = get_blend_color(mode, stroke_color, mix_color); - } - else { - FragColor = stroke_color; - } - gl_FragDepth = min(stroke_depth, mix_depth); - } - else { - if (mask_layer == ON) { - discard; - } - else { - /* if not using mask, return mix color */ - FragColor = mix_color; - gl_FragDepth = mix_depth; - } - } - - /* apply tone mapping */ - FragColor = tone(FragColor); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl new file mode 100644 index 00000000000..8774b633467 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -0,0 +1,593 @@ + +/* Must match C declaration. */ +struct gpMaterial { + vec4 stroke_color; + vec4 fill_color; + vec4 fill_mix_color; + vec4 fill_uv_rot_scale; + vec4 fill_uv_offset; + /* Put float/int at the end to avoid padding error */ + float stroke_texture_mix; + float stroke_u_scale; + float fill_texture_mix; + int flag; + /* Please ensure 16 byte alignment (multiple of vec4). */ +}; + +/* flag */ +#define GP_STROKE_ALIGNMENT_STROKE 1 +#define GP_STROKE_ALIGNMENT_OBJECT 2 +#define GP_STROKE_ALIGNMENT_FIXED 3 +#define GP_STROKE_ALIGNMENT 0x3 +#define GP_STROKE_OVERLAP (1 << 2) +#define GP_STROKE_TEXTURE_USE (1 << 3) +#define GP_STROKE_TEXTURE_STENCIL (1 << 4) +#define GP_STROKE_TEXTURE_PREMUL (1 << 5) +#define GP_STROKE_DOTS (1 << 6) +#define GP_FILL_TEXTURE_USE (1 << 10) +#define GP_FILL_TEXTURE_PREMUL (1 << 11) +#define GP_FILL_TEXTURE_CLIP (1 << 12) +#define GP_FILL_GRADIENT_USE (1 << 13) +#define GP_FILL_GRADIENT_RADIAL (1 << 14) +/* High bits are used to pass material ID to fragment shader. */ +#define GP_MATID_SHIFT 16 + +/* Multiline defines can crash blender with certain GPU drivers. */ +/* clang-format off */ +#define GP_FILL_FLAGS (GP_FILL_TEXTURE_USE | GP_FILL_TEXTURE_PREMUL | GP_FILL_TEXTURE_CLIP | GP_FILL_GRADIENT_USE | GP_FILL_GRADIENT_RADIAL) +/* clang-format on */ + +#define GP_FLAG_TEST(flag, val) (((flag) & (val)) != 0) + +/* Must match C declaration. */ +struct gpLight { + vec4 color_type; + vec4 right; + vec4 up; + vec4 forward; + vec4 position; + /* Please ensure 16 byte alignment (multiple of vec4). */ +}; + +#define spot_size right.w +#define spot_blend up.w + +#define GP_LIGHT_TYPE_POINT 0.0 +#define GP_LIGHT_TYPE_SPOT 1.0 +#define GP_LIGHT_TYPE_SUN 2.0 +#define GP_LIGHT_TYPE_AMBIENT 3.0 + +#ifdef GP_MATERIAL_BUFFER_LEN + +layout(std140) uniform gpMaterialBlock +{ + gpMaterial materials[GP_MATERIAL_BUFFER_LEN]; +}; + +#endif + +#ifdef GPENCIL_LIGHT_BUFFER_LEN + +layout(std140) uniform gpLightBlock +{ + gpLight lights[GPENCIL_LIGHT_BUFFER_LEN]; +}; + +#endif + +/* Must match eGPLayerBlendModes */ +#define MODE_REGULAR 0 +#define MODE_OVERLAY 1 +#define MODE_ADD 2 +#define MODE_SUB 3 +#define MODE_MULTIPLY 4 +#define MODE_DIVIDE 5 +#define MODE_OVERLAY_SECOND_PASS 999 + +void blend_mode_output( + int blend_mode, vec4 color, float opacity, out vec4 frag_color, out vec4 frag_revealage) +{ + switch (blend_mode) { + case MODE_REGULAR: + /* Reminder: Blending func is premult alpha blend (dst.rgba * (1 - src.a) + src.rgb).*/ + color *= opacity; + frag_color = color; + frag_revealage = vec4(0.0, 0.0, 0.0, color.a); + break; + case MODE_MULTIPLY: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + color.a *= opacity; + frag_revealage = frag_color = (1.0 - color.a) + color.a * color; + break; + case MODE_DIVIDE: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + color.a *= opacity; + frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18); + break; + case MODE_OVERLAY: + /* Reminder: Blending func is multiply blend (dst.rgba * src.rgba).*/ + /** + * We need to separate the overlay equation into 2 term (one mul and one add). + * This is the standard overlay equation (per channel): + * rtn = (src < 0.5) ? (2.0 * src * dst) : (1.0 - 2.0 * (1.0 - src) * (1.0 - dst)); + * We rewrite the second branch like this: + * rtn = 1 - 2 * (1 - src) * (1 - dst); + * rtn = 1 - 2 (1 - dst + src * dst - src); + * rtn = 1 - 2 (1 - dst * (1 - src) - src); + * rtn = 1 - 2 + dst * (2 - 2 * src) + 2 * src; + * rtn = (- 1 + 2 * src) + dst * (2 - 2 * src); + **/ + color = mix(vec4(0.5), color, color.a * opacity); + vec4 s = step(-0.5, -color); + frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0); + break; + case MODE_OVERLAY_SECOND_PASS: + /* Reminder: Blending func is additive blend (dst.rgba + src.rgba).*/ + color = mix(vec4(0.5), color, color.a * opacity); + frag_revealage = frag_color = (-1.0 + 2.0 * color) * step(-0.5, -color); + break; + case MODE_SUB: + case MODE_ADD: + /* Reminder: Blending func is additive / subtractive blend (dst.rgba +/- src.rgba).*/ + frag_color = color * color.a * opacity; + frag_revealage = vec4(0.0); + break; + } +} + +#ifdef GPU_VERTEX_SHADER +# define IN_OUT out +#else +# define IN_OUT in +#endif + +/* Shader interface. */ +IN_OUT vec4 finalColorMul; +IN_OUT vec4 finalColorAdd; +IN_OUT vec3 finalPos; +IN_OUT vec2 finalUvs; +noperspective IN_OUT float strokeThickness; +noperspective IN_OUT float strokeHardeness; +flat IN_OUT vec2 strokeAspect; +flat IN_OUT vec2 strokePt1; +flat IN_OUT vec2 strokePt2; +flat IN_OUT int matFlag; +flat IN_OUT float depth; + +#ifdef GPU_FRAGMENT_SHADER + +# define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) + +float stroke_round_cap_mask(vec2 p1, vec2 p2, vec2 aspect, float thickness, float hardfac) +{ + /* We create our own uv space to avoid issues with triangulation and linear + * interpolation artifacts. */ + vec2 line = p2.xy - p1.xy; + vec2 pos = gl_FragCoord.xy - p1.xy; + float line_len = length(line); + float half_line_len = line_len * 0.5; + /* Normalize */ + line = (line_len > 0.0) ? (line / line_len) : vec2(1.0, 0.0); + /* Create a uv space that englobe the whole segment into a capsule. */ + vec2 uv_end; + uv_end.x = max(abs(dot(line, pos) - half_line_len) - half_line_len, 0.0); + uv_end.y = dot(vec2(-line.y, line.x), pos); + /* Divide by stroke radius. */ + uv_end /= thickness; + uv_end *= aspect; + + float dist = clamp(1.0 - length(uv_end) * 2.0, 0.0, 1.0); + if (hardfac > 0.999) { + return step(1e-8, dist); + } + else { + /* Modulate the falloff profile */ + float hardness = 1.0 - hardfac; + dist = pow(dist, mix(0.01, 10.0, hardness)); + return smoothstep(0.0, 1.0, dist); + } +} + +#endif + +uniform vec2 sizeViewport; +uniform vec2 sizeViewportInv; + +/* Per Object */ +uniform bool strokeOrder3d; +uniform int gpMaterialOffset; +uniform float thicknessScale; +uniform float thicknessWorldScale; +#define thicknessIsScreenSpace (thicknessWorldScale < 0.0) +#define MATERIAL(m) materials[m + gpMaterialOffset] + +#ifdef GPU_VERTEX_SHADER + +/* Per Layer */ +uniform float thicknessOffset; +uniform float vertexColorOpacity; +uniform vec4 layerTint; +uniform float layerOpacity; /* Used for onion skin. */ +uniform float strokeIndexOffset = 0.0; + +/* All of these attribs are quad loaded the same way + * as GL_LINES_ADJACENCY would feed a geometry shader: + * - ma reference the previous adjacency point. + * - ma1 reference the current line first point. + * - ma2 reference the current line second point. + * - ma3 reference the next adjacency point. + * Note that we are rendering quad instances and not using any index buffer (except for fills). + */ +in vec4 ma; +in vec4 ma1; +in vec4 ma2; +in vec4 ma3; +# define strength1 ma1.y +# define strength2 ma2.y +# define stroke_id1 ma1.z +# define point_id1 ma1.w +/* Position contains thickness in 4th component. */ +in vec4 pos; /* Prev adj vert */ +in vec4 pos1; /* Current edge */ +in vec4 pos2; /* Current edge */ +in vec4 pos3; /* Next adj vert */ +# define thickness1 pos1.w +# define thickness2 pos2.w +/* xy is UV for fills, z is U of stroke, w is cosine of UV angle with sign of sine. */ +in vec4 uv1; +in vec4 uv2; + +in vec4 col1; +in vec4 col2; + +in vec4 fcol1; + +/* hard.x is aspect. */ +in vec2 hard1; +in vec2 hard2; +# define aspect1 hard1.x +# define aspect2 hard2.x + +void discard_vert() +{ + /* We set the vertex at the camera origin to generate 0 fragments. */ + gl_Position = vec4(0.0, 0.0, -3e36, 0.0); +} + +vec2 project_to_screenspace(vec4 v) +{ + return ((v.xy / v.w) * 0.5 + 0.5) * sizeViewport; +} + +vec2 rotate_90deg(vec2 v) +{ + /* Counter Clock-Wise. */ + return vec2(-v.y, v.x); +} + +mat4 model_matrix_get() +{ + return ModelMatrix; +} + +vec3 transform_point(mat4 m, vec3 v) +{ + return (m * vec4(v, 1.0)).xyz; +} + +vec2 safe_normalize(vec2 v) +{ + float len_sqr = dot(v, v); + if (len_sqr > 0.0) { + return v / sqrt(len_sqr); + } + else { + return vec2(1.0, 0.0); + } +} + +vec2 safe_normalize_len(vec2 v, out float len) +{ + len = sqrt(dot(v, v)); + if (len > 0.0) { + return v / len; + } + else { + return vec2(1.0, 0.0); + } +} + +float stroke_thickness_modulate(float thickness) +{ + /* Modify stroke thickness by object and layer factors.-*/ + thickness *= thicknessScale; + thickness += thicknessOffset; + thickness = max(1.0, thickness); + + if (thicknessIsScreenSpace) { + /* Multiply offset by view Z so that offset is constant in screenspace. + * (e.i: does not change with the distance to camera) */ + thickness *= gl_Position.w; + } + else { + /* World space point size. */ + thickness *= thicknessWorldScale * ProjectionMatrix[1][1] * sizeViewport.y; + } + return thickness; +} + +# ifdef GP_MATERIAL_BUFFER_LEN +void color_output(vec4 stroke_col, vec4 vert_col, float vert_strength, float mix_tex) +{ + /* Mix stroke with other colors. */ + vec4 mixed_col = stroke_col; + mixed_col.rgb = mix(mixed_col.rgb, vert_col.rgb, vert_col.a * vertexColorOpacity); + mixed_col.rgb = mix(mixed_col.rgb, layerTint.rgb, layerTint.a); + mixed_col.a *= vert_strength * layerOpacity; + /** + * This is what the fragment shader looks like. + * out = col * finalColorMul + col.a * finalColorAdd. + * finalColorMul is how much of the texture color to keep. + * finalColorAdd is how much of the mixed color to add. + * Note that we never add alpha. This is to keep the texture act as a stencil. + * We do however, modulate the alpha (reduce it). + **/ + /* We add the mixed color. This is 100% mix (no texture visible). */ + finalColorMul = vec4(mixed_col.aaa, mixed_col.a); + finalColorAdd = vec4(mixed_col.rgb * mixed_col.a, 0.0); + /* Then we blend according to the texture mix factor. + * Note that we keep the alpha modulation. */ + finalColorMul.rgb *= mix_tex; + finalColorAdd.rgb *= 1.0 - mix_tex; +} +# endif + +void stroke_vertex() +{ + int m = int(ma1.x); + bool is_dot = false; + bool is_squares = false; + +# ifdef GP_MATERIAL_BUFFER_LEN + if (m != -1.0) { + is_dot = GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_ALIGNMENT); + is_squares = !GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_DOTS); + } +# endif + + /* Special Case. Stroke with single vert are rendered as dots. Do not discard them. */ + if (!is_dot && ma.x == -1.0 && ma2.x == -1.0) { + is_dot = true; + is_squares = false; + } + + /* Enpoints, we discard the vertices. */ + if (ma1.x == -1.0 || (!is_dot && ma2.x == -1.0)) { + discard_vert(); + return; + } + + mat4 model_mat = model_matrix_get(); + + /* Avoid using a vertex attrib for quad positioning. */ + float x = float(gl_VertexID & 1) * 2.0 - 1.0; /* [-1..1] */ + float y = float(gl_VertexID & 2) - 1.0; /* [-1..1] */ + + bool use_curr = is_dot || (x == -1.0); + + vec3 wpos_adj = transform_point(model_mat, (use_curr) ? pos.xyz : pos3.xyz); + vec3 wpos1 = transform_point(model_mat, pos1.xyz); + vec3 wpos2 = transform_point(model_mat, pos2.xyz); + + vec4 ndc_adj = point_world_to_ndc(wpos_adj); + vec4 ndc1 = point_world_to_ndc(wpos1); + vec4 ndc2 = point_world_to_ndc(wpos2); + + gl_Position = (use_curr) ? ndc1 : ndc2; + finalPos = (use_curr) ? wpos1 : wpos2; + + vec2 ss_adj = project_to_screenspace(ndc_adj); + vec2 ss1 = project_to_screenspace(ndc1); + vec2 ss2 = project_to_screenspace(ndc2); + /* Screenspace Lines tangents. */ + float line_len; + vec2 line = safe_normalize_len(ss2 - ss1, line_len); + vec2 line_adj = safe_normalize((use_curr) ? (ss1 - ss_adj) : (ss_adj - ss2)); + + float thickness = abs((use_curr) ? thickness1 : thickness2); + thickness = stroke_thickness_modulate(thickness); + + finalUvs = vec2(x, y) * 0.5 + 0.5; + strokeHardeness = (use_curr) ? hard1.y : hard2.y; + + if (is_dot) { +# ifdef GP_MATERIAL_BUFFER_LEN + int alignement = MATERIAL(m).flag & GP_STROKE_ALIGNMENT; +# endif + + vec2 x_axis; +# ifdef GP_MATERIAL_BUFFER_LEN + if (alignement == GP_STROKE_ALIGNMENT_STROKE) { + x_axis = (ma2.x == -1.0) ? line_adj : line; + } + else if (alignement == GP_STROKE_ALIGNMENT_FIXED) { + /* Default for no-material drawing. */ + x_axis = vec2(1.0, 0.0); + } + else +# endif + { /* GP_STROKE_ALIGNMENT_OBJECT */ + vec4 ndc_x = point_world_to_ndc(wpos1 + model_mat[0].xyz); + vec2 ss_x = project_to_screenspace(ndc_x); + x_axis = safe_normalize(ss_x - ss1); + } + + /* Rotation: Encoded as Cos + Sin sign. */ + float rot_sin = sqrt(1.0 - uv1.w * uv1.w) * sign(uv1.w); + float rot_cos = abs(uv1.w); + x_axis = mat2(rot_cos, -rot_sin, rot_sin, rot_cos) * x_axis; + + vec2 y_axis = rotate_90deg(x_axis); + + strokeAspect.x = aspect1; + + if (strokeAspect.x > 1.0) { + strokeAspect.y = strokeAspect.x; + strokeAspect.x = 1.0; + } + else { + strokeAspect.x = 1.0 / strokeAspect.x; + strokeAspect.y = 1.0; + } + + x /= strokeAspect.x; + y /= strokeAspect.y; + + gl_Position.xy += (x * x_axis + y * y_axis) * sizeViewportInv.xy * thickness; + + strokePt1 = ss1; + strokePt2 = ss1 + x_axis * 0.5; + strokeThickness = (is_squares) ? 1e18 : (thickness / gl_Position.w); + } + else { + bool is_stroke_start = (ma.x == -1.0 && x == -1.0); + bool is_stroke_end = (ma3.x == -1.0 && x == 1.0); + + /* Mitter tangent vector. */ + vec2 miter_tan = safe_normalize(line_adj + line); + float miter_dot = dot(miter_tan, line_adj); + /* Break corners after a certain angle to avoid really thick corners. */ + const float miter_limit = 0.5; /* cos(60°) */ + bool miter_break = (miter_dot < miter_limit) || is_stroke_start || is_stroke_end; + miter_tan = (miter_break) ? line : (miter_tan / miter_dot); + + vec2 miter = rotate_90deg(miter_tan); + + strokePt1.xy = ss1; + strokePt2.xy = ss2; + strokeThickness = thickness / gl_Position.w; + strokeAspect = vec2(1.0); + + vec2 screen_ofs = miter * y; + + /* Reminder: we packed the cap flag into the sign of stength and thickness sign. */ + if ((is_stroke_start && strength1 > 0.0) || (is_stroke_end && thickness1 > 0.0) || + miter_break) { + screen_ofs += line * x; + } + + gl_Position.xy += screen_ofs * sizeViewportInv.xy * thickness; + + finalUvs.x = (use_curr) ? uv1.z : uv2.z; +# ifdef GP_MATERIAL_BUFFER_LEN + finalUvs.x *= MATERIAL(m).stroke_u_scale; +# endif + } + +# ifdef GP_MATERIAL_BUFFER_LEN + vec4 vert_col = (use_curr) ? col1 : col2; + float vert_strength = abs((use_curr) ? strength1 : strength2); + vec4 stroke_col = MATERIAL(m).stroke_color; + float mix_tex = MATERIAL(m).stroke_texture_mix; + + color_output(stroke_col, vert_col, vert_strength, mix_tex); + + matFlag = MATERIAL(m).flag & ~GP_FILL_FLAGS; +# endif + + if (strokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + depth = -1.0; + } +# ifdef GP_MATERIAL_BUFFER_LEN + else if (GP_FLAG_TEST(MATERIAL(m).flag, GP_STROKE_OVERLAP)) { + /* Use the index of the point as depth. + * This means the stroke can overlap itself. */ + depth = (point_id1 + strokeIndexOffset + 1.0) * 0.0000002; + } +# endif + else { + /* Use the index of first point of the stroke as depth. + * We render using a greater depth test this means the stroke + * cannot overlap itself. + * We offset by one so that the fill can be overlapped by its stroke. + * The offset is ok since we pad the strokes data because of adjacency infos. */ + depth = (stroke_id1 + strokeIndexOffset + 1.0) * 0.0000002; + } +} + +void fill_vertex() +{ + mat4 model_mat = model_matrix_get(); + + vec3 wpos = transform_point(model_mat, pos1.xyz); + gl_Position = point_world_to_ndc(wpos); + finalPos = wpos; + +# ifdef GP_MATERIAL_BUFFER_LEN + int m = int(ma1.x); + + vec4 fill_col = MATERIAL(m).fill_color; + float mix_tex = MATERIAL(m).fill_texture_mix; + + /* Special case: We don't modulate alpha in gradient mode. */ + if (GP_FLAG_TEST(MATERIAL(m).flag, GP_FILL_GRADIENT_USE)) { + fill_col.a = 1.0; + } + + /* Decode fill opacity. */ + vec4 fcol_decode = vec4(fcol1.rgb, floor(fcol1.a / 10.0)); + float fill_opacity = fcol1.a - (fcol_decode.a * 10); + fcol_decode.a /= 10000.0f; + + /* Apply opacity. */ + fill_col.a *= fill_opacity; + /* If factor is > 1 force opacity. */ + if (fill_opacity > 1.0) { + fill_col.a += fill_opacity - 1.0f; + } + + fill_col.a = clamp(fill_col.a, 0.0, 1.0); + + color_output(fill_col, fcol_decode, 1.0, mix_tex); + + matFlag = MATERIAL(m).flag & GP_FILL_FLAGS; + matFlag |= m << GP_MATID_SHIFT; + + vec2 loc = MATERIAL(m).fill_uv_offset.xy; + mat2x2 rot_scale = mat2x2(MATERIAL(m).fill_uv_rot_scale.xy, MATERIAL(m).fill_uv_rot_scale.zw); + finalUvs = rot_scale * uv1.xy + loc; +# endif + + strokeThickness = 1e18; + strokeAspect = vec2(1.0); + strokePt1 = strokePt2 = vec2(0.0); + + if (strokeOrder3d) { + /* Use the fragment depth (see fragment shader). */ + depth = -1.0; + /* We still offset the fills a little to avoid overlaps */ + gl_Position.z += 0.000002; + } + else { + /* Use the index of first point of the stroke as depth. */ + depth = (stroke_id1 + strokeIndexOffset) * 0.0000002; + } +} + +void gpencil_vertex() +{ + /* Trick to detect if a drawcall is stroke or fill. + * This does mean that we need to draw an empty stroke segment before starting + * to draw the real stroke segments. */ + bool is_fill = (gl_InstanceID == 0); + + if (!is_fill) { + stroke_vertex(); + } + else { + fill_vertex(); + } +} + +#endif diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl new file mode 100644 index 00000000000..71597197bd8 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_frag.glsl @@ -0,0 +1,17 @@ + +uniform sampler2D depthBuf; +uniform float strokeDepth2d; +uniform bool strokeOrder3d; + +noperspective in vec4 uvcoordsvar; + +void main() +{ + float depth = textureLod(depthBuf, uvcoordsvar.xy, 0).r; + if (strokeOrder3d) { + gl_FragDepth = depth; + } + else { + gl_FragDepth = (depth != 0.0) ? gl_FragCoord.z : 1.0; + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl new file mode 100644 index 00000000000..1e5a900f486 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_depth_merge_vert.glsl @@ -0,0 +1,14 @@ + +uniform vec4 gpModelMatrix[4]; + +noperspective out vec4 uvcoordsvar; + +void main() +{ + mat4 model_matrix = mat4(gpModelMatrix[0], gpModelMatrix[1], gpModelMatrix[2], gpModelMatrix[3]); + int v = gl_VertexID % 3; + float x = -1.0 + float((v & 1) << 2); + float y = -1.0 + float((v & 2) << 1); + gl_Position = ViewProjectionMatrix * (model_matrix * vec4(x, y, 0.0, 1.0)); + uvcoordsvar = vec4((gl_Position.xy / gl_Position.w + 1.0) * 0.5, 0.0, 0.0); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl deleted file mode 100644 index 6a2a4f68dc9..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl +++ /dev/null @@ -1,17 +0,0 @@ -in vec4 mColor; -in vec2 mTexCoord; -out vec4 fragColor; - -void main() -{ - vec2 centered = mTexCoord - vec2(0.5); - float dist_squared = dot(centered, centered); - const float rad_squared = 0.25; - - // round point with jaggy edges - if (dist_squared > rad_squared) { - discard; - } - - fragColor = mColor; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl deleted file mode 100644 index e0634a7d1a7..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl +++ /dev/null @@ -1,53 +0,0 @@ -uniform vec2 Viewport; - -layout(points) in; -layout(triangle_strip, max_vertices = 4) out; - -in vec4 finalColor[1]; -in float finalThickness[1]; - -out vec4 mColor; -out vec2 mTexCoord; - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - return vec2(vertex.xy / vertex.w) * Viewport; -} - -/* get zdepth value */ -float getZdepth(vec4 point) -{ - return min(-0.05, (point.z / point.w)); -} - -void main(void) -{ - vec4 P0 = gl_in[0].gl_Position; - vec2 sp0 = toScreenSpace(P0); - - float size = finalThickness[0]; - - /* generate the triangle strip */ - mTexCoord = vec2(0, 1); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x - size, sp0.y + size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 0); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x - size, sp0.y - size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(1, 1); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x + size, sp0.y + size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = vec2(1, 0); - mColor = finalColor[0]; - gl_Position = vec4(vec2(sp0.x + size, sp0.y - size) / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl deleted file mode 100644 index 57908f3251b..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl +++ /dev/null @@ -1,19 +0,0 @@ - -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float size; - -out vec4 finalColor; -out float finalThickness; - -void main() -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalColor = color; - finalThickness = size; - - /* Dirty fix waiting for new GPencil engine. */ - finalColor.rgb = pow(finalColor.rgb, vec3(2.2)); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl deleted file mode 100644 index 8285541e0b4..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl +++ /dev/null @@ -1,234 +0,0 @@ -uniform vec4 color2; -uniform int fill_type; -uniform float mix_factor; - -uniform float gradient_angle; -uniform float gradient_radius; -uniform float pattern_gridsize; -uniform vec2 gradient_scale; -uniform vec2 gradient_shift; - -uniform float texture_angle; -uniform vec2 texture_scale; -uniform vec2 texture_offset; -uniform int texture_mix; -uniform int texture_flip; -uniform float texture_opacity; -uniform int xraymode; -uniform int drawmode; -uniform float layer_opacity; - -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; -uniform int texture_clamp; - -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -/* keep this list synchronized with list in gpencil_draw_utils.c */ -#define SOLID 0 -#define GRADIENT 1 -#define RADIAL 2 -#define CHECKER 3 -#define TEXTURE 4 -#define PATTERN 5 - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -#define GP_DRAWMODE_2D 0 -#define GP_DRAWMODE_3D 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -in vec4 finalColor; -in vec2 texCoord_interp; -out vec4 fragColor; -#define texture2D texture - -void set_color(in vec4 color, - in vec4 color2, - in vec4 tcolor, - in float mixv, - in float factor, - in int tmix, - in int flip, - out vec4 ocolor) -{ - /* full color A */ - if (mixv == 1.0) { - if (tmix == 1) { - ocolor = (flip == 0) ? color : tcolor; - } - else { - ocolor = (flip == 0) ? color : color2; - } - } - /* full color B */ - else if (mixv == 0.0) { - if (tmix == 1) { - ocolor = (flip == 0) ? tcolor : color; - } - else { - ocolor = (flip == 0) ? color2 : color; - } - } - /* mix of colors */ - else { - if (tmix == 1) { - ocolor = (flip == 0) ? mix(color, tcolor, factor) : mix(tcolor, color, factor); - } - else { - ocolor = (flip == 0) ? mix(color, color2, factor) : mix(color2, color, factor); - } - } - ocolor.a *= layer_opacity; -} - -void main() -{ - vec2 t_center = vec2(0.5, 0.5); - mat2 matrot_tex = mat2( - cos(texture_angle), -sin(texture_angle), sin(texture_angle), cos(texture_angle)); - vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + texture_offset; - vec4 tmp_color; - tmp_color = (texture_clamp == 0) ? - texture_read_as_srgb( - myTexture, myTexturePremultiplied, rot_tex * texture_scale) : - texture_read_as_srgb( - myTexture, myTexturePremultiplied, clamp(rot_tex * texture_scale, 0.0, 1.0)); - vec4 text_color = vec4(tmp_color[0], tmp_color[1], tmp_color[2], tmp_color[3] * texture_opacity); - vec4 checker_color; - - /* wireframe with x-ray discard */ - if ((viewport_xray == 1) && (shading_type[0] == OB_WIRE)) { - discard; - } - - /* solid fill */ - if (fill_type == SOLID) { - fragColor = finalColor; - } - else { - vec2 center = vec2(0.5, 0.5) + gradient_shift; - mat2 matrot = mat2( - cos(gradient_angle), -sin(gradient_angle), sin(gradient_angle), cos(gradient_angle)); - vec2 rot = (((matrot * (texCoord_interp - center)) + center) * gradient_scale) + - gradient_shift; - /* gradient */ - if (fill_type == GRADIENT) { - set_color(finalColor, - color2, - text_color, - mix_factor, - rot.x - mix_factor + 0.5, - texture_mix, - texture_flip, - fragColor); - } - /* radial gradient */ - if (fill_type == RADIAL) { - float in_rad = gradient_radius * mix_factor; - float ex_rad = gradient_radius - in_rad; - float intensity = 0; - float distance = length((center - texCoord_interp) * gradient_scale); - if (distance > gradient_radius) { - discard; - } - if (distance > in_rad) { - intensity = clamp(((distance - in_rad) / ex_rad), 0.0, 1.0); - } - set_color(finalColor, - color2, - text_color, - mix_factor, - intensity, - texture_mix, - texture_flip, - fragColor); - } - /* Checkerboard */ - if (fill_type == CHECKER) { - vec2 pos = rot / pattern_gridsize; - if ((fract(pos.x) < 0.5 && fract(pos.y) < 0.5) || - (fract(pos.x) > 0.5 && fract(pos.y) > 0.5)) { - checker_color = (texture_flip == 0) ? finalColor : color2; - } - else { - checker_color = (texture_flip == 0) ? color2 : finalColor; - } - /* mix with texture */ - fragColor = (texture_mix == 1) ? mix(checker_color, text_color, mix_factor) : checker_color; - fragColor.a *= layer_opacity; - } - /* texture */ - if (fill_type == TEXTURE) { - fragColor = (texture_mix == 1) ? mix(text_color, finalColor, mix_factor) : text_color; - fragColor.a *= layer_opacity; - } - /* pattern */ - if (fill_type == PATTERN) { - fragColor = finalColor; - fragColor.a = min(text_color.a, finalColor.a) * layer_opacity; - } - } - - /* set zdepth */ - if (xraymode == GP_XRAY_FRONT) { - gl_FragDepth = min(-0.05, (gl_FragCoord.z / gl_FragCoord.w)); - } - else if (xraymode == GP_XRAY_3DSPACE) { - /* if 3D mode, move slightly the fill to avoid z-fighting between stroke and fill on same - * stroke */ - if (drawmode == GP_DRAWMODE_3D) { - gl_FragDepth = gl_FragCoord.z * 1.0001; - } - else { - gl_FragDepth = gl_FragCoord.z; - } - } - else { - gl_FragDepth = 0.000001; - } - - /* if wireframe override colors */ - if (shading_type[0] == OB_WIRE) { - fragColor = wire_color; - } - - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - fragColor = wire_color; - } - if (viewport_xray == 1) { - fragColor.a *= 0.5; - } - } - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl deleted file mode 100644 index 263dc570423..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl +++ /dev/null @@ -1,16 +0,0 @@ - -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in vec2 texCoord; - -out vec4 finalColor; -out vec2 texCoord_interp; - -void main(void) -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalColor = color; - texCoord_interp = texCoord; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl new file mode 100644 index 00000000000..8c2032f834a --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_frag.glsl @@ -0,0 +1,126 @@ + +uniform sampler2D gpFillTexture; +uniform sampler2D gpStrokeTexture; +uniform sampler2D gpSceneDepthTexture; +uniform sampler2D gpMaskTexture; +uniform vec3 gpNormal; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 revealColor; + +float length_squared(vec2 v) +{ + return dot(v, v); +} +float length_squared(vec3 v) +{ + return dot(v, v); +} + +vec3 gpencil_lighting(void) +{ + vec3 light_accum = vec3(0.0); + for (int i = 0; i < GPENCIL_LIGHT_BUFFER_LEN; i++) { + if (lights[i].color_type.x == -1.0) { + break; + } + vec3 L = lights[i].position.xyz - finalPos; + float vis = 1.0; + /* Spot Attenuation. */ + if (lights[i].color_type.w == GP_LIGHT_TYPE_SPOT) { + mat3 rot_scale = mat3(lights[i].right.xyz, lights[i].up.xyz, lights[i].forward.xyz); + vec3 local_L = rot_scale * L; + local_L /= abs(local_L.z); + float ellipse = inversesqrt(length_squared(local_L)); + vis *= smoothstep(0.0, 1.0, (ellipse - lights[i].spot_size) / lights[i].spot_blend); + /* Also mask +Z cone. */ + vis *= step(0.0, local_L.z); + } + /* Inverse square decay. Skip for suns. */ + float L_len_sqr = length_squared(L); + if (lights[i].color_type.w < GP_LIGHT_TYPE_SUN) { + vis /= L_len_sqr; + } + else { + L = lights[i].forward.xyz; + L_len_sqr = 1.0; + } + /* Lambertian falloff */ + if (lights[i].color_type.w != GP_LIGHT_TYPE_AMBIENT) { + L /= sqrt(L_len_sqr); + vis *= clamp(dot(gpNormal, L), 0.0, 1.0); + } + light_accum += vis * lights[i].color_type.rgb; + } + /* Clamp to avoid NaNs. */ + return clamp(light_accum, 0.0, 1e10); +} + +void main() +{ + vec4 col; + if (GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_USE)) { + bool premul = GP_FLAG_TEST(matFlag, GP_STROKE_TEXTURE_PREMUL); + col = texture_read_as_linearrgb(gpStrokeTexture, premul, finalUvs); + } + else if (GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_USE)) { + bool use_clip = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_CLIP); + vec2 uvs = (use_clip) ? clamp(finalUvs, 0.0, 1.0) : finalUvs; + bool premul = GP_FLAG_TEST(matFlag, GP_FILL_TEXTURE_PREMUL); + col = texture_read_as_linearrgb(gpFillTexture, premul, uvs); + } + else if (GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_USE)) { + bool radial = GP_FLAG_TEST(matFlag, GP_FILL_GRADIENT_RADIAL); + float fac = clamp(radial ? length(finalUvs * 2.0 - 1.0) : finalUvs.x, 0.0, 1.0); + int matid = matFlag >> GP_MATID_SHIFT; + col = mix(MATERIAL(matid).fill_color, MATERIAL(matid).fill_mix_color, fac); + } + else /* SOLID */ { + col = vec4(1.0); + } + col.rgb *= col.a; + + /* Composite all other colors on top of texture color. + * Everything is premult by col.a to have the stencil effect. */ + fragColor = col * finalColorMul + col.a * finalColorAdd; + + fragColor.rgb *= gpencil_lighting(); + + fragColor *= stroke_round_cap_mask( + strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness); + + /* For compatibility with colored alpha buffer. + * Note that we are limited to mono-chromatic alpha blending here + * because of the blend equation and the limit of 1 color target + * when using custom color blending. */ + revealColor = vec4(0.0, 0.0, 0.0, fragColor.a); + + if (fragColor.a < 0.001) { + discard; + } + + /* Manual depth test */ + vec2 uvs = gl_FragCoord.xy / vec2(textureSize(gpSceneDepthTexture, 0).xy); + float scene_depth = texture(gpSceneDepthTexture, uvs).r; + if (gl_FragCoord.z > scene_depth) { + discard; + } + + /* FIXME(fclem) Grrr. This is bad for performance but it's the easiest way to not get + * depth written where the mask obliterate the layer. */ + float mask = texture(gpMaskTexture, uvs).r; + if (mask < 0.001) { + discard; + } + + /* We override the fragment depth using the fragment shader to ensure a constant value. + * This has a cost as the depth test cannot happen early. + * We could do this in the vertex shader but then perspective interpolation of uvs and + * fragment clipping gets really complicated. */ + if (depth >= 0.0) { + gl_FragDepth = depth; + } + else { + gl_FragDepth = gl_FragCoord.z; + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl new file mode 100644 index 00000000000..6fbc7f47dac --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_layer_blend_frag.glsl @@ -0,0 +1,31 @@ + +uniform sampler2D colorBuf; +uniform sampler2D revealBuf; +uniform sampler2D maskBuf; +uniform int blendMode; +uniform float blendOpacity; + +in vec4 uvcoordsvar; + +/* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +void main() +{ + vec4 color; + + /* Remember, this is associated alpha (aka. premult). */ + color.rgb = textureLod(colorBuf, uvcoordsvar.xy, 0).rgb; + /* Stroke only render mono-chromatic revealage. We convert to alpha. */ + color.a = 1.0 - textureLod(revealBuf, uvcoordsvar.xy, 0).r; + + float mask = textureLod(maskBuf, uvcoordsvar.xy, 0).r; + mask *= blendOpacity; + + fragColor = vec4(1.0, 0.0, 1.0, 1.0); + fragRevealage = vec4(1.0, 0.0, 1.0, 1.0); + + blend_mode_output(blendMode, color, mask, fragColor, fragRevealage); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl new file mode 100644 index 00000000000..b21b4147087 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_mask_invert_frag.glsl @@ -0,0 +1,11 @@ + +in vec4 uvcoordsvar; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +void main() +{ + /* Blend mode does the inversion. */ + fragRevealage = fragColor = vec4(1.0); +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl deleted file mode 100644 index 1d1cd6349dc..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl +++ /dev/null @@ -1,9 +0,0 @@ -uniform vec3 color; -uniform float opacity; - -out vec4 FragColor; - -void main() -{ - FragColor = vec4(color, 1.0 - opacity); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl deleted file mode 100644 index d79b8fb4d8a..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl +++ /dev/null @@ -1,126 +0,0 @@ -uniform int color_type; -uniform int mode; -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; - -uniform float gradient_f; -uniform vec2 gradient_s; - -uniform vec4 colormix; -uniform float mix_stroke_factor; -uniform int shading_type[2]; - -in vec4 mColor; -in vec2 mTexCoord; -out vec4 fragColor; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -#define texture2D texture - -#define GPENCIL_MODE_LINE 0 -#define GPENCIL_MODE_DOTS 1 -#define GPENCIL_MODE_BOX 2 - -/* keep this list synchronized with list in gpencil_engine.h */ -#define GPENCIL_COLOR_SOLID 0 -#define GPENCIL_COLOR_TEXTURE 1 -#define GPENCIL_COLOR_PATTERN 2 - -#define OB_SOLID 3 -#define V3D_SHADING_TEXTURE_COLOR 3 - -bool no_texture = (shading_type[0] == OB_SOLID) && (shading_type[1] != V3D_SHADING_TEXTURE_COLOR); - -/* Function to check the point inside ellipse */ -float check_ellipse_point(vec2 pt, vec2 radius) -{ - float p = (pow(pt.x, 2) / pow(radius.x, 2)) + (pow(pt.y, 2) / pow(radius.y, 2)); - - return p; -} - -/* Function to check the point inside box */ -vec2 check_box_point(vec2 pt, vec2 radius) -{ - vec2 rtn; - rtn.x = abs(pt.x) / radius.x; - rtn.y = abs(pt.y) / radius.y; - - return rtn; -} - -void main() -{ - vec2 centered = mTexCoord - vec2(0.5); - float ellip = check_ellipse_point(centered, vec2(gradient_s / 2.0)); - vec2 box; - - if (mode != GPENCIL_MODE_BOX) { - if (ellip > 1.0) { - discard; - } - } - else { - box = check_box_point(centered, vec2(gradient_s / 2.0)); - if ((box.x > 1.0) || (box.y > 1.0)) { - discard; - } - } - - /* Solid */ - if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) { - fragColor = mColor; - } - /* texture */ - if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); - if (mix_stroke_factor > 0.0) { - fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor); - fragColor.a = text_color.a; - } - else { - fragColor = text_color; - } - - /* mult both alpha factor to use strength factor with texture */ - fragColor.a = min(fragColor.a * mColor.a, fragColor.a); - } - /* pattern */ - if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) { - vec4 text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); - fragColor = mColor; - /* mult both alpha factor to use strength factor with color alpha limit */ - fragColor.a = min(text_color.a * mColor.a, mColor.a); - } - - if (gradient_f < 1.0) { - float dist = length(centered) * 2.0; - float decay = dist * (1.0 - gradient_f) * fragColor.a; - fragColor.a = clamp(fragColor.a - decay, 0.0, 1.0); - if (mode == GPENCIL_MODE_DOTS) { - fragColor.a = fragColor.a * (1.0 - ellip); - } - } - - if (fragColor.a < 0.0035) { - discard; - } - - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl deleted file mode 100644 index a2f4c1f9b15..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl +++ /dev/null @@ -1,142 +0,0 @@ -uniform vec2 Viewport; -uniform int xraymode; -uniform int alignment_mode; - -layout(points) in; -layout(triangle_strip, max_vertices = 4) out; - -in vec4 finalColor[1]; -in float finalThickness[1]; -in vec2 finaluvdata[1]; -in vec4 finalprev_pos[1]; - -out vec4 mColor; -out vec2 mTexCoord; - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -#define M_PI 3.14159265358979323846 /* pi */ -#define M_2PI 6.28318530717958647692 /* 2*pi */ -#define FALSE 0 - -/* keep this definition equals to GP_STYLE_FOLLOW_FIXED value */ -#define FIXED 2 - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - return vec2(vertex.xy / vertex.w) * Viewport; -} - -/* get zdepth value */ -float getZdepth(vec4 point) -{ - if (xraymode == GP_XRAY_FRONT) { - return min(-0.05, (point.z / point.w)); - } - if (xraymode == GP_XRAY_3DSPACE) { - return (point.z / point.w); - } - - /* in front by default */ - return 0.000001; -} - -vec2 rotateUV(vec2 uv, float angle) -{ - /* translate center of rotation to the center of texture */ - vec2 new_uv = uv - vec2(0.5f, 0.5f); - vec2 rot_uv; - rot_uv.x = new_uv.x * cos(angle) - new_uv.y * sin(angle); - rot_uv.y = new_uv.y * cos(angle) + new_uv.x * sin(angle); - return rot_uv + vec2(0.5f, 0.5f); -} - -vec2 rotatePoint(vec2 center, vec2 point, float angle) -{ - /* translate center of rotation to the center */ - vec2 new_point = point - center; - vec2 rot_point; - rot_point.x = new_point.x * cos(angle) - new_point.y * sin(angle); - rot_point.y = new_point.y * cos(angle) + new_point.x * sin(angle); - return rot_point + center; -} - -/* Calculate angle of the stroke using previous point as reference. - * The angle is calculated using the x axis (1, 0) as 0 degrees */ -float getAngle(vec2 pt0, vec2 pt1) -{ - /* do not rotate one point only (no reference to rotate) */ - if (pt0 == pt1) { - return 0.0; - } - - if (alignment_mode == FIXED) { - return 0.0; - } - - /* default horizontal line (x-axis) in screen space */ - vec2 v0 = vec2(1.0, 0.0); - - /* vector of direction */ - vec2 vn = vec2(normalize(pt1 - pt0)); - - /* angle signed (function ported from angle_signed_v2v2) */ - float perp_dot = (v0[1] * vn[0]) - (v0[0] * vn[1]); - float angle = atan(perp_dot, dot(v0, vn)); - - /* get full circle rotation */ - if (angle > 0.0) { - angle = M_PI + (M_PI - angle); - } - else { - angle *= -1.0; - } - - return angle; -} - -void main(void) -{ - /* receive points */ - vec4 P0 = gl_in[0].gl_Position; - vec2 sp0 = toScreenSpace(P0); - - vec4 P1 = finalprev_pos[0]; - vec2 sp1 = toScreenSpace(P1); - vec2 point; - - float size = finalThickness[0]; - vec2 center = vec2(sp0.x, sp0.y); - - /* get angle of stroke to rotate texture */ - float angle = getAngle(sp0, sp1); - - /* generate the triangle strip */ - mTexCoord = rotateUV(vec2(0, 1), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x - size, sp0.y + size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(0, 0), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x - size, sp0.y - size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(1, 1), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x + size, sp0.y + size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - mTexCoord = rotateUV(vec2(1, 0), finaluvdata[0].y); - mColor = finalColor[0]; - point = rotatePoint(center, vec2(sp0.x + size, sp0.y - size), angle); - gl_Position = vec4(point / Viewport, getZdepth(P0), 1.0); - EmitVertex(); - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl deleted file mode 100644 index 33d7d714231..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl +++ /dev/null @@ -1,66 +0,0 @@ - -uniform float pixsize; /* rv3d->pixsize */ -uniform int keep_size; -uniform float objscale; -uniform float pixfactor; -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float thickness; -in vec2 uvdata; -in vec3 prev_pos; - -out vec4 finalColor; -out float finalThickness; -out vec2 finaluvdata; -out vec4 finalprev_pos; - -#define TRUE 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -void main() -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalprev_pos = point_world_to_ndc((gpModelMatrix * vec4(prev_pos, 1.0)).xyz); - finalColor = color; - - if (keep_size == TRUE) { - finalThickness = thickness; - } - else { - float size = (ProjectionMatrix[3][3] == 0.0) ? (thickness / (gl_Position.z * defaultpixsize)) : - (thickness / defaultpixsize); - finalThickness = max(size * objscale, 0.5); /* set a minimum size */ - } - - /* for wireframe override size and color */ - if (shading_type[0] == OB_WIRE) { - finalThickness = 2.0; - finalColor = wire_color; - } - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - finalColor = wire_color; - } - if (viewport_xray == 1) { - finalColor.a *= 0.5; - } - } - - finaluvdata = uvdata; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl deleted file mode 100644 index 2f4429a858f..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl +++ /dev/null @@ -1,15 +0,0 @@ -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - float stroke_depth = texelFetch(strokeDepth, uv, 0).r; - vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba; - - FragColor = stroke_color; - gl_FragDepth = stroke_depth; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl deleted file mode 100644 index 0f1665b73c2..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl +++ /dev/null @@ -1,110 +0,0 @@ -uniform int color_type; -uniform sampler2D myTexture; -uniform bool myTexturePremultiplied; - -uniform float gradient_f; - -uniform vec4 colormix; -uniform float mix_stroke_factor; -uniform int shading_type[2]; - -uniform int fade_layer; -uniform float fade_layer_factor; -uniform bool fade_ob; -uniform vec3 fade_color; -uniform float fade_ob_factor; - -in vec4 mColor; -in vec2 mTexCoord; -in vec2 uvfac; - -out vec4 fragColor; - -#define texture2D texture - -/* keep this list synchronized with list in gpencil_engine.h */ -#define GPENCIL_COLOR_SOLID 0 -#define GPENCIL_COLOR_TEXTURE 1 -#define GPENCIL_COLOR_PATTERN 2 - -#define ENDCAP 1.0 - -#define OB_SOLID 3 -#define V3D_SHADING_TEXTURE_COLOR 3 - -bool no_texture = (shading_type[0] == OB_SOLID) && (shading_type[1] != V3D_SHADING_TEXTURE_COLOR); - -void main() -{ - - vec4 tColor = vec4(mColor); - /* if uvfac[1] == 1, then encap */ - if (uvfac[1] == ENDCAP) { - vec2 center = vec2(uvfac[0], 0.5); - float dist = length(mTexCoord - center); - if (dist > 0.50) { - discard; - } - } - - if ((color_type == GPENCIL_COLOR_SOLID) || (no_texture)) { - fragColor = tColor; - } - - /* texture for endcaps */ - vec4 text_color; - if (uvfac[1] == ENDCAP) { - text_color = texture_read_as_srgb( - myTexture, myTexturePremultiplied, vec2(mTexCoord.x, mTexCoord.y)); - } - else { - text_color = texture_read_as_srgb(myTexture, myTexturePremultiplied, mTexCoord); - } - - /* texture */ - if ((color_type == GPENCIL_COLOR_TEXTURE) && (!no_texture)) { - if (mix_stroke_factor > 0.0) { - fragColor.rgb = mix(text_color.rgb, colormix.rgb, mix_stroke_factor); - fragColor.a = text_color.a; - } - else { - fragColor = text_color; - } - - /* mult both alpha factor to use strength factor */ - fragColor.a = min(fragColor.a * tColor.a, fragColor.a); - } - /* pattern */ - if ((color_type == GPENCIL_COLOR_PATTERN) && (!no_texture)) { - fragColor = tColor; - /* mult both alpha factor to use strength factor with color alpha limit */ - fragColor.a = min(text_color.a * tColor.a, tColor.a); - } - - /* gradient */ - /* keep this disabled while the line glitch bug exists - if (gradient_f < 1.0) { - float d = abs(mTexCoord.y - 0.5) * (1.1 - gradient_f); - float alpha = 1.0 - clamp((fragColor.a - (d * 2.0)), 0.03, 1.0); - fragColor.a = smoothstep(fragColor.a, 0.0, alpha); - - } - */ - - if (fragColor.a < 0.0035) { - discard; - } - - /* Apply paper opacity */ - if (fade_layer == 1) { - /* Layer is below, mix with background. */ - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_layer_factor); - } - else if (fade_layer == 2) { - /* Layer is above, change opacity. */ - fragColor.a *= fade_layer_factor; - } - else if (fade_ob == true) { - fragColor.rgb = mix(fade_color.rgb, fragColor.rgb, fade_ob_factor); - } -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl deleted file mode 100644 index 3300514dd13..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl +++ /dev/null @@ -1,264 +0,0 @@ -uniform vec2 Viewport; -uniform int xraymode; -uniform int color_type; -uniform int caps_mode[2]; - -layout(lines_adjacency) in; -layout(triangle_strip, max_vertices = 13) out; - -in vec4 finalColor[4]; -in float finalThickness[4]; -in vec2 finaluvdata[4]; - -out vec4 mColor; -out vec2 mTexCoord; -out vec2 uvfac; - -#define GP_XRAY_FRONT 0 -#define GP_XRAY_3DSPACE 1 - -/* keep this list synchronized with list in gpencil_engine.h */ -#define GPENCIL_COLOR_SOLID 0 -#define GPENCIL_COLOR_TEXTURE 1 -#define GPENCIL_COLOR_PATTERN 2 - -#define GPENCIL_FLATCAP 1 - -/* project 3d point to 2d on screen space */ -vec2 toScreenSpace(vec4 vertex) -{ - return vec2(vertex.xy / vertex.w) * Viewport; -} - -/* get zdepth value */ -float getZdepth(vec4 point) -{ - if (xraymode == GP_XRAY_FRONT) { - return min(-0.05, (point.z / point.w)); - } - if (xraymode == GP_XRAY_3DSPACE) { - return (point.z / point.w); - } - - /* in front by default */ - return 0.000001; -} - -/* check equality but with a small tolerance */ -bool is_equal(vec4 p1, vec4 p2) -{ - float limit = 0.0001; - float x = abs(p1.x - p2.x); - float y = abs(p1.y - p2.y); - float z = abs(p1.z - p2.z); - - if ((x < limit) && (y < limit) && (z < limit)) { - return true; - } - - return false; -} - -void main(void) -{ - float MiterLimit = 0.75; - uvfac = vec2(0.0, 0.0); - - /* receive 4 points */ - vec4 P0 = gl_in[0].gl_Position; - vec4 P1 = gl_in[1].gl_Position; - vec4 P2 = gl_in[2].gl_Position; - vec4 P3 = gl_in[3].gl_Position; - - /* get the four vertices passed to the shader */ - vec2 sp0 = toScreenSpace(P0); // start of previous segment - vec2 sp1 = toScreenSpace(P1); // end of previous segment, start of current segment - vec2 sp2 = toScreenSpace(P2); // end of current segment, start of next segment - vec2 sp3 = toScreenSpace(P3); // end of next segment - - /* culling outside viewport */ - vec2 area = Viewport * 4.0; - if (sp1.x < -area.x || sp1.x > area.x) { - return; - } - if (sp1.y < -area.y || sp1.y > area.y) { - return; - } - if (sp2.x < -area.x || sp2.x > area.x) { - return; - } - if (sp2.y < -area.y || sp2.y > area.y) { - return; - } - - /* culling behind camera */ - if (P1.w < 0 || P2.w < 0) { - return; - } - - /* determine the direction of each of the 3 segments (previous, current, next) */ - vec2 v0 = normalize(sp1 - sp0); - vec2 v1 = normalize(sp2 - sp1); - vec2 v2 = normalize(sp3 - sp2); - - /* determine the normal of each of the 3 segments (previous, current, next) */ - vec2 n0 = vec2(-v0.y, v0.x); - vec2 n1 = vec2(-v1.y, v1.x); - vec2 n2 = vec2(-v2.y, v2.x); - - /* determine miter lines by averaging the normals of the 2 segments */ - vec2 miter_a = normalize(n0 + n1); // miter at start of current segment - vec2 miter_b = normalize(n1 + n2); // miter at end of current segment - - /* determine the length of the miter by projecting it onto normal and then inverse it */ - float an1 = dot(miter_a, n1); - float bn1 = dot(miter_b, n2); - if (an1 == 0) { - an1 = 1; - } - if (bn1 == 0) { - bn1 = 1; - } - float length_a = finalThickness[1] / an1; - float length_b = finalThickness[2] / bn1; - if (length_a <= 0.0) { - length_a = 0.01; - } - if (length_b <= 0.0) { - length_b = 0.01; - } - - /* prevent excessively long miters at sharp corners */ - if (dot(v0, v1) < -MiterLimit) { - miter_a = n1; - length_a = finalThickness[1]; - - /* close the gap */ - if (dot(v0, n1) > 0) { - mTexCoord = vec2(0, 0); - mColor = finalColor[1]; - gl_Position = vec4((sp1 + finalThickness[1] * n0) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 0); - mColor = finalColor[1]; - gl_Position = vec4((sp1 + finalThickness[1] * n1) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 0.5); - mColor = finalColor[1]; - gl_Position = vec4(sp1 / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - EndPrimitive(); - } - else { - mTexCoord = vec2(0, 1); - mColor = finalColor[1]; - gl_Position = vec4((sp1 - finalThickness[1] * n1) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 1); - mColor = finalColor[1]; - gl_Position = vec4((sp1 - finalThickness[1] * n0) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0, 0.5); - mColor = finalColor[1]; - gl_Position = vec4(sp1 / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - EndPrimitive(); - } - } - - if (dot(v1, v2) < -MiterLimit) { - miter_b = n1; - length_b = finalThickness[2]; - } - - /* generate the start endcap */ - if ((caps_mode[0] != GPENCIL_FLATCAP) && is_equal(P0, P2)) { - vec4 cap_color = finalColor[1]; - - mTexCoord = vec2(2.0, 0.5); - mColor = cap_color; - vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0.0, -0.5); - mColor = cap_color; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 - (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = vec2(0.0, 1.5); - mColor = cap_color; - uvfac = vec2(0.0, 1.0); - gl_Position = vec4((sp1 + (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - } - - float y_a = 0.0; - float y_b = 1.0; - - /* invert uv (vertical) */ - if (finaluvdata[2].x > 1.0) { - if ((finaluvdata[1].y != 0.0) && (finaluvdata[2].y != 0.0)) { - float d = ceil(finaluvdata[2].x) - 1.0; - if (floor(d / 2.0) == (d / 2.0)) { - y_a = 1.0; - y_b = 0.0; - } - } - } - /* generate the triangle strip */ - uvfac = vec2(0.0, 0.0); - mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 0) : vec2(finaluvdata[1].x, y_a); - mColor = finalColor[1]; - gl_Position = vec4((sp1 + length_a * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 1) : vec2(finaluvdata[1].x, y_b); - mColor = finalColor[1]; - gl_Position = vec4((sp1 - length_a * miter_a) / Viewport, getZdepth(P1), 1.0); - EmitVertex(); - - mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(1, 0) : vec2(finaluvdata[2].x, y_a); - mColor = finalColor[2]; - gl_Position = vec4((sp2 + length_b * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(1, 1) : vec2(finaluvdata[2].x, y_b); - mColor = finalColor[2]; - gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - /* generate the end endcap */ - if ((caps_mode[1] != GPENCIL_FLATCAP) && is_equal(P1, P3) && (finaluvdata[2].x > 0)) { - vec4 cap_color = finalColor[2]; - - mTexCoord = vec2(finaluvdata[2].x, 1.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - mTexCoord = vec2(finaluvdata[2].x, -0.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - gl_Position = vec4((sp2 - (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - - mTexCoord = vec2(finaluvdata[2].x + 2, 0.5); - mColor = cap_color; - uvfac = vec2(finaluvdata[2].x, 1.0); - vec2 svn2 = normalize(sp2 - sp1) * length_b * 4.0; - gl_Position = vec4((sp2 + svn2) / Viewport, getZdepth(P2), 1.0); - EmitVertex(); - } - - EndPrimitive(); -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl deleted file mode 100644 index 8df08f0bf68..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl +++ /dev/null @@ -1,63 +0,0 @@ - -uniform float pixsize; /* rv3d->pixsize */ -uniform int keep_size; -uniform float objscale; -uniform float pixfactor; -uniform int viewport_xray; -uniform int shading_type[2]; -uniform vec4 wire_color; -uniform mat4 gpModelMatrix; - -in vec3 pos; -in vec4 color; -in float thickness; -in vec2 uvdata; - -out vec4 finalColor; -out float finalThickness; -out vec2 finaluvdata; - -#define TRUE 1 - -#define OB_WIRE 2 -#define OB_SOLID 3 - -#define V3D_SHADING_MATERIAL_COLOR 0 -#define V3D_SHADING_TEXTURE_COLOR 3 -#define V3D_SHADING_VERTEX_COLOR 5 - -float defaultpixsize = pixsize * (1000.0 / pixfactor); - -void main(void) -{ - gl_Position = point_world_to_ndc((gpModelMatrix * vec4(pos, 1.0)).xyz); - finalColor = color; - - if (keep_size == TRUE) { - finalThickness = thickness; - } - else { - float size = (ProjectionMatrix[3][3] == 0.0) ? (thickness / (gl_Position.z * defaultpixsize)) : - (thickness / defaultpixsize); - finalThickness = max(size * objscale, 1.0); - } - - /* for wireframe override size and color */ - if (shading_type[0] == OB_WIRE) { - finalThickness = 1.0; - finalColor = wire_color; - } - /* for solid override color */ - if (shading_type[0] == OB_SOLID) { - if ((shading_type[1] != V3D_SHADING_MATERIAL_COLOR) && - (shading_type[1] != V3D_SHADING_TEXTURE_COLOR) && - (shading_type[1] != V3D_SHADING_VERTEX_COLOR)) { - finalColor = wire_color; - } - if (viewport_xray == 1) { - finalColor.a *= 0.5; - } - } - - finaluvdata = uvdata; -} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl new file mode 100644 index 00000000000..c6cfee5ef2d --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -0,0 +1,5 @@ + +void main() +{ + gpencil_vertex(); +} \ No newline at end of file diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl new file mode 100644 index 00000000000..503248558ad --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl @@ -0,0 +1,354 @@ + +uniform sampler2D colorBuf; +uniform sampler2D revealBuf; + +in vec4 uvcoordsvar; + +/* Reminder: This is considered SRC color in blend equations. + * Same operation on all buffers. */ +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 fragRevealage; + +float gaussian_weight(float x) +{ + return exp(-x * x / (2.0 * 0.35 * 0.35)); +} + +#if defined(COMPOSITE) + +uniform bool isFirstPass; + +void main() +{ + if (isFirstPass) { + /* Blend mode is multiply. */ + fragColor.rgb = fragRevealage.rgb = texture(revealBuf, uvcoordsvar.xy).rgb; + fragColor.a = fragRevealage.a = 1.0; + } + else { + /* Blend mode is additive. */ + fragRevealage = vec4(0.0); + fragColor.rgb = texture(colorBuf, uvcoordsvar.xy).rgb; + fragColor.a = 0.0; + } +} + +#elif defined(COLORIZE) + +uniform vec3 lowColor; +uniform vec3 highColor; +uniform float factor; +uniform int mode; + +const mat3 sepia_mat = mat3( + vec3(0.393, 0.349, 0.272), vec3(0.769, 0.686, 0.534), vec3(0.189, 0.168, 0.131)); + +# define MODE_GRAYSCALE 0 +# define MODE_SEPIA 1 +# define MODE_DUOTONE 2 +# define MODE_CUSTOM 3 +# define MODE_TRANSPARENT 4 + +void main() +{ + fragColor = texture(colorBuf, uvcoordsvar.xy); + fragRevealage = texture(revealBuf, uvcoordsvar.xy); + + float luma = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.723)); + + /* No blending. */ + switch (mode) { + case MODE_GRAYSCALE: + fragColor.rgb = mix(fragColor.rgb, vec3(luma), factor); + break; + case MODE_SEPIA: + fragColor.rgb = mix(fragColor.rgb, sepia_mat * fragColor.rgb, factor); + break; + case MODE_DUOTONE: + fragColor.rgb = luma * ((luma <= factor) ? lowColor : highColor); + break; + case MODE_CUSTOM: + fragColor.rgb = mix(fragColor.rgb, luma * lowColor, factor); + break; + case MODE_TRANSPARENT: + default: + fragColor.rgb *= factor; + fragRevealage.rgb = mix(vec3(1.0), fragRevealage.rgb, factor); + break; + } +} + +#elif defined(BLUR) + +uniform vec2 offset; +uniform int sampCount; + +void main() +{ + vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); + vec2 ofs = offset * pixel_size; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + /* No blending. */ + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + ofs * x; + fragColor.rgb += texture(colorBuf, uv).rgb * weight; + fragRevealage.rgb += texture(revealBuf, uv).rgb * weight; + } + + fragColor /= weight_accum; + fragRevealage /= weight_accum; +} + +#elif defined(TRANSFORM) + +uniform vec2 axisFlip = vec2(1.0); +uniform vec2 waveDir = vec2(0.0); +uniform vec2 waveOffset = vec2(0.0); +uniform float wavePhase = 0.0; +uniform vec2 swirlCenter = vec2(0.0); +uniform float swirlAngle = 0.0; +uniform float swirlRadius = 0.0; + +void main() +{ + vec2 uv = (uvcoordsvar.xy - 0.5) * axisFlip + 0.5; + + /* Wave deform. */ + float wave_time = dot(uv, waveDir.xy); + uv += sin(wave_time + wavePhase) * waveOffset; + /* Swirl deform. */ + if (swirlRadius > 0.0) { + vec2 tex_size = vec2(textureSize(colorBuf, 0).xy); + vec2 pix_coord = uv * tex_size - swirlCenter; + float dist = length(pix_coord); + float percent = clamp((swirlRadius - dist) / swirlRadius, 0.0, 1.0); + float theta = percent * percent * swirlAngle; + float s = sin(theta); + float c = cos(theta); + mat2 rot = mat2(vec2(c, -s), vec2(s, c)); + uv = (rot * pix_coord + swirlCenter) / tex_size; + } + + fragColor = texture(colorBuf, uv); + fragRevealage = texture(revealBuf, uv); +} + +#elif defined(GLOW) + +uniform vec4 glowColor; +uniform vec2 offset; +uniform int sampCount; +uniform vec3 threshold; +uniform bool firstPass; +uniform bool glowUnder; +uniform int blendMode; + +void main() +{ + vec2 pixel_size = 1.0 / vec2(textureSize(revealBuf, 0).xy); + vec2 ofs = offset * pixel_size; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + ofs * x; + vec3 col = texture(colorBuf, uv).rgb; + vec3 rev = texture(revealBuf, uv).rgb; + if (threshold.x > -1.0) { + if (threshold.y > -1.0) { + if (all(lessThan(abs(col - threshold), vec3(0.05)))) { + weight = 0.0; + } + } + else { + if (dot(col, vec3(1.0 / 3.0)) < threshold.x) { + weight = 0.0; + } + } + } + fragColor.rgb += col * weight; + fragRevealage.rgb += (1.0 - rev) * weight; + } + + if (weight_accum > 0.0) { + fragColor *= glowColor.rgbb / weight_accum; + fragRevealage = fragRevealage / weight_accum; + } + fragRevealage = 1.0 - fragRevealage; + + if (glowUnder) { + if (firstPass) { + /* In first pass we copy the revealage buffer in the alpha channel. + * This let us do the alpha under in second pass. */ + vec3 original_revealage = texture(revealBuf, uvcoordsvar.xy).rgb; + fragRevealage.a = clamp(dot(original_revealage.rgb, vec3(0.333334)), 0.0, 1.0); + } + else { + /* Recover original revealage. */ + fragRevealage.a = texture(revealBuf, uvcoordsvar.xy).a; + } + } + + if (!firstPass) { + fragColor.a = clamp(1.0 - dot(fragRevealage.rgb, vec3(0.333334)), 0.0, 1.0); + fragRevealage.a *= glowColor.a; + blend_mode_output(blendMode, fragColor, fragRevealage.a, fragColor, fragRevealage); + } +} + +#elif defined(RIM) + +uniform vec2 blurDir; +uniform vec2 uvOffset; +uniform vec3 rimColor; +uniform vec3 maskColor; +uniform int sampCount; +uniform int blendMode; +uniform bool isFirstPass; + +void main() +{ + /* Blur revealage buffer. */ + fragRevealage = vec4(0.0); + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = uvcoordsvar.xy + blurDir * x + uvOffset; + vec3 col = texture(revealBuf, uv).rgb; + if (any(not(equal(vec2(0.0), floor(uv))))) { + col = vec3(0.0); + } + fragRevealage.rgb += col * weight; + } + fragRevealage /= weight_accum; + + if (isFirstPass) { + /* In first pass we copy the reveal buffer. This let us do alpha masking in second pass. */ + fragColor = texture(revealBuf, uvcoordsvar.xy); + /* Also add the masked color to the reveal buffer. */ + vec3 col = texture(colorBuf, uvcoordsvar.xy).rgb; + if (all(lessThan(abs(col - maskColor), vec3(0.05)))) { + fragColor = vec4(1.0); + } + } + else { + /* Premult by foreground alpha (alpha mask). */ + float mask = 1.0 - clamp(dot(vec3(0.333334), texture(colorBuf, uvcoordsvar.xy).rgb), 0.0, 1.0); + + /* fragRevealage is blurred shadow. */ + float rim = clamp(dot(vec3(0.333334), fragRevealage.rgb), 0.0, 1.0); + + vec4 color = vec4(rimColor, 1.0); + + blend_mode_output(blendMode, color, rim * mask, fragColor, fragRevealage); + } +} + +#elif defined(SHADOW) + +uniform vec4 shadowColor; +uniform vec2 uvRotX; +uniform vec2 uvRotY; +uniform vec2 uvOffset; +uniform vec2 blurDir; +uniform vec2 waveDir; +uniform vec2 waveOffset; +uniform float wavePhase; +uniform int sampCount; +uniform bool isFirstPass; + +vec2 compute_uvs(float x) +{ + vec2 uv = uvcoordsvar.xy; + /* Tranform UV (loc, rot, scale) */ + uv = uv.x * uvRotX + uv.y * uvRotY + uvOffset; + uv += blurDir * x; + /* Wave deform. */ + float wave_time = dot(uv, waveDir.xy); + uv += sin(wave_time + wavePhase) * waveOffset; + return uv; +} + +void main() +{ + /* Blur revealage buffer. */ + fragRevealage = vec4(0.0); + float weight_accum = 0.0; + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount); + float weight = gaussian_weight(x); + weight_accum += weight; + vec2 uv = compute_uvs(x); + vec3 col = texture(revealBuf, uv).rgb; + if (any(not(equal(vec2(0.0), floor(uv))))) { + col = vec3(1.0); + } + fragRevealage.rgb += col * weight; + } + fragRevealage /= weight_accum; + + /* No blending in first pass, alpha over premult in second pass. */ + if (isFirstPass) { + /* In first pass we copy the reveal buffer. This let us do alpha under in second pass. */ + fragColor = texture(revealBuf, uvcoordsvar.xy); + } + else { + /* fragRevealage is blurred shadow. */ + float shadow_fac = 1.0 - clamp(dot(vec3(0.333334), fragRevealage.rgb), 0.0, 1.0); + /* Premult by foreground revealage (alpha under). */ + vec3 original_revealage = texture(colorBuf, uvcoordsvar.xy).rgb; + shadow_fac *= clamp(dot(vec3(0.333334), original_revealage), 0.0, 1.0); + /* Modulate by opacity */ + shadow_fac *= shadowColor.a; + /* Apply shadow color. */ + fragColor.rgb = mix(vec3(0.0), shadowColor.rgb, shadow_fac); + /* Alpha over (mask behind the shadow). */ + fragColor.a = shadow_fac; + + fragRevealage.rgb = original_revealage * (1.0 - shadow_fac); + /* Replace the whole revealage buffer. */ + fragRevealage.a = 1.0; + } +} + +#elif defined(PIXELIZE) + +uniform vec2 targetPixelSize; +uniform vec2 targetPixelOffset; +uniform vec2 accumOffset; +uniform int sampCount; + +void main() +{ + vec2 pixel = floor((uvcoordsvar.xy - targetPixelOffset) / targetPixelSize); + vec2 uv = (pixel + 0.5) * targetPixelSize + targetPixelOffset; + + fragColor = vec4(0.0); + fragRevealage = vec4(0.0); + + for (int i = -sampCount; i <= sampCount; i++) { + float x = float(i) / float(sampCount + 1); + vec2 uv_ofs = uv + accumOffset * 0.5 * x; + fragColor += texture(colorBuf, uv_ofs); + fragRevealage += texture(revealBuf, uv_ofs); + } + + fragColor /= float(sampCount) * 2.0 + 1.0; + fragRevealage /= float(sampCount) * 2.0 + 1.0; +} + +#endif diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl deleted file mode 100644 index 926b11e4083..00000000000 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl +++ /dev/null @@ -1,76 +0,0 @@ -in vec4 uvcoordsvar; - -out vec4 FragColor; - -uniform sampler2D strokeColor; -uniform sampler2D strokeDepth; -uniform int tonemapping; -uniform vec4 select_color; -uniform int do_select; - -float srgb_to_linearrgb(float c) -{ - if (c < 0.04045) { - return (c < 0.0) ? 0.0 : c * (1.0 / 12.92); - } - else { - return pow((c + 0.055) * (1.0 / 1.055), 2.4); - } -} - -float linearrgb_to_srgb(float c) -{ - if (c < 0.0031308) { - return (c < 0.0) ? 0.0 : c * 12.92; - } - else { - return 1.055 * pow(c, 1.0 / 2.4) - 0.055; - } -} - -bool check_borders(ivec2 uv, int size) -{ - for (int x = -size; x <= size; x++) { - for (int y = -size; y <= size; y++) { - vec4 stroke_color = texelFetch(strokeColor, ivec2(uv.x + x, uv.y + y), 0).rgba; - if (stroke_color.a > 0) { - return true; - } - } - } - - return false; -} - -void main() -{ - ivec2 uv = ivec2(gl_FragCoord.xy); - float stroke_depth = texelFetch(strokeDepth, uv, 0).r; - vec4 stroke_color = texelFetch(strokeColor, uv, 0).rgba; - - /* premult alpha factor to remove double blend effects */ - if (stroke_color.a > 0) { - stroke_color = vec4(vec3(stroke_color.rgb / stroke_color.a), stroke_color.a); - } - - /* apply color correction for render only */ - if (tonemapping == 1) { - stroke_color.r = srgb_to_linearrgb(stroke_color.r); - stroke_color.g = srgb_to_linearrgb(stroke_color.g); - stroke_color.b = srgb_to_linearrgb(stroke_color.b); - } - - FragColor = clamp(stroke_color, 0.0, 1.0); - gl_FragDepth = clamp(stroke_depth, 0.0, 1.0); - - if (do_select == 1) { - if (stroke_color.a == 0) { - if (check_borders(uv, 2)) { - FragColor = select_color; - gl_FragDepth = 0.000001; - /* Dirty fix waiting for new GPencil engine. */ - FragColor.rgb = pow(FragColor.rgb, vec3(2.2)); - } - } - } -} diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index a8d2c4c6cf0..cfa0fa9eb1a 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -25,6 +25,8 @@ #include "DRW_engine.h" #include "DRW_render.h" +#include "DEG_depsgraph_query.h" + #include "ED_view3d.h" #include "BKE_object.h" @@ -79,6 +81,7 @@ static void OVERLAY_engine_init(void *vedata) pd->xray_enabled = XRAY_ACTIVE(v3d); pd->xray_enabled_and_not_wire = pd->xray_enabled && v3d->shading.type > OB_WIRE; pd->clear_in_front = (v3d->shading.type != OB_SOLID); + pd->cfra = DEG_get_ctime(draw_ctx->depsgraph); OVERLAY_antialiasing_init(vedata); @@ -133,11 +136,14 @@ static void OVERLAY_cache_init(void *vedata) case CTX_MODE_SCULPT: OVERLAY_sculpt_cache_init(vedata); break; - case CTX_MODE_OBJECT: - case CTX_MODE_PAINT_GPENCIL: case CTX_MODE_EDIT_GPENCIL: + case CTX_MODE_PAINT_GPENCIL: case CTX_MODE_SCULPT_GPENCIL: + case CTX_MODE_VERTEX_GPENCIL: case CTX_MODE_WEIGHT_GPENCIL: + OVERLAY_edit_gpencil_cache_init(vedata); + break; + case CTX_MODE_OBJECT: break; default: BLI_assert(!"Draw mode invalid"); @@ -148,6 +154,7 @@ static void OVERLAY_cache_init(void *vedata) OVERLAY_background_cache_init(vedata); OVERLAY_extra_cache_init(vedata); OVERLAY_facing_cache_init(vedata); + OVERLAY_gpencil_cache_init(vedata); OVERLAY_grid_cache_init(vedata); OVERLAY_image_cache_init(vedata); OVERLAY_metaball_cache_init(vedata); @@ -216,8 +223,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) const bool in_paint_mode = (ob == draw_ctx->obact) && (draw_ctx->object_mode & OB_MODE_ALL_PAINT); const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != NULL); - const bool has_surface = ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_MBALL, OB_FONT); - const bool draw_surface = !((ob->dt < OB_WIRE) || (!renderable && (ob->dt != OB_WIRE))); + const bool has_surface = ELEM( + ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_MBALL, OB_FONT, OB_GPENCIL); + const bool draw_surface = (ob->dt >= OB_WIRE) && (renderable || (ob->dt == OB_WIRE)); const bool draw_facing = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION); const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0; const bool draw_wires = draw_surface && has_surface && @@ -429,6 +437,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_armature_draw(vedata); OVERLAY_particle_draw(vedata); OVERLAY_metaball_draw(vedata); + OVERLAY_gpencil_draw(vedata); OVERLAY_extra_draw(vedata); if (DRW_state_is_fbo()) { @@ -491,6 +500,13 @@ static void OVERLAY_draw_scene(void *vedata) case CTX_MODE_SCULPT: OVERLAY_sculpt_draw(vedata); break; + case CTX_MODE_EDIT_GPENCIL: + case CTX_MODE_PAINT_GPENCIL: + case CTX_MODE_SCULPT_GPENCIL: + case CTX_MODE_VERTEX_GPENCIL: + case CTX_MODE_WEIGHT_GPENCIL: + OVERLAY_edit_gpencil_draw(vedata); + break; default: break; } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index b40e95d538c..49f266291da 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -38,7 +38,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" -#include "DNA_gpencil_types.h" +#include "DNA_curve_types.h" #include "DNA_lightprobe_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" @@ -1330,89 +1330,6 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, /** \} */ -/* -------------------------------------------------------------------- */ -/** \name GPencil. - * \{ */ - -static void OVERLAY_gpencil_color_names(Object *ob) -{ - if (ob->mode != OB_MODE_EDIT_GPENCIL) { - return; - } - - bGPdata *gpd = (bGPdata *)ob->data; - if (gpd == NULL) { - return; - } - - const DRWContextState *draw_ctx = DRW_context_state_get(); - ViewLayer *view_layer = draw_ctx->view_layer; - int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); - uchar color[4]; - /* Color Management: Exception here as texts are drawn in sRGB space directly. */ - UI_GetThemeColor3ubv(theme_id, color); - color[3] = 255; - struct DRWTextStore *dt = DRW_text_cache_ensure(); - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->flag & GP_LAYER_HIDE) { - continue; - } - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1); - if (ma == NULL) { - continue; - } - - MaterialGPencilStyle *gp_style = ma->gp_style; - /* skip stroke if it doesn't have any valid data */ - if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) { - continue; - } - /* check if the color is visible */ - if (gp_style->flag & GP_STYLE_COLOR_HIDE) { - continue; - } - - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - float fpt[3]; - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - if (pt->flag & GP_SPOINT_SELECT) { - mul_v3_m4v3(fpt, ob->obmat, &pt->x); - DRW_text_cache_add(dt, - fpt, - ma->id.name + 2, - strlen(ma->id.name + 2), - 10, - 0, - DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, - color); - break; - } - } - } - } - } -} - -void OVERLAY_gpencil_cache_populate(OVERLAY_Data *UNUSED(vedata), Object *ob) -{ - /* don't show object extras in set's */ - if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) { - if ((ob->dtx & OB_DRAWNAME) && DRW_state_show_text()) { - OVERLAY_gpencil_color_names(ob); - } - } -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Volumetric / Smoke sim * \{ */ diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c new file mode 100644 index 00000000000..c96c448c63b --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -0,0 +1,391 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#include "DRW_render.h" + +#include "BKE_gpencil.h" + +#include "UI_resources.h" + +#include "DNA_gpencil_types.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_view3d.h" + +#include "overlay_private.h" + +#include "draw_common.h" +#include "draw_manager_text.h" + +void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUShader *sh; + DRWShadingGroup *grp; + + /* Default: Display nothing. */ + pd->edit_gpencil_points_grp = NULL; + pd->edit_gpencil_wires_grp = NULL; + psl->edit_gpencil_ps = NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL; + Scene *scene = draw_ctx->scene; + ToolSettings *ts = scene->toolsettings; + + if (gpd == NULL || ob->type != OB_GPENCIL) { + return; + } + + /* For sculpt show only if mask mode, and only points if not stroke mode. */ + const bool use_sculpt_mask = (GPENCIL_SCULPT_MODE(gpd) && + GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt)); + const bool show_sculpt_points = (GPENCIL_SCULPT_MODE(gpd) && + (ts->gpencil_selectmode_sculpt & + (GP_SCULPT_MASK_SELECTMODE_POINT | + GP_SCULPT_MASK_SELECTMODE_SEGMENT))); + + /* For vertex paint show only if mask mode, and only points if not stroke mode. */ + bool use_vertex_mask = (GPENCIL_VERTEX_MODE(gpd) && + GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex)); + const bool show_vertex_points = (GPENCIL_VERTEX_MODE(gpd) && + (ts->gpencil_selectmode_vertex & + (GP_VERTEX_MASK_SELECTMODE_POINT | + GP_VERTEX_MASK_SELECTMODE_SEGMENT))); + + /* If Sculpt or Vertex mode and the mask is disabled, the select must be hidden. */ + const bool hide_select = ((GPENCIL_SCULPT_MODE(gpd) && !use_sculpt_mask) || + (GPENCIL_VERTEX_MODE(gpd) && !use_vertex_mask)); + + const bool do_multiedit = GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool show_multi_edit_lines = (v3d->gp_flag & V3D_GP_SHOW_MULTIEDIT_LINES) != 0; + + const bool show_lines = (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES) || show_multi_edit_lines; + + const bool hide_lines = !GPENCIL_EDIT_MODE(gpd) && !GPENCIL_WEIGHT_MODE(gpd) && + !use_sculpt_mask && !use_vertex_mask && !show_lines; + + /* Special case when vertex paint and multiedit lines. */ + if (do_multiedit && show_multi_edit_lines && GPENCIL_VERTEX_MODE(gpd)) { + use_vertex_mask = true; + } + + const bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); + + /* Show Edit points if: + * Edit mode: Not in Stroke selection mode + * Sculpt mode: If use Mask and not Stroke mode + * Weight mode: Always + * Vertex mode: If use Mask and not Stroke mode + */ + const bool show_points = show_sculpt_points || is_weight_paint || show_vertex_points || + (GPENCIL_EDIT_MODE(gpd) && + (ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE)); + + if ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_gpencil_ps, state | pd->clipping_state); + + if (show_lines && !hide_lines) { + sh = OVERLAY_shader_edit_gpencil_wire(); + pd->edit_gpencil_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", show_multi_edit_lines); + DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint); + DRW_shgroup_uniform_bool_copy(grp, "hideSelect", hide_select); + DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + + if (show_points && !hide_select) { + sh = OVERLAY_shader_edit_gpencil_point(); + pd->edit_gpencil_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", do_multiedit); + DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint); + DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + } + + /* control points for primitives and speed guide */ + const bool is_cppoint = (gpd->runtime.tot_cp_points > 0); + const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && + (draw_ctx->object_mode == OB_MODE_PAINT_GPENCIL)); + const bool is_show_gizmo = (((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) && + ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0)); + + if ((is_cppoint || is_speed_guide) && (is_show_gizmo)) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_gpencil_gizmos_ps, state); + + sh = OVERLAY_shader_edit_gpencil_guide_point(); + grp = DRW_shgroup_create(sh, psl->edit_gpencil_gizmos_ps); + + if (gpd->runtime.cp_points != NULL) { + for (int i = 0; i < gpd->runtime.tot_cp_points; i++) { + bGPDcontrolpoint *cp = &gpd->runtime.cp_points[i]; + grp = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", &cp->x); + DRW_shgroup_uniform_float_copy(grp, "pSize", cp->size * 0.8f * G_draw.block.sizePixel); + DRW_shgroup_uniform_vec4_copy(grp, "pColor", cp->color); + DRW_shgroup_call_procedural_points(grp, NULL, 1); + } + } + + if (ts->gp_sculpt.guide.use_guide) { + float color[4]; + if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_CUSTOM) { + UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.location); + } + else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT && + ts->gp_sculpt.guide.reference_object != NULL) { + UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.reference_object->loc); + } + else { + UI_GetThemeColor4fv(TH_REDALERT, color); + DRW_shgroup_uniform_vec3_copy(grp, "pPosition", scene->cursor.location); + } + DRW_shgroup_uniform_vec4_copy(grp, "pColor", color); + DRW_shgroup_uniform_float_copy(grp, "pSize", 8.0f * G_draw.block.sizePixel); + DRW_shgroup_call_procedural_points(grp, NULL, 1); + } + } +} + +void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + struct GPUShader *sh; + DRWShadingGroup *grp; + + /* Default: Display nothing. */ + psl->gpencil_canvas_ps = NULL; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL; + Scene *scene = draw_ctx->scene; + ToolSettings *ts = scene->toolsettings; + const View3DCursor *cursor = &scene->cursor; + + if (gpd == NULL || ob->type != OB_GPENCIL) { + return; + } + + const bool show_overlays = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + const bool show_grid = (v3d->gp_flag & V3D_GP_SHOW_GRID) != 0; + + if (show_grid && show_overlays) { + const char *grid_unit = NULL; + float mat[4][4]; + float col_grid[4]; + float size[2]; + + /* set color */ + copy_v3_v3(col_grid, gpd->grid.color); + col_grid[3] = max_ff(v3d->overlay.gpencil_grid_opacity, 0.01f); + + copy_m4_m4(mat, ob->obmat); + + float viewinv[4][4]; + /* Set the grid in the selected axis */ + switch (ts->gp_sculpt.lock_axis) { + case GP_LOCKAXIS_X: + swap_v4_v4(mat[0], mat[2]); + break; + case GP_LOCKAXIS_Y: + swap_v4_v4(mat[1], mat[2]); + break; + case GP_LOCKAXIS_Z: + /* Default. */ + break; + case GP_LOCKAXIS_CURSOR: + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, (float[3]){1, 1, 1}); + break; + case GP_LOCKAXIS_VIEW: + /* view aligned */ + DRW_view_viewmat_get(NULL, viewinv, true); + copy_v3_v3(mat[0], viewinv[0]); + copy_v3_v3(mat[1], viewinv[1]); + break; + } + + translate_m4(mat, gpd->grid.offset[0], gpd->grid.offset[1], 0.0f); + mul_v2_v2fl(size, gpd->grid.scale, 2.0f * ED_scene_grid_scale(scene, &grid_unit)); + rescale_m4(mat, (float[3]){size[0], size[1], 0.0f}); + + const int gridlines = (gpd->grid.lines <= 0) ? 1 : gpd->grid.lines; + int line_ct = gridlines * 4 + 2; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->gpencil_canvas_ps, state); + + sh = OVERLAY_shader_gpencil_canvas(); + grp = DRW_shgroup_create(sh, psl->gpencil_canvas_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_vec4_copy(grp, "color", col_grid); + DRW_shgroup_uniform_vec3_copy(grp, "xAxis", mat[0]); + DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]); + DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]); + DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_ct / 2); + DRW_shgroup_call_procedural_lines(grp, NULL, line_ct); + } +} + +static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + bGPdata *gpd = (bGPdata *)ob->data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + + if (pd->edit_gpencil_wires_grp) { + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_wires_grp); + DRW_shgroup_uniform_vec4_copy(grp, "gpEditColor", gpd->line_color); + + struct GPUBatch *geom = DRW_cache_gpencil_edit_lines_get(ob, pd->cfra); + DRW_shgroup_call_no_cull(pd->edit_gpencil_wires_grp, geom, ob); + } + + if (pd->edit_gpencil_points_grp) { + const bool show_direction = (v3d->gp_flag & V3D_GP_SHOW_STROKE_DIRECTION) != 0; + + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->edit_gpencil_points_grp); + DRW_shgroup_uniform_float_copy(grp, "doStrokeEndpoints", show_direction); + + struct GPUBatch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra); + DRW_shgroup_call_no_cull(grp, geom, ob); + } +} + +static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + Object *ob = (Object *)thunk; + Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1); + if (ma == NULL) { + return; + } + MaterialGPencilStyle *gp_style = ma->gp_style; + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) { + return; + } + /* check if the color is visible */ + if (gp_style->flag & GP_MATERIAL_HIDE) { + return; + } + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + /* Draw name at the first selected point. */ + if (pt->flag & GP_SPOINT_SELECT) { + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); + uchar color[4]; + UI_GetThemeColor4ubv(theme_id, color); + + float fpt[3]; + mul_v3_m4v3(fpt, ob->obmat, &pt->x); + + struct DRWTextStore *dt = DRW_text_cache_ensure(); + DRW_text_cache_add(dt, + fpt, + ma->id.name + 2, + strlen(ma->id.name + 2), + 10, + 0, + DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, + color); + break; + } + } + } +} + +static void OVERLAY_gpencil_color_names(Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + int cfra = DEG_get_ctime(draw_ctx->depsgraph); + + BKE_gpencil_visible_stroke_iter( + ob, NULL, overlay_gpencil_draw_stroke_color_name, ob, false, cfra); +} + +void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return; + } + + if (GPENCIL_ANY_MODE(gpd)) { + OVERLAY_edit_gpencil_cache_populate(vedata, ob); + } + + /* don't show object extras in set's */ + if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) { + if ((v3d->gp_flag & V3D_GP_SHOW_MATERIAL_NAME) && (ob->mode == OB_MODE_EDIT_GPENCIL) && + DRW_state_show_text()) { + OVERLAY_gpencil_color_names(ob); + } + } +} + +void OVERLAY_gpencil_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->gpencil_canvas_ps) { + DRW_draw_pass(psl->gpencil_canvas_ps); + } +} + +void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->edit_gpencil_gizmos_ps) { + DRW_draw_pass(psl->edit_gpencil_gizmos_ps); + } + + if (psl->edit_gpencil_ps) { + DRW_draw_pass(psl->edit_gpencil_ps); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 555e0084c49..29eb4fd12a4 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -155,7 +155,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, DRW_shgroup_uniform_bool_copy(grp, "selected", selected); DRW_shgroup_uniform_vec3_copy(grp, "customColor", color); /* Only draw the required range. */ - DRW_shgroup_call_range(grp, mpath_batch_line_get(mpath), start_index, len); + DRW_shgroup_call_range(grp, NULL, mpath_batch_line_get(mpath), start_index, len); } /* Draw points. */ @@ -167,7 +167,7 @@ static void motion_path_cache(OVERLAY_Data *vedata, DRW_shgroup_uniform_bool_copy(grp, "showKeyFrames", show_keyframes); DRW_shgroup_uniform_vec3_copy(grp, "customColor", color); /* Only draw the required range. */ - DRW_shgroup_call_range(grp, mpath_batch_points_get(mpath), start_index, len); + DRW_shgroup_call_range(grp, NULL, mpath_batch_points_get(mpath), start_index, len); } /* Draw frame numbers at each frame-step value. */ diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c index 63738b3c214..e77a0a143a9 100644 --- a/source/blender/draw/engines/overlay/overlay_outline.c +++ b/source/blender/draw/engines/overlay/overlay_outline.c @@ -23,13 +23,67 @@ #include "DRW_render.h" #include "BKE_global.h" +#include "BKE_gpencil.h" -#include "DNA_lightprobe_types.h" +#include "BKE_object.h" + +#include "DNA_gpencil_types.h" #include "UI_resources.h" #include "overlay_private.h" +/* Returns the normal plane in ndc space. */ +static void gpencil_depth_plane(Object *ob, float r_plane[4]) +{ + /* TODO put that into private data. */ + float viewinv[4][4]; + DRW_view_viewmat_get(NULL, viewinv, true); + float *camera_z_axis = viewinv[2]; + float *camera_pos = viewinv[3]; + + /* Find the normal most likely to represent the gpObject. */ + /* TODO: This does not work quite well if you use + * strokes not aligned with the object axes. Maybe we could try to + * compute the minimum axis of all strokes. But this would be more + * computationaly heavy and should go into the GPData evaluation. */ + BoundBox *bbox = BKE_object_boundbox_get(ob); + /* Convert bbox to matrix */ + float mat[4][4], size[3], center[3]; + BKE_boundbox_calc_size_aabb(bbox, size); + BKE_boundbox_calc_center_aabb(bbox, center); + unit_m4(mat); + copy_v3_v3(mat[3], center); + /* Avoid division by 0.0 later. */ + add_v3_fl(size, 1e-8f); + rescale_m4(mat, size); + /* BBox space to World. */ + mul_m4_m4m4(mat, ob->obmat, mat); + /* BBox center in world space. */ + copy_v3_v3(center, mat[3]); + /* View Vector. */ + if (DRW_view_is_persp_get(NULL)) { + /* BBox center to camera vector. */ + sub_v3_v3v3(r_plane, camera_pos, mat[3]); + } + else { + copy_v3_v3(r_plane, camera_z_axis); + } + /* World to BBox space. */ + invert_m4(mat); + /* Normalize the vector in BBox space. */ + mul_mat3_m4_v3(mat, r_plane); + normalize_v3(r_plane); + + transpose_m4(mat); + /* mat is now a "normal" matrix which will transform + * BBox space normal to world space. */ + mul_mat3_m4_v3(mat, r_plane); + normalize_v3(r_plane); + + plane_from_point_normal_v3(r_plane, center, r_plane); +} + void OVERLAY_outline_init(OVERLAY_Data *vedata) { OVERLAY_FramebufferList *fbl = vedata->fbl; @@ -79,6 +133,11 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) pd->outlines_grp = grp = DRW_shgroup_create(sh_geom, psl->outlines_prepass_ps); DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); + + GPUShader *sh_gpencil = OVERLAY_shader_outline_prepass_gpencil(); + + pd->outlines_gpencil_grp = grp = DRW_shgroup_create(sh_gpencil, psl->outlines_prepass_ps); + DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); } /* outlines_prepass_ps is still needed for selection of probes. */ @@ -107,6 +166,98 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) } } +typedef struct iterData { + Object *ob; + DRWShadingGroup *stroke_grp; + DRWShadingGroup *fill_grp; + int cfra; + float plane[4]; +} iterData; + +static void gp_layer_cache_populate(bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *UNUSED(gps), + void *thunk) +{ + iterData *iter = (iterData *)thunk; + bGPdata *gpd = (bGPdata *)iter->ob->data; + + const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0; + const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D); + + float object_scale = mat4_to_scale(iter->ob->obmat); + /* Negate thickness sign to tag that strokes are in screen space. + * Convert to world units (by default, 1 meter = 2000 px). */ + float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f); + + DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp); + DRW_shgroup_uniform_bool_copy(grp, "strokeOrder3d", is_stroke_order_3d); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewportInv", DRW_viewport_invert_size_get()); + DRW_shgroup_uniform_vec2_copy(grp, "sizeViewport", DRW_viewport_size_get()); + DRW_shgroup_uniform_float_copy(grp, "thicknessScale", object_scale); + DRW_shgroup_uniform_float_copy(grp, "thicknessOffset", (float)gpl->line_change); + DRW_shgroup_uniform_float_copy(grp, "thicknessWorldScale", thickness_scale); + DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane); +} + +static void gp_stroke_cache_populate(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + iterData *iter = (iterData *)thunk; + + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); + + bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; + bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; + // TODO: What about simplify Fill? + bool show_fill = (gps->tot_triangles > 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0; + + if (hide_material) { + return; + } + + if (show_fill) { + struct GPUBatch *geom = DRW_cache_gpencil_fills_get(iter->ob, iter->cfra); + int vfirst = gps->runtime.fill_start * 3; + int vcount = gps->tot_triangles * 3; + DRW_shgroup_call_range(iter->fill_grp, iter->ob, geom, vfirst, vcount); + } + + if (show_stroke) { + struct GPUBatch *geom = DRW_cache_gpencil_strokes_get(iter->ob, iter->cfra); + /* Start one vert before to have gl_InstanceID > 0 (see shader). */ + int vfirst = gps->runtime.stroke_start - 1; + /* Include "potential" cyclic vertex and start adj vertex (see shader). */ + int vcount = gps->totpoints + 1 + 1; + DRW_shgroup_call_instance_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount); + } +} + +static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob) +{ + /* No outlines in edit mode. */ + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd && GPENCIL_ANY_MODE(gpd)) { + return; + } + + iterData iter = { + .ob = ob, + .stroke_grp = pd->outlines_gpencil_grp, + .fill_grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp), + .cfra = pd->cfra, + }; + + if (gpd->draw_mode == GP_DRAWMODE_2D) { + gpencil_depth_plane(ob, iter.plane); + } + + BKE_gpencil_visible_stroke_iter( + ob, gp_layer_cache_populate, gp_stroke_cache_populate, &iter, false, pd->cfra); +} + void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, Object *ob, OVERLAY_DupliData *dupli, @@ -123,6 +274,11 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, return; } + if (ob->type == OB_GPENCIL) { + OVERLAY_outline_gpencil(pd, ob); + return; + } + if (dupli && !init_dupli) { geom = dupli->outline_geom; shgroup = dupli->outline_shgrp; diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 185df723301..167a8e940df 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -60,6 +60,8 @@ typedef struct OVERLAY_PassList { DRWPass *clipping_frustum_ps; DRWPass *edit_curve_wire_ps[2]; DRWPass *edit_curve_handle_ps; + DRWPass *edit_gpencil_ps; + DRWPass *edit_gpencil_gizmos_ps; DRWPass *edit_lattice_ps; DRWPass *edit_mesh_depth_ps[2]; DRWPass *edit_mesh_verts_ps[2]; @@ -76,6 +78,7 @@ typedef struct OVERLAY_PassList { DRWPass *extra_blend_ps; DRWPass *extra_centers_ps; DRWPass *extra_grid_ps; + DRWPass *gpencil_canvas_ps; DRWPass *facing_ps; DRWPass *grid_ps; DRWPass *image_background_ps; @@ -219,6 +222,8 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *edit_curve_points_grp; DRWShadingGroup *edit_lattice_points_grp; DRWShadingGroup *edit_lattice_wires_grp; + DRWShadingGroup *edit_gpencil_points_grp; + DRWShadingGroup *edit_gpencil_wires_grp; DRWShadingGroup *edit_mesh_depth_grp[2]; DRWShadingGroup *edit_mesh_faces_grp[2]; DRWShadingGroup *edit_mesh_faces_cage_grp[2]; @@ -238,6 +243,7 @@ typedef struct OVERLAY_PrivateData { DRWShadingGroup *motion_path_lines_grp; DRWShadingGroup *motion_path_points_grp; DRWShadingGroup *outlines_grp; + DRWShadingGroup *outlines_gpencil_grp; DRWShadingGroup *paint_surf_grp; DRWShadingGroup *paint_wire_grp; DRWShadingGroup *paint_wire_selected_grp; @@ -277,6 +283,7 @@ typedef struct OVERLAY_PrivateData { float xray_opacity; short v3d_flag; /* TODO move to View3DOverlay */ short v3d_gridflag; /* TODO move to View3DOverlay */ + int cfra; DRWState clipping_state; OVERLAY_ShadingData shdata; @@ -419,6 +426,12 @@ void OVERLAY_edit_curve_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_edit_surf_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_edit_curve_draw(OVERLAY_Data *vedata); +void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata); +void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata); +void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_gpencil_draw(OVERLAY_Data *vedata); +void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata); + void OVERLAY_edit_lattice_cache_init(OVERLAY_Data *vedata); void OVERLAY_edit_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob); @@ -446,7 +459,6 @@ void OVERLAY_extra_centers_draw(OVERLAY_Data *vedata); void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_empty_cache_populate(OVERLAY_Data *vedata, Object *ob); -void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob); void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob); @@ -547,6 +559,9 @@ GPUShader *OVERLAY_shader_depth_only(void); GPUShader *OVERLAY_shader_edit_curve_handle(void); GPUShader *OVERLAY_shader_edit_curve_point(void); GPUShader *OVERLAY_shader_edit_curve_wire(void); +GPUShader *OVERLAY_shader_edit_gpencil_guide_point(void); +GPUShader *OVERLAY_shader_edit_gpencil_point(void); +GPUShader *OVERLAY_shader_edit_gpencil_wire(void); GPUShader *OVERLAY_shader_edit_lattice_point(void); GPUShader *OVERLAY_shader_edit_lattice_wire(void); GPUShader *OVERLAY_shader_edit_mesh_analysis(void); @@ -564,12 +579,14 @@ GPUShader *OVERLAY_shader_extra_wire(bool use_object); GPUShader *OVERLAY_shader_extra_loose_point(void); GPUShader *OVERLAY_shader_extra_point(void); GPUShader *OVERLAY_shader_facing(void); +GPUShader *OVERLAY_shader_gpencil_canvas(void); GPUShader *OVERLAY_shader_grid(void); GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); GPUShader *OVERLAY_shader_motion_path_vert(void); GPUShader *OVERLAY_shader_uniform_color(void); GPUShader *OVERLAY_shader_outline_prepass(bool use_wire); +GPUShader *OVERLAY_shader_outline_prepass_gpencil(void); GPUShader *OVERLAY_shader_extra_grid(void); GPUShader *OVERLAY_shader_outline_detect(void); GPUShader *OVERLAY_shader_paint_face(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index d33ef239198..86d5f58957e 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -54,6 +54,9 @@ extern char datatoc_edit_curve_handle_geom_glsl[]; extern char datatoc_edit_curve_handle_vert_glsl[]; extern char datatoc_edit_curve_point_vert_glsl[]; extern char datatoc_edit_curve_wire_vert_glsl[]; +extern char datatoc_edit_gpencil_canvas_vert_glsl[]; +extern char datatoc_edit_gpencil_guide_vert_glsl[]; +extern char datatoc_edit_gpencil_vert_glsl[]; extern char datatoc_edit_lattice_point_vert_glsl[]; extern char datatoc_edit_lattice_wire_vert_glsl[]; extern char datatoc_edit_mesh_common_lib_glsl[]; @@ -113,6 +116,8 @@ extern char datatoc_gpu_shader_flat_color_frag_glsl[]; extern char datatoc_gpu_shader_point_varying_color_varying_outline_aa_frag_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; +extern char datatoc_gpencil_common_lib_glsl[]; + extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_fullscreen_vert_glsl[]; extern char datatoc_common_fxaa_lib_glsl[]; @@ -138,6 +143,9 @@ typedef struct OVERLAY_Shaders { GPUShader *edit_curve_handle; GPUShader *edit_curve_point; GPUShader *edit_curve_wire; + GPUShader *edit_gpencil_guide_point; + GPUShader *edit_gpencil_point; + GPUShader *edit_gpencil_wire; GPUShader *edit_lattice_point; GPUShader *edit_lattice_wire; GPUShader *edit_mesh_vert; @@ -159,11 +167,13 @@ typedef struct OVERLAY_Shaders { GPUShader *extra_lightprobe_grid; GPUShader *extra_loose_point; GPUShader *facing; + GPUShader *gpencil_canvas; GPUShader *grid; GPUShader *image; GPUShader *motion_path_line; GPUShader *motion_path_vert; GPUShader *outline_prepass; + GPUShader *outline_prepass_gpencil; GPUShader *outline_prepass_wire; GPUShader *outline_detect; GPUShader *paint_face; @@ -557,6 +567,58 @@ GPUShader *OVERLAY_shader_edit_curve_wire(void) return sh_data->edit_curve_wire; } +GPUShader *OVERLAY_shader_edit_gpencil_guide_point(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->edit_gpencil_guide_point) { + sh_data->edit_gpencil_guide_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_guide_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + }); + } + return sh_data->edit_gpencil_guide_point; +} + +GPUShader *OVERLAY_shader_edit_gpencil_point(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->edit_gpencil_point) { + sh_data->edit_gpencil_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_POINTS\n", NULL}, + }); + } + return sh_data->edit_gpencil_point; +} + +GPUShader *OVERLAY_shader_edit_gpencil_wire(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->edit_gpencil_wire) { + sh_data->edit_gpencil_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_3D_smooth_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_gpencil_wire; +} + GPUShader *OVERLAY_shader_edit_lattice_point(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -883,6 +945,21 @@ GPUShader *OVERLAY_shader_facing(void) return sh_data->facing; } +GPUShader *OVERLAY_shader_gpencil_canvas(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->gpencil_canvas) { + sh_data->gpencil_canvas = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_gpencil_canvas_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_extra_frag_glsl, NULL}, + }); + } + return sh_data->gpencil_canvas; +} + GPUShader *OVERLAY_shader_grid(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; @@ -998,6 +1075,32 @@ GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) return use_wire ? sh_data->outline_prepass_wire : sh_data->outline_prepass; } +GPUShader *OVERLAY_shader_outline_prepass_gpencil(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg]; + OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg]; + if (!sh_data->outline_prepass_gpencil) { + sh_data->outline_prepass_gpencil = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_outline_prepass_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_gpencil_common_lib_glsl, + datatoc_outline_prepass_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define USE_GPENCIL\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + } + return sh_data->outline_prepass_gpencil; +} + GPUShader *OVERLAY_shader_outline_detect(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 5dbdc71dae1..4df9faace18 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -201,7 +201,11 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, shgrp = pd->wires_grp[is_xray][use_coloring]; } - if (use_sculpt_pbvh) { + if (ob->type == OB_GPENCIL) { + /* TODO (fclem) Make GPencil objects have correct boundbox. */ + DRW_shgroup_call_no_cull(shgrp, geom, ob); + } + else if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, true, false, false); } else { diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl new file mode 100644 index 00000000000..5aa7fe78e4e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_canvas_vert.glsl @@ -0,0 +1,35 @@ + +uniform vec4 color; +uniform vec3 xAxis; +uniform vec3 yAxis; +uniform vec3 origin; +uniform int halfLineCount; + +flat out vec4 finalColor; +flat out vec2 edgeStart; +noperspective out vec2 edgePos; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec2 pos; + pos.x = float(gl_VertexID % 2); + pos.y = float(gl_VertexID / 2) / float(halfLineCount - 1); + + if (pos.y > 1.0) { + pos.xy = pos.yx; + pos.x -= 1.0 + 1.0 / float(halfLineCount - 1); + } + + pos -= 0.5; + + vec3 world_pos = xAxis * pos.x + yAxis * pos.y + origin; + + gl_Position = point_world_to_ndc(world_pos); + + finalColor = color; + + /* Convert to screen position [0..sizeVp]. */ + edgePos = edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl new file mode 100644 index 00000000000..ef68b0f4e6f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_guide_vert.glsl @@ -0,0 +1,14 @@ +uniform vec4 pColor; +uniform float pSize; +uniform vec3 pPosition; + +out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + gl_Position = point_world_to_ndc(pPosition); + finalColor = pColor; + gl_PointSize = pSize; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl new file mode 100644 index 00000000000..cb03ad44615 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl @@ -0,0 +1,103 @@ + +uniform float normalSize; +uniform bool doMultiframe; +uniform bool doStrokeEndpoints; +uniform bool hideSelect; +uniform bool doWeightColor; +uniform float gpEditOpacity; +uniform vec4 gpEditColor; +uniform sampler1D weightTex; + +in vec3 pos; +in float ma; +in uint vflag; +in float weight; + +out vec4 finalColor; + +void discard_vert() +{ + /* We set the vertex at the camera origin to generate 0 fragments. */ + gl_Position = vec4(0.0, 0.0, -3e36, 0.0); +} + +#define GP_EDIT_POINT_SELECTED (1u << 0u) +#define GP_EDIT_STROKE_SELECTED (1u << 1u) +#define GP_EDIT_MULTIFRAME (1u << 2u) +#define GP_EDIT_STROKE_START (1u << 3u) +#define GP_EDIT_STROKE_END (1u << 4u) + +#ifdef USE_POINTS +# define colorUnselect colorGpencilVertex +# define colorSelect colorGpencilVertexSelect +#else +# define colorUnselect gpEditColor +# define colorSelect (hideSelect ? colorUnselect : colorGpencilVertexSelect) +#endif + +vec3 weight_to_rgb(float t) +{ + if (t < 0.0) { + /* No weight */ + return colorUnselect.rgb; + } + else if (t > 1.0) { + /* Error color */ + return vec3(1.0, 0.0, 1.0); + } + else { + return texture(weightTex, t).rgb; + } +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + bool is_multiframe = (vflag & GP_EDIT_MULTIFRAME) != 0u; + bool is_stroke_sel = (vflag & GP_EDIT_STROKE_SELECTED) != 0u; + bool is_point_sel = (vflag & GP_EDIT_POINT_SELECTED) != 0u; + + if (doWeightColor) { + finalColor.rgb = weight_to_rgb(weight); + finalColor.a = gpEditOpacity; + } + else { + finalColor = (is_point_sel) ? colorSelect : colorUnselect; + finalColor.a *= gpEditOpacity; + } + +#ifdef USE_POINTS + gl_PointSize = sizeVertex * 2.0; + + if (doStrokeEndpoints && !doWeightColor) { + bool is_stroke_start = (vflag & GP_EDIT_STROKE_START) != 0u; + bool is_stroke_end = (vflag & GP_EDIT_STROKE_END) != 0u; + + if (is_stroke_start) { + gl_PointSize *= 2.0; + finalColor.rgb = vec3(0.0, 1.0, 0.0); + } + else if (is_stroke_end) { + gl_PointSize *= 1.5; + finalColor.rgb = vec3(1.0, 0.0, 0.0); + } + } + + if ((!is_stroke_sel && !doWeightColor) || (!doMultiframe && is_multiframe)) { + discard_vert(); + } +#endif + + /* Discard unwanted padding vertices. */ + if (ma == -1.0 || (is_multiframe && !doMultiframe)) { + discard_vert(); + } + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl index f6e3724eb51..85f79e94263 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl @@ -1,10 +1,46 @@ +uniform vec4 gpDepthPlane; + flat in uint objectId; /* using uint because 16bit uint can contain more ids than int. */ out uint outId; +vec3 ray_plane_intersection(vec3 ray_ori, vec3 ray_dir, vec4 plane) +{ + float d = dot(plane.xyz, ray_dir); + vec3 plane_co = plane.xyz * (-plane.w / dot(plane.xyz, plane.xyz)); + vec3 h = ray_ori - plane_co; + float lambda = -dot(plane.xyz, h) / ((abs(d) < 1e-8) ? 1e-8 : d); + return ray_ori + ray_dir * lambda; +} + void main() { - outId = objectId; +#ifdef USE_GPENCIL + if (stroke_round_cap_mask(strokePt1, strokePt2, strokeAspect, strokeThickness, strokeHardeness) < + 0.001) { + discard; + } + + if (depth != -1.0) { + /* Stroke order 2D. */ + bool is_persp = ProjectionMatrix[3][3] == 0.0; + vec2 uvs = vec2(gl_FragCoord.xy) * sizeViewportInv; + vec3 pos_ndc = vec3(uvs, gl_FragCoord.z) * 2.0 - 1.0; + vec4 pos_world = ViewProjectionMatrixInverse * vec4(pos_ndc, 1.0); + vec3 pos = pos_world.xyz / pos_world.w; + + vec3 ray_ori = pos; + vec3 ray_dir = (is_persp) ? (ViewMatrixInverse[3].xyz - pos) : ViewMatrixInverse[2].xyz; + vec3 isect = ray_plane_intersection(ray_ori, ray_dir, gpDepthPlane); + vec4 ndc = point_world_to_ndc(isect); + gl_FragDepth = (ndc.z / ndc.w) * 0.5 + 0.5; + } + else { + gl_FragDepth = gl_FragCoord.z; + } +#endif + + outId = uint(objectId); } diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl index 984e55b0c46..a2021759196 100644 --- a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl @@ -1,7 +1,9 @@ uniform bool isTransform; +#ifndef USE_GPENCIL in vec3 pos; +#endif #ifdef USE_GEOM out vec3 vPos; @@ -47,11 +49,19 @@ uint outline_colorid_get(void) void main() { +#ifdef USE_GPENCIL + gpencil_vertex(); +# ifdef USE_WORLD_CLIP_PLANES + vec3 world_pos = point_object_to_world(pos1.xyz); +# endif + +#else vec3 world_pos = point_object_to_world(pos); -#ifdef USE_GEOM + gl_Position = point_world_to_ndc(world_pos); +# ifdef USE_GEOM vPos = point_world_to_view(world_pos); +# endif #endif - gl_Position = point_world_to_ndc(world_pos); /* Small bias to always be on top of the geom. */ gl_Position.z -= 1e-3; diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl index 31ac9a2b181..1abac302cda 100644 --- a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl @@ -101,8 +101,10 @@ void wire_object_color_get(out vec3 rim_col, out vec3 wire_col) void main() { + bool no_attrib = all(equal(nor, vec3(0))); + vec3 wnor = no_attrib ? ViewMatrixInverse[2].xyz : normalize(normal_object_to_world(nor)); + vec3 wpos = point_object_to_world(pos); - vec3 wnor = normalize(normal_object_to_world(nor)); bool is_persp = (ProjectionMatrix[3][3] == 0.0); vec3 V = (is_persp) ? normalize(ViewMatrixInverse[3].xyz - wpos) : ViewMatrixInverse[2].xyz; @@ -147,7 +149,7 @@ void main() #endif /* Cull flat edges below threshold. */ - if (get_edge_sharpness(wd) < 0.0) { + if (!no_attrib && (get_edge_sharpness(wd) < 0.0)) { edgeStart = vec2(-1.0); } diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index c8f74120113..6e005e7ccaf 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -117,6 +117,60 @@ static bool workbench_render_framebuffers_init(void) return ok; } +static void workbench_render_result_z(struct RenderLayer *rl, + const char *viewname, + const rcti *rect) +{ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + if ((view_layer->passflag & SCE_PASS_Z) != 0) { + RenderPass *rp = RE_pass_find_by_name(rl, RE_PASSNAME_Z, viewname); + + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_read_depth(dfbl->default_fb, + rect->xmin, + rect->ymin, + BLI_rcti_size_x(rect), + BLI_rcti_size_y(rect), + rp->rect); + + float winmat[4][4]; + DRW_view_winmat_get(NULL, winmat, false); + + int pix_ct = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect); + + /* Convert ogl depth [0..1] to view Z [near..far] */ + if (DRW_view_is_persp_get(NULL)) { + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { + rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; + rp->rect[i] = winmat[3][2] / (rp->rect[i] + winmat[2][2]); + } + } + } + else { + /* Keep in mind, near and far distance are negatives. */ + float near = DRW_view_near_distance_get(NULL); + float far = DRW_view_far_distance_get(NULL); + float range = fabsf(far - near); + + for (int i = 0; i < pix_ct; i++) { + if (rp->rect[i] == 1.0f) { + rp->rect[i] = 1e10f; /* Background */ + } + else { + rp->rect[i] = -rp->rect[i] * range + near; + } + } + } + } +} + static void workbench_render_framebuffers_finish(void) { } @@ -195,8 +249,8 @@ void workbench_render(WORKBENCH_Data *data, const char *viewname = RE_GetActiveRenderView(engine->re); RenderPass *rp = RE_pass_find_by_name(render_layer, RE_PASSNAME_COMBINED, viewname); - GPU_framebuffer_bind(dfbl->color_only_fb); - GPU_framebuffer_read_color(dfbl->color_only_fb, + GPU_framebuffer_bind(dfbl->default_fb); + GPU_framebuffer_read_color(dfbl->default_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -205,6 +259,8 @@ void workbench_render(WORKBENCH_Data *data, 0, rp->rect); + workbench_render_result_z(render_layer, viewname, rect); + workbench_render_framebuffers_finish(); } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index fc7ca6c7d67..cc257b80daf 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -280,8 +280,9 @@ typedef enum { DRW_STATE_BLEND_BACKGROUND = (1 << 20), DRW_STATE_BLEND_OIT = (1 << 21), DRW_STATE_BLEND_MUL = (1 << 22), + DRW_STATE_BLEND_SUB = (1 << 23), /** Use dual source blending. WARNING: Only one color buffer allowed. */ - DRW_STATE_BLEND_CUSTOM = (1 << 23), + DRW_STATE_BLEND_CUSTOM = (1 << 24), DRW_STATE_IN_FRONT_SELECT = (1 << 25), DRW_STATE_LOGIC_INVERT = (1 << 26), @@ -362,10 +363,10 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, #define DRW_shgroup_call_no_cull(shgrp, geom, ob) \ DRW_shgroup_call_ex(shgrp, ob, NULL, geom, true, NULL) -void DRW_shgroup_call_range(DRWShadingGroup *shgroup, - struct GPUBatch *geom, - uint v_sta, - uint v_ct); +void DRW_shgroup_call_range( + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); +void DRW_shgroup_call_instance_range( + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint v_sta, uint v_ct); void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_ct); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_ct); @@ -402,6 +403,17 @@ void DRW_buffer_add_entry_array(DRWCallBuffer *buffer, const void *attr[], uint void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state); void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state); + +/* Reminders: + * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value) + * stencil_value being the value stored in the stencil buffer. + * - (writemask & reference) is what gets written if the test condition is fullfiled. + **/ +void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, + uint write_mask, + uint reference, + uint comp_mask); +/* TODO remove this function. Obsolete version. mask is actually reference value. */ void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask); /* Issue a clear command. */ diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index f37e5b14d83..d0cea5b8c5c 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -136,6 +136,7 @@ static struct DRWShapeCache { GPUBatch *drw_particle_cross; GPUBatch *drw_particle_circle; GPUBatch *drw_particle_axis; + GPUBatch *drw_gpencil_dummy_quad; } SHC = {NULL}; void DRW_shape_cache_free(void) @@ -746,6 +747,29 @@ GPUBatch *DRW_cache_normal_arrow_get(void) return SHC.drw_normal_arrow; } +/* -------------------------------------------------------------------- */ +/** \name Dummy vbos + * + * We need a dummy vbo containing the vertex count to draw instances ranges. + * + * \{ */ + +GPUBatch *DRW_gpencil_dummy_buffer_get(void) +{ + if (SHC.drw_gpencil_dummy_quad == NULL) { + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, 4); + + SHC.drw_gpencil_dummy_quad = GPU_batch_create_ex( + GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO); + } + return SHC.drw_gpencil_dummy_quad; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Common Object API * \{ */ @@ -793,6 +817,9 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) return DRW_cache_text_face_wireframe_get(ob); case OB_MBALL: return DRW_cache_mball_face_wireframe_get(ob); + case OB_GPENCIL: { + return DRW_cache_gpencil_face_wireframe_get(ob); + } default: return NULL; } diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index cb81bdafe62..8ac0d7ada21 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -29,6 +29,7 @@ struct ModifierData; struct Object; struct PTCacheEdit; struct ParticleSystem; +struct bGPDstroke; void DRW_shape_cache_free(void); void DRW_shape_cache_reset(void); @@ -46,6 +47,9 @@ struct GPUBatch *DRW_cache_cube_get(void); struct GPUBatch *DRW_cache_sphere_get(void); struct GPUBatch *DRW_cache_normal_arrow_get(void); +/* Dummy VBOs */ +struct GPUBatch *DRW_gpencil_dummy_buffer_get(void); + /* Common Object */ struct GPUBatch *DRW_cache_object_all_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_object_edge_detection_get(struct Object *ob, bool *r_is_manifold); @@ -196,4 +200,17 @@ struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob, struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob); struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold); +/* GPencil */ +struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_edit_lines_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_edit_points_get(struct Object *ob, int cfra); +struct GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(struct Object *ob); +struct GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(struct Object *ob); + +struct GPUBatch *DRW_cache_gpencil_face_wireframe_get(struct Object *ob); + +struct bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(struct Object *ob); +void DRW_cache_gpencil_sbuffer_clear(struct Object *ob); + #endif /* __DRAW_CACHE_H__ */ diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c new file mode 100644 index 00000000000..c6cedd3f1d2 --- /dev/null +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -0,0 +1,745 @@ +/* + * 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) 2020, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup draw + */ + +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_screen_types.h" + +#include "BKE_deform.h" +#include "BKE_gpencil.h" + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "GPU_batch.h" +#include "ED_gpencil.h" + +#include "DEG_depsgraph_query.h" + +#include "BLI_polyfill_2d.h" +#include "BLI_hash.h" + +#include "draw_cache.h" +#include "draw_cache_impl.h" + +/* ---------------------------------------------------------------------- */ +typedef struct GpencilBatchCache { + /** Instancing Data */ + GPUVertBuf *vbo; + GPUVertBuf *vbo_col; + /** Fill Topology */ + GPUIndexBuf *ibo; + /** Instancing Batches */ + GPUBatch *stroke_batch; + GPUBatch *fill_batch; + GPUBatch *lines_batch; + + /** Edit Mode */ + GPUVertBuf *edit_vbo; + GPUBatch *edit_lines_batch; + GPUBatch *edit_points_batch; + + /** Cache is dirty */ + bool is_dirty; + /** Edit mode flag */ + bool is_editmode; + /** Last cache frame */ + int cache_frame; +} GpencilBatchCache; + +static bool gpencil_batch_cache_valid(GpencilBatchCache *cache, bGPdata *gpd, int cfra) +{ + bool valid = true; + if (cache == NULL) { + return false; + } + + cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); + if (cfra != cache->cache_frame) { + valid = false; + } + else if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) { + valid = false; + } + else if (gpd->flag & GP_DATA_PYTHON_UPDATED) { + gpd->flag &= ~GP_DATA_PYTHON_UPDATED; + valid = false; + } + else if (cache->is_dirty) { + valid = false; + } + + return valid; +} + +static GpencilBatchCache *gpencil_batch_cache_init(Object *ob, int cfra) +{ + bGPdata *gpd = (bGPdata *)ob->data; + + GpencilBatchCache *cache = gpd->runtime.gpencil_cache; + + if (!cache) { + cache = gpd->runtime.gpencil_cache = MEM_callocN(sizeof(*cache), __func__); + } + else { + memset(cache, 0, sizeof(*cache)); + } + + cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); + cache->is_dirty = true; + cache->cache_frame = cfra; + return cache; +} + +static void gpencil_batch_cache_clear(GpencilBatchCache *cache) +{ + if (!cache) { + return; + } + + GPU_BATCH_DISCARD_SAFE(cache->lines_batch); + GPU_BATCH_DISCARD_SAFE(cache->fill_batch); + GPU_BATCH_DISCARD_SAFE(cache->stroke_batch); + GPU_VERTBUF_DISCARD_SAFE(cache->vbo); + GPU_VERTBUF_DISCARD_SAFE(cache->vbo_col); + GPU_INDEXBUF_DISCARD_SAFE(cache->ibo); + + GPU_BATCH_DISCARD_SAFE(cache->edit_lines_batch); + GPU_BATCH_DISCARD_SAFE(cache->edit_points_batch); + GPU_VERTBUF_DISCARD_SAFE(cache->edit_vbo); + + cache->is_dirty = true; +} + +static GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra) +{ + bGPdata *gpd = (bGPdata *)ob->data; + + GpencilBatchCache *cache = gpd->runtime.gpencil_cache; + if (!gpencil_batch_cache_valid(cache, gpd, cfra)) { + gpencil_batch_cache_clear(cache); + return gpencil_batch_cache_init(ob, cfra); + } + else { + return cache; + } +} + +void DRW_gpencil_batch_cache_dirty_tag(bGPdata *gpd) +{ + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} + +void DRW_gpencil_batch_cache_free(bGPdata *gpd) +{ + gpencil_batch_cache_clear(gpd->runtime.gpencil_cache); + MEM_SAFE_FREE(gpd->runtime.gpencil_cache); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + return; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Formats. + * \{ */ + +/* MUST match the format below. */ +typedef struct gpStrokeVert { + /** Mat is float because we need to pack other float attribs with it. */ + float mat, strength, stroke_id, point_id; + /** Position and thickness packed in the same attribute. */ + float pos[3], thickness; + float uv_fill[2], u_stroke, v_rot; + /** Aspect ratio and hardnes. */ + float aspect_ratio, hardness; +} gpStrokeVert; + +static GPUVertFormat *gpencil_stroke_format(void) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "ma", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "hard", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + /* IMPORTANT: This means having only 4 attributes to fit into GPU module limit of 16 attrib. */ + GPU_vertformat_multiload_enable(&format, 4); + } + return &format; +} + +/* MUST match the format below. */ +typedef struct gpEditVert { + uint vflag; + float weight; +} gpEditVert; + +static GPUVertFormat *gpencil_edit_stroke_format(void) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT); + GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + return &format; +} + +/* MUST match the format below. */ +typedef struct gpColorVert { + float vcol[4]; /* Vertex color */ + float fcol[4]; /* Fill color */ +} gpColorVert; + +static GPUVertFormat *gpencil_color_format(void) +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "col", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "fcol", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + /* IMPORTANT: This means having only 4 attributes to fit into GPU module limit of 16 attrib. */ + GPU_vertformat_multiload_enable(&format, 4); + } + return &format; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex Buffers. + * \{ */ + +typedef struct gpIterData { + bGPdata *gpd; + gpStrokeVert *verts; + gpColorVert *cols; + GPUIndexBufBuilder ibo; + int vert_len; + int tri_len; +} gpIterData; + +static GPUVertBuf *gpencil_dummy_buffer_get(void) +{ + GPUBatch *batch = DRW_gpencil_dummy_buffer_get(); + return batch->verts[0]; +} + +static int gpencil_stroke_is_cyclic(const bGPDstroke *gps) +{ + return ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2); +} + +static void gpencil_buffer_add_point(gpStrokeVert *verts, + gpColorVert *cols, + const bGPDstroke *gps, + const bGPDspoint *pt, + int v, + bool is_endpoint) +{ + /* Note: we use the sign of stength and thickness to pass cap flag. */ + const bool round_cap0 = (gps->caps[0] == GP_STROKE_CAP_ROUND); + const bool round_cap1 = (gps->caps[1] == GP_STROKE_CAP_ROUND); + gpStrokeVert *vert = &verts[v]; + gpColorVert *col = &cols[v]; + copy_v3_v3(vert->pos, &pt->x); + copy_v2_v2(vert->uv_fill, pt->uv_fill); + copy_v4_v4(col->vcol, pt->vert_color); + copy_v4_v4(col->fcol, gps->vert_color_fill); + + /* Encode fill opacity defined by opacity modifier in vertex color alpha. If + * no opacity modifier, the value will be always 1.0f. The opacity factor can be any + * value between 0.0f and 2.0f */ + col->fcol[3] = (((int)(col->fcol[3] * 10000.0f)) * 10.0f) + gps->fill_opacity_fac; + + vert->strength = (round_cap0) ? pt->strength : -pt->strength; + vert->u_stroke = pt->uv_fac; + vert->stroke_id = gps->runtime.stroke_start; + vert->point_id = v; + /* Rotation are in [-90°..90°] range, so we can encode the sign of the angle + the cosine + * because the cosine will always be positive. */ + vert->v_rot = cosf(pt->uv_rot) * signf(pt->uv_rot); + vert->thickness = max_ff(0.0f, gps->thickness * pt->pressure) * (round_cap1 ? 1.0 : -1.0); + /* Tag endpoint material to -1 so they get discarded by vertex shader. */ + vert->mat = (is_endpoint) ? -1 : (gps->mat_nr % GP_MATERIAL_BUFFER_LEN); + + vert->aspect_ratio = gps->aspect_ratio[0] / max_ff(gps->aspect_ratio[1], 1e-8); + vert->hardness = gps->hardeness; +} + +static void gpencil_buffer_add_stroke(gpStrokeVert *verts, + gpColorVert *cols, + const bGPDstroke *gps) +{ + const bGPDspoint *pts = gps->points; + int pts_len = gps->totpoints; + bool is_cyclic = gpencil_stroke_is_cyclic(gps); + int v = gps->runtime.stroke_start; + + /* First point for adjacency (not drawn). */ + int adj_idx = (is_cyclic) ? (pts_len - 1) : min_ii(pts_len - 1, 1); + gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true); + + for (int i = 0; i < pts_len; i++) { + gpencil_buffer_add_point(verts, cols, gps, &pts[i], v++, false); + } + /* Draw line to first point to complete the loop for cyclic strokes. */ + if (is_cyclic) { + gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false); + } + /* Last adjacency point (not drawn). */ + adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2); + gpencil_buffer_add_point(verts, cols, gps, &pts[adj_idx], v++, true); +} + +static void gpencil_buffer_add_fill(GPUIndexBufBuilder *ibo, const bGPDstroke *gps) +{ + int tri_len = gps->tot_triangles; + int v = gps->runtime.stroke_start; + for (int i = 0; i < tri_len; i++) { + uint *tri = gps->triangles[i].verts; + GPU_indexbuf_add_tri_verts(ibo, v + tri[0], v + tri[1], v + tri[2]); + } +} + +static void gpencil_stroke_iter_cb(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + gpIterData *iter = (gpIterData *)thunk; + gpencil_buffer_add_stroke(iter->verts, iter->cols, gps); + if (gps->tot_triangles > 0) { + gpencil_buffer_add_fill(&iter->ibo, gps); + } +} + +static void gp_object_verts_count_cb(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + gpIterData *iter = (gpIterData *)thunk; + + /* Store first index offset */ + gps->runtime.stroke_start = iter->vert_len; + gps->runtime.fill_start = iter->tri_len; + iter->vert_len += gps->totpoints + 2 + gpencil_stroke_is_cyclic(gps); + iter->tri_len += gps->tot_triangles; +} + +static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra) +{ + bGPdata *gpd = (bGPdata *)ob->data; + + if (cache->vbo == NULL) { + /* Should be discarded together. */ + BLI_assert(cache->vbo == NULL && cache->ibo == NULL); + BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL); + /* TODO/PERF: Could be changed to only do it if needed. + * For now it's simpler to assume we always need it + * since multiple viewport could or could not need it. + * Ideally we should have a dedicated onion skin geom batch. */ + /* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */ + bool do_onion = true; + + /* First count how many vertices and triangles are needed for the whole object. */ + gpIterData iter = { + .gpd = gpd, + .verts = NULL, + .ibo = {0}, + .vert_len = 1, /* Start at 1 for the gl_InstanceID trick to work (see vert shader). */ + .tri_len = 0, + }; + BKE_gpencil_visible_stroke_iter(ob, NULL, gp_object_verts_count_cb, &iter, do_onion, cfra); + + /* Create VBOs. */ + GPUVertFormat *format = gpencil_stroke_format(); + GPUVertFormat *format_col = gpencil_color_format(); + cache->vbo = GPU_vertbuf_create_with_format(format); + cache->vbo_col = GPU_vertbuf_create_with_format(format_col); + /* Add extra space at the end of the buffer because of quad load. */ + GPU_vertbuf_data_alloc(cache->vbo, iter.vert_len + 2); + GPU_vertbuf_data_alloc(cache->vbo_col, iter.vert_len + 2); + iter.verts = (gpStrokeVert *)cache->vbo->data; + iter.cols = (gpColorVert *)cache->vbo_col->data; + /* Create IBO. */ + GPU_indexbuf_init(&iter.ibo, GPU_PRIM_TRIS, iter.tri_len, iter.vert_len); + + /* Fill buffers with data. */ + BKE_gpencil_visible_stroke_iter(ob, NULL, gpencil_stroke_iter_cb, &iter, do_onion, cfra); + + /* Mark last 2 verts as invalid. */ + for (int i = 0; i < 2; i++) { + iter.verts[iter.vert_len + i].mat = -1; + } + + /* Finish the IBO. */ + cache->ibo = GPU_indexbuf_build(&iter.ibo); + + /* Create the batches */ + cache->fill_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo); + GPU_batch_vertbuf_add(cache->fill_batch, cache->vbo_col); + cache->stroke_batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL); + GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo, 0); + GPU_batch_instbuf_add_ex(cache->stroke_batch, cache->vbo_col, 0); + + gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; + cache->is_dirty = false; + } +} + +GPUBatch *DRW_cache_gpencil_strokes_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + + return cache->stroke_batch; +} + +GPUBatch *DRW_cache_gpencil_fills_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + + return cache->fill_batch; +} + +static void gp_lines_indices_cb(bGPDlayer *UNUSED(gpl), + bGPDframe *UNUSED(gpf), + bGPDstroke *gps, + void *thunk) +{ + gpIterData *iter = (gpIterData *)thunk; + int pts_len = gps->totpoints + gpencil_stroke_is_cyclic(gps); + + int start = gps->runtime.stroke_start + 1; + int end = start + pts_len; + for (int i = start; i < end; i++) { + GPU_indexbuf_add_generic_vert(&iter->ibo, i); + } + GPU_indexbuf_add_primitive_restart(&iter->ibo); +} + +GPUBatch *DRW_cache_gpencil_face_wireframe_get(Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + int cfra = DEG_get_ctime(draw_ctx->depsgraph); + + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + + if (cache->lines_batch == NULL) { + GPUVertBuf *vbo = cache->vbo; + + gpIterData iter = { + .gpd = ob->data, + .ibo = {0}, + }; + + GPU_indexbuf_init_ex(&iter.ibo, GPU_PRIM_LINE_STRIP, vbo->vertex_len, vbo->vertex_len); + + /* IMPORTANT: Keep in sync with gpencil_edit_batches_ensure() */ + bool do_onion = true; + BKE_gpencil_visible_stroke_iter(ob, NULL, gp_lines_indices_cb, &iter, do_onion, cfra); + + GPUIndexBuf *ibo = GPU_indexbuf_build(&iter.ibo); + + cache->lines_batch = GPU_batch_create_ex(GPU_PRIM_LINE_STRIP, vbo, ibo, GPU_BATCH_OWNS_INDEX); + } + return cache->lines_batch; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Sbuffer stroke batches. + * \{ */ + +bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob) +{ + bGPdata *gpd = (bGPdata *)ob->data; + Brush *brush = gpd->runtime.sbuffer_brush; + /* Convert the sbuffer to a bGPDstroke. */ + if (gpd->runtime.sbuffer_gps == NULL) { + bGPDstroke *gps = MEM_callocN(sizeof(*gps), "bGPDstroke sbuffer"); + gps->totpoints = gpd->runtime.sbuffer_used; + gps->mat_nr = max_ii(0, gpd->runtime.matid - 1); + gps->flag = gpd->runtime.sbuffer_sflag; + gps->thickness = brush->size; + gps->hardeness = brush->gpencil_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); + + /* Reduce slightly the opacity of fill to make easy fill areas while drawing. */ + gps->fill_opacity_fac = 0.8f; + + gps->tot_triangles = max_ii(0, gpd->runtime.sbuffer_used - 2); + gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND; + gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */ + copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill); + gpd->runtime.sbuffer_gps = gps; + } + return gpd->runtime.sbuffer_gps; +} + +static void gpencil_sbuffer_stroke_ensure(bGPdata *gpd, bool do_stroke, bool do_fill) +{ + tGPspoint *tpoints = gpd->runtime.sbuffer; + bGPDstroke *gps = gpd->runtime.sbuffer_gps; + int vert_len = gpd->runtime.sbuffer_used; + + /* DRW_cache_gpencil_sbuffer_stroke_data_get need to have been called previously. */ + BLI_assert(gps != NULL); + + if (do_stroke && (gpd->runtime.sbuffer_stroke_batch == NULL)) { + gps->points = MEM_mallocN(vert_len * sizeof(*gps->points), __func__); + + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + ARegion *region = draw_ctx->region; + Object *ob = draw_ctx->obact; + + BLI_assert(ob && (ob->type == OB_GPENCIL)); + + /* Get origin to reproject points. */ + float origin[3]; + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + ToolSettings *ts = scene->toolsettings; + ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, origin); + + for (int i = 0; i < vert_len; i++) { + ED_gpencil_tpoint_to_point(region, origin, &tpoints[i], &gps->points[i]); + mul_m4_v3(ob->imat, &gps->points[i].x); + bGPDspoint *pt = &gps->points[i]; + copy_v4_v4(pt->vert_color, gpd->runtime.vert_color); + } + /* Calc uv data along the stroke. */ + BKE_gpencil_stroke_uv_update(gps); + + /* Create VBO. */ + GPUVertFormat *format = gpencil_stroke_format(); + GPUVertFormat *format_color = gpencil_color_format(); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(format); + GPUVertBuf *vbo_col = GPU_vertbuf_create_with_format(format_color); + /* Add extra space at the end (and start) of the buffer because of quad load and cyclic. */ + GPU_vertbuf_data_alloc(vbo, 1 + vert_len + 1 + 2); + GPU_vertbuf_data_alloc(vbo_col, 1 + vert_len + 1 + 2); + gpStrokeVert *verts = (gpStrokeVert *)vbo->data; + gpColorVert *cols = (gpColorVert *)vbo_col->data; + + /* Fill buffers with data. */ + gpencil_buffer_add_stroke(verts, cols, gps); + + GPUBatch *batch = GPU_batch_create(GPU_PRIM_TRI_STRIP, gpencil_dummy_buffer_get(), NULL); + GPU_batch_instbuf_add_ex(batch, vbo, true); + GPU_batch_instbuf_add_ex(batch, vbo_col, true); + + gpd->runtime.sbuffer_stroke_batch = batch; + + MEM_freeN(gps->points); + } + + if (do_fill && (gpd->runtime.sbuffer_fill_batch == NULL)) { + /* Create IBO. */ + GPUIndexBufBuilder ibo_builder; + GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, gps->tot_triangles, vert_len); + + if (gps->tot_triangles > 0) { + float(*tpoints2d)[2] = MEM_mallocN(sizeof(*tpoints2d) * vert_len, __func__); + /* Triangulate in 2D. */ + for (int i = 0; i < vert_len; i++) { + copy_v2_v2(tpoints2d[i], &tpoints[i].x); + } + /* Compute directly inside the IBO data buffer. */ + /* OPTI: This is a bottleneck if the stroke is very long. */ + BLI_polyfill_calc(tpoints2d, (uint)vert_len, 0, (uint(*)[3])ibo_builder.data); + /* Add stroke start offset. */ + for (int i = 0; i < gps->tot_triangles * 3; i++) { + ibo_builder.data[i] += gps->runtime.stroke_start; + } + /* HACK since we didn't use the builder API to avoid another malloc and copy, + * we need to set the number of indices manually. */ + ibo_builder.index_len = gps->tot_triangles * 3; + + MEM_freeN(tpoints2d); + } + + GPUIndexBuf *ibo = GPU_indexbuf_build(&ibo_builder); + GPUVertBuf *vbo = gpd->runtime.sbuffer_stroke_batch->inst[0]; + GPUVertBuf *vbo_col = gpd->runtime.sbuffer_stroke_batch->inst[1]; + + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_INDEX); + GPU_batch_vertbuf_add(batch, vbo_col); + + gpd->runtime.sbuffer_fill_batch = batch; + } +} + +GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(Object *ob) +{ + bGPdata *gpd = (bGPdata *)ob->data; + gpencil_sbuffer_stroke_ensure(gpd, true, false); + + return gpd->runtime.sbuffer_stroke_batch; +} + +GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(Object *ob) +{ + bGPdata *gpd = (bGPdata *)ob->data; + /* Fill batch also need stroke batch to be created (vbo is shared). */ + gpencil_sbuffer_stroke_ensure(gpd, true, true); + + return gpd->runtime.sbuffer_fill_batch; +} + +/* Sbuffer batches are temporary. We need to clear it after drawing */ +void DRW_cache_gpencil_sbuffer_clear(Object *ob) +{ + bGPdata *gpd = (bGPdata *)ob->data; + MEM_SAFE_FREE(gpd->runtime.sbuffer_gps); + GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_fill_batch); + GPU_BATCH_DISCARD_SAFE(gpd->runtime.sbuffer_stroke_batch); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/* Edit GPencil Batches */ + +#define GP_EDIT_POINT_SELECTED (1 << 0) +#define GP_EDIT_STROKE_SELECTED (1 << 1) +#define GP_EDIT_MULTIFRAME (1 << 2) +#define GP_EDIT_STROKE_START (1 << 3) +#define GP_EDIT_STROKE_END (1 << 4) + +typedef struct gpEditIterData { + gpEditVert *verts; + int vgindex; +} gpEditIterData; + +static uint32_t gpencil_point_edit_flag(const bool layer_lock, + const bGPDspoint *pt, + int v, + int v_len) +{ + uint32_t sflag = 0; + SET_FLAG_FROM_TEST(sflag, (!layer_lock) && pt->flag & GP_SPOINT_SELECT, GP_EDIT_POINT_SELECTED); + SET_FLAG_FROM_TEST(sflag, v == 0, GP_EDIT_STROKE_START); + SET_FLAG_FROM_TEST(sflag, v == (v_len - 1), GP_EDIT_STROKE_END); + return sflag; +} + +static float gpencil_point_edit_weight(const MDeformVert *dvert, int v, int vgindex) +{ + return (dvert && dvert[v].dw) ? BKE_defvert_find_weight(&dvert[v], vgindex) : -1.0f; +} + +static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl, + bGPDframe *gpf, + bGPDstroke *gps, + void *thunk) +{ + gpEditIterData *iter = (gpEditIterData *)thunk; + const int v_len = gps->totpoints; + const int v = gps->runtime.stroke_start + 1; + MDeformVert *dvert = ((iter->vgindex > -1) && gps->dvert) ? gps->dvert : NULL; + gpEditVert *vert_ptr = iter->verts + v; + + const bool layer_lock = (gpl->flag & GP_LAYER_LOCKED); + uint32_t sflag = 0; + SET_FLAG_FROM_TEST( + sflag, (!layer_lock) && gps->flag & GP_STROKE_SELECT, GP_EDIT_STROKE_SELECTED); + SET_FLAG_FROM_TEST(sflag, gpf->runtime.onion_id != 0.0f, GP_EDIT_MULTIFRAME); + + for (int i = 0; i < v_len; i++) { + vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[i], i, v_len); + vert_ptr->weight = gpencil_point_edit_weight(dvert, i, iter->vgindex); + vert_ptr++; + } + /* Draw line to first point to complete the loop for cyclic strokes. */ + vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len); + vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex); +} + +static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra) +{ + bGPdata *gpd = (bGPdata *)ob->data; + + if (cache->edit_vbo == NULL) { + /* TODO/PERF: Could be changed to only do it if needed. + * For now it's simpler to assume we always need it + * since multiple viewport could or could not need it. + * Ideally we should have a dedicated onion skin geom batch. */ + /* IMPORTANT: Keep in sync with gpencil_batches_ensure() */ + bool do_onion = true; + + /* Vertex counting has already been done for cache->vbo. */ + BLI_assert(cache->vbo); + int vert_len = cache->vbo->vertex_len; + + gpEditIterData iter; + iter.vgindex = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, iter.vgindex)) { + iter.vgindex = -1; + } + + /* Create VBO. */ + GPUVertFormat *format = gpencil_edit_stroke_format(); + cache->edit_vbo = GPU_vertbuf_create_with_format(format); + /* Add extra space at the end of the buffer because of quad load. */ + GPU_vertbuf_data_alloc(cache->edit_vbo, vert_len); + iter.verts = (gpEditVert *)cache->edit_vbo->data; + + /* Fill buffers with data. */ + BKE_gpencil_visible_stroke_iter(ob, NULL, gpencil_edit_stroke_iter_cb, &iter, do_onion, cfra); + + /* Create the batches */ + cache->edit_points_batch = GPU_batch_create(GPU_PRIM_POINTS, cache->vbo, NULL); + GPU_batch_vertbuf_add(cache->edit_points_batch, cache->edit_vbo); + + cache->edit_lines_batch = GPU_batch_create(GPU_PRIM_LINE_STRIP, cache->vbo, NULL); + GPU_batch_vertbuf_add(cache->edit_lines_batch, cache->edit_vbo); + + gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; + cache->is_dirty = false; + } +} + +GPUBatch *DRW_cache_gpencil_edit_lines_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + gpencil_edit_batches_ensure(ob, cache, cfra); + + return cache->edit_lines_batch; +} + +GPUBatch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra); + gpencil_batches_ensure(ob, cache, cfra); + gpencil_edit_batches_ensure(ob, cache, cfra); + + return cache->edit_points_batch; +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index a4e46b3b59f..b1ad1455d8c 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -72,6 +72,8 @@ void DRW_globals_update(void) UI_COLOR_RGBA_FROM_U8(0xB0, 0x00, 0xB0, 0xFF, gb->colorVertexMissingData); UI_GetThemeColor4fv(TH_EDITMESH_ACTIVE, gb->colorEditMeshActive); UI_GetThemeColor4fv(TH_EDGE_SELECT, gb->colorEdgeSelect); + UI_GetThemeColor4fv(TH_GP_VERTEX, gb->colorGpencilVertex); + UI_GetThemeColor4fv(TH_GP_VERTEX_SELECT, gb->colorGpencilVertexSelect); UI_GetThemeColor4fv(TH_EDGE_SEAM, gb->colorEdgeSeam); UI_GetThemeColor4fv(TH_EDGE_SHARP, gb->colorEdgeSharp); diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index bea287da4e9..24d3b7fa7b6 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -68,6 +68,8 @@ typedef struct GlobalsUboStorage { float colorFace[4]; float colorFaceSelect[4]; float colorFaceFreestyle[4]; + float colorGpencilVertex[4]; + float colorGpencilVertexSelect[4]; float colorNormal[4]; float colorVNormal[4]; float colorLNormal[4]; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 4ec49536211..85caf0825e0 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1183,7 +1183,7 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), const bool use_xray = XRAY_ENABLED(v3d); drw_engines_enable_from_engine(engine_type, drawtype, use_xray); - if (gpencil_engine_needed) { + if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) { use_drw_engine(&draw_engine_gpencil_type); } drw_engines_enable_overlays(); @@ -1654,14 +1654,6 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine, void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph) { - /* This function is only valid for Cycles & Workbench - * Eevee does all work in the Eevee render directly. - * Maybe it can be done equal for all engines? - */ - if (STREQ(engine->type->name, "Eevee")) { - return; - } - /* Early out if there are no grease pencil objects, especially important * to avoid failing in in background renders without OpenGL context. */ if (!DRW_render_check_grease_pencil(depsgraph)) { @@ -1740,8 +1732,13 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph GPU_framebuffer_restore(); /* Changing Context */ - /* GPXX Review this context */ - DRW_opengl_context_disable(); + if (re_gl_context != NULL) { + DRW_gpu_render_context_disable(re_gpu_context); + DRW_opengl_render_context_disable(re_gl_context); + } + else { + DRW_opengl_context_disable(); + } DST.buffer_finish_called = false; } @@ -1838,14 +1835,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) drw_view_reset(); engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect); DST.buffer_finish_called = false; - - /* grease pencil: render result is merged in the previous render result. */ - if (DRW_render_check_grease_pencil(depsgraph)) { - DRW_state_reset(); - drw_view_reset(); - DRW_render_gpencil_to_image(engine, render_layer, &render_rect); - DST.buffer_finish_called = false; - } } RE_engine_end_result(engine, render_result, false, false, false); @@ -2215,13 +2204,6 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } } - /* TODO: GPXX Workaround for grease pencil selection while draw manager support a callback from - * scene finish */ - void *data = GPU_viewport_engine_data_get(DST.viewport, &draw_engine_gpencil_type); - if (data != NULL) { - DRW_gpencil_free_runtime_data(data); - } - DRW_state_lock(0); DRW_state_reset(); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 6d415ee95b3..cb825becd73 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -181,7 +181,8 @@ typedef enum { DRW_CMD_DRAW = 0, /* Only sortable type. Must be 0. */ DRW_CMD_DRAW_RANGE = 1, DRW_CMD_DRAW_INSTANCE = 2, - DRW_CMD_DRAW_PROCEDURAL = 3, + DRW_CMD_DRAW_INSTANCE_RANGE = 3, + DRW_CMD_DRAW_PROCEDURAL = 4, /* Other Commands */ DRW_CMD_CLEAR = 12, DRW_CMD_DRWSTATE = 13, @@ -200,6 +201,7 @@ typedef struct DRWCommandDraw { /* Assume DRWResourceHandle to be 0. */ typedef struct DRWCommandDrawRange { GPUBatch *batch; + DRWResourceHandle handle; uint vert_first; uint vert_count; } DRWCommandDrawRange; @@ -211,6 +213,13 @@ typedef struct DRWCommandDrawInstance { uint use_attribs; /* bool */ } DRWCommandDrawInstance; +typedef struct DRWCommandDrawInstanceRange { + GPUBatch *batch; + DRWResourceHandle handle; + uint inst_first; + uint inst_count; +} DRWCommandDrawInstanceRange; + typedef struct DRWCommandDrawProcedural { GPUBatch *batch; DRWResourceHandle handle; @@ -224,7 +233,9 @@ typedef struct DRWCommandSetMutableState { } DRWCommandSetMutableState; typedef struct DRWCommandSetStencil { - uint mask; + uint write_mask; + uint comp_mask; + uint ref; } DRWCommandSetStencil; typedef struct DRWCommandSetSelectID { @@ -243,6 +254,7 @@ typedef union DRWCommand { DRWCommandDraw draw; DRWCommandDrawRange range; DRWCommandDrawInstance instance; + DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; DRWCommandSetMutableState state; DRWCommandSetStencil stencil; @@ -273,6 +285,7 @@ typedef enum { DRW_UNIFORM_BLOCK_OBMATS, DRW_UNIFORM_BLOCK_OBINFOS, DRW_UNIFORM_RESOURCE_CHUNK, + DRW_UNIFORM_RESOURCE_ID, /** Legacy / Fallback */ DRW_UNIFORM_BASE_INSTANCE, DRW_UNIFORM_MODEL_MATRIX, @@ -469,7 +482,6 @@ typedef struct DRWManager { /* Managed by `DRW_state_set`, `DRW_state_reset` */ DRWState state; DRWState state_lock; - uint stencil_mask; /* Per viewport */ GPUViewport *viewport; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 28d5daf011c..83142da051a 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -637,13 +637,12 @@ static void drw_command_draw(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResou cmd->handle = handle; } -static void drw_command_draw_range(DRWShadingGroup *shgroup, - GPUBatch *batch, - uint start, - uint count) +static void drw_command_draw_range( + DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, uint start, uint count) { DRWCommandDrawRange *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_RANGE); cmd->batch = batch; + cmd->handle = handle; cmd->vert_first = start; cmd->vert_count = count; } @@ -661,6 +660,16 @@ static void drw_command_draw_instance(DRWShadingGroup *shgroup, cmd->use_attribs = use_attrib; } +static void drw_command_draw_intance_range( + DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, uint start, uint count) +{ + DRWCommandDrawInstanceRange *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_INSTANCE_RANGE); + cmd->batch = batch; + cmd->handle = handle; + cmd->inst_first = start; + cmd->inst_count = count; +} + static void drw_command_draw_procedural(DRWShadingGroup *shgroup, GPUBatch *batch, DRWResourceHandle handle, @@ -681,11 +690,18 @@ static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf, cmd->select_id = select_id; } -static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup, uint mask) +static void drw_command_set_stencil_mask(DRWShadingGroup *shgroup, + uint write_mask, + uint reference, + uint comp_mask) { - BLI_assert(mask <= 0xFF); + BLI_assert(write_mask <= 0xFF); + BLI_assert(reference <= 0xFF); + BLI_assert(comp_mask <= 0xFF); DRWCommandSetStencil *cmd = drw_command_create(shgroup, DRW_CMD_STENCIL); - cmd->mask = mask; + cmd->write_mask = write_mask; + cmd->comp_mask = comp_mask; + cmd->ref = reference; } static void drw_command_clear(DRWShadingGroup *shgroup, @@ -746,13 +762,27 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup, } } -void DRW_shgroup_call_range(DRWShadingGroup *shgroup, struct GPUBatch *geom, uint v_sta, uint v_ct) +void DRW_shgroup_call_range( + DRWShadingGroup *shgroup, struct Object *ob, GPUBatch *geom, uint v_sta, uint v_ct) +{ + BLI_assert(geom != NULL); + if (G.f & G_FLAG_PICKSEL) { + drw_command_set_select_id(shgroup, NULL, DST.select_id); + } + DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); + drw_command_draw_range(shgroup, geom, handle, v_sta, v_ct); +} + +void DRW_shgroup_call_instance_range( + DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct) { + BLI_assert(i_ct > 0); BLI_assert(geom != NULL); if (G.f & G_FLAG_PICKSEL) { drw_command_set_select_id(shgroup, NULL, DST.select_id); } - drw_command_draw_range(shgroup, geom, v_sta, v_ct); + DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); + drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct); } static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup, @@ -1101,12 +1131,18 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) int info_ubo_location = GPU_shader_get_uniform_block(shader, "infoBlock"); int baseinst_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_BASE_INSTANCE); int chunkid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_CHUNK); + int resourceid_location = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_RESOURCE_ID); if (chunkid_location != -1) { drw_shgroup_uniform_create_ex( shgroup, chunkid_location, DRW_UNIFORM_RESOURCE_CHUNK, NULL, 0, 1); } + if (resourceid_location != -1) { + drw_shgroup_uniform_create_ex( + shgroup, resourceid_location, DRW_UNIFORM_RESOURCE_ID, NULL, 0, 1); + } + if (baseinst_location != -1) { drw_shgroup_uniform_create_ex( shgroup, baseinst_location, DRW_UNIFORM_BASE_INSTANCE, NULL, 0, 1); @@ -1302,9 +1338,18 @@ void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state) drw_command_set_mutable_state(shgroup, 0x0, state); } +void DRW_shgroup_stencil_set(DRWShadingGroup *shgroup, + uint write_mask, + uint reference, + uint comp_mask) +{ + drw_command_set_stencil_mask(shgroup, write_mask, reference, comp_mask); +} + +/* TODO remove this function. */ void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, uint mask) { - drw_command_set_stencil_mask(shgroup, mask); + drw_command_set_stencil_mask(shgroup, 0xFF, mask, 0xFF); } void DRW_shgroup_clear_framebuffer(DRWShadingGroup *shgroup, diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 90de20c0d30..49c71a3f212 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -50,6 +50,7 @@ void DRW_select_load_id(uint id) typedef struct DRWCommandsState { GPUBatch *batch; int resource_chunk; + int resource_id; int base_inst; int inst_count; int v_first; @@ -60,6 +61,7 @@ typedef struct DRWCommandsState { int obinfos_loc; int baseinst_loc; int chunkid_loc; + int resourceid_loc; /* Legacy matrix support. */ int obmat_loc; int obinv_loc; @@ -221,7 +223,6 @@ void drw_state_set(DRWState state) { int test; if (CHANGED_ANY_STORE_VAR(DRW_STATE_STENCIL_TEST_ENABLED, test)) { - DST.stencil_mask = STENCIL_UNDEFINED; if (test) { glEnable(GL_STENCIL_TEST); } @@ -234,11 +235,12 @@ void drw_state_set(DRWState state) /* Blending (all buffer) */ { int test; - if (CHANGED_ANY_STORE_VAR( - DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_ADD | - DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT | - DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT, - test)) { + if (CHANGED_ANY_STORE_VAR(DRW_STATE_BLEND_ALPHA | DRW_STATE_BLEND_ALPHA_PREMUL | + DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_MUL | + DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_OIT | + DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_CUSTOM | + DRW_STATE_LOGIC_INVERT | DRW_STATE_BLEND_SUB, + test)) { if (test) { glEnable(GL_BLEND); @@ -278,6 +280,9 @@ void drw_state_set(DRWState state) /* Let alpha accumulate. */ glBlendFunc(GL_ONE, GL_ONE); } + else if ((state & DRW_STATE_BLEND_SUB) != 0) { + glBlendFunc(GL_ONE, GL_ONE); + } else if ((state & DRW_STATE_BLEND_CUSTOM) != 0) { /* Custom blend parameters using dual source blending. * Can only be used with one Draw Buffer. */ @@ -293,6 +298,13 @@ void drw_state_set(DRWState state) else { BLI_assert(0); } + + if ((state & DRW_STATE_BLEND_SUB) != 0) { + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + } + else { + glBlendEquation(GL_FUNC_ADD); + } } else { glDisable(GL_BLEND); @@ -385,19 +397,23 @@ void drw_state_set(DRWState state) DST.state = state; } -static void drw_stencil_set(uint mask) +static void drw_stencil_state_set(uint write_mask, uint reference, uint compare_mask) { - if (DST.stencil_mask != mask) { - DST.stencil_mask = mask; - if ((DST.state & DRW_STATE_STENCIL_ALWAYS) != 0) { - glStencilFunc(GL_ALWAYS, mask, 0xFF); - } - else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) { - glStencilFunc(GL_EQUAL, mask, 0xFF); - } - else if ((DST.state & DRW_STATE_STENCIL_NEQUAL) != 0) { - glStencilFunc(GL_NOTEQUAL, mask, 0xFF); - } + /* Reminders: + * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value) + * stencil_value being the value stored in the stencil buffer. + * - (writemask & reference) is what gets written if the test condition is fullfiled. + **/ + glStencilMask(write_mask); + + if ((DST.state & DRW_STATE_STENCIL_ALWAYS) != 0) { + glStencilFunc(GL_ALWAYS, reference, compare_mask); + } + else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) { + glStencilFunc(GL_EQUAL, reference, compare_mask); + } + else if ((DST.state & DRW_STATE_STENCIL_NEQUAL) != 0) { + glStencilFunc(GL_NOTEQUAL, reference, compare_mask); } } @@ -978,6 +994,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, state->chunkid_loc = uni->location; GPU_shader_uniform_int(shgroup->shader, uni->location, 0); break; + case DRW_UNIFORM_RESOURCE_ID: + state->resourceid_loc = uni->location; + break; case DRW_UNIFORM_TFEEDBACK_TARGET: BLI_assert(data && (*use_tfeedback == false)); *use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader, @@ -1096,6 +1115,14 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa } state->resource_chunk = chunk; } + + if (state->resourceid_loc != -1) { + int id = DRW_handle_id_get(handle); + if (state->resource_id != id) { + GPU_shader_uniform_int(NULL, state->resourceid_loc, id); + state->resource_id = id; + } + } } static void draw_call_batching_flush(DRWShadingGroup *shgroup, DRWCommandsState *state) @@ -1114,6 +1141,7 @@ static void draw_call_single_do(DRWShadingGroup *shgroup, DRWResourceHandle handle, int vert_first, int vert_count, + int inst_first, int inst_count, bool do_base_instance) { @@ -1142,7 +1170,7 @@ static void draw_call_single_do(DRWShadingGroup *shgroup, batch, vert_first, vert_count, - do_base_instance ? DRW_handle_id_get(&handle) : 0, + do_base_instance ? DRW_handle_id_get(&handle) : inst_first, inst_count, state->baseinst_loc); } @@ -1151,6 +1179,7 @@ static void draw_call_batching_start(DRWCommandsState *state) { state->neg_scale = false; state->resource_chunk = 0; + state->resource_id = -1; state->base_inst = 0; state->inst_count = 0; state->v_first = 0; @@ -1227,6 +1256,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) .obinfos_loc = -1, .baseinst_loc = -1, .chunkid_loc = -1, + .resourceid_loc = -1, .obmat_loc = -1, .obinv_loc = -1, .mvp_loc = -1, @@ -1307,7 +1337,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) drw_state_set((pass_state & ~state.drw_state_disabled) | state.drw_state_enabled); break; case DRW_CMD_STENCIL: - drw_stencil_set(cmd->stencil.mask); + drw_stencil_state_set(cmd->stencil.write_mask, cmd->stencil.ref, cmd->stencil.comp_mask); break; case DRW_CMD_SELECTID: state.select_id = cmd->select_id.select_id; @@ -1315,8 +1345,9 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) break; case DRW_CMD_DRAW: if (!USE_BATCHING || state.obmats_loc == -1 || (G.f & G_FLAG_PICKSEL) || - cmd->draw.batch->inst[0]) { - draw_call_single_do(shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0, true); + cmd->draw.batch->inst) { + draw_call_single_do( + shgroup, &state, cmd->draw.batch, cmd->draw.handle, 0, 0, 0, 0, true); } else { draw_call_batching_do(shgroup, &state, &cmd->draw); @@ -1329,6 +1360,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->procedural.handle, 0, cmd->procedural.vert_count, + 0, 1, true); break; @@ -1339,6 +1371,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) cmd->instance.handle, 0, 0, + 0, cmd->instance.inst_count, cmd->instance.use_attribs == 0); break; @@ -1346,12 +1379,24 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) draw_call_single_do(shgroup, &state, cmd->range.batch, - (DRWResourceHandle)0, + cmd->range.handle, cmd->range.vert_first, cmd->range.vert_count, + 0, 1, true); break; + case DRW_CMD_DRAW_INSTANCE_RANGE: + draw_call_single_do(shgroup, + &state, + cmd->instance_range.batch, + cmd->instance_range.handle, + 0, + 0, + cmd->instance_range.inst_first, + cmd->instance_range.inst_count, + false); + break; } } diff --git a/source/blender/draw/intern/shaders/common_globals_lib.glsl b/source/blender/draw/intern/shaders/common_globals_lib.glsl index 6a3bf150095..cfadb87819c 100644 --- a/source/blender/draw/intern/shaders/common_globals_lib.glsl +++ b/source/blender/draw/intern/shaders/common_globals_lib.glsl @@ -32,6 +32,8 @@ layout(std140) uniform globalsBlock vec4 colorFace; vec4 colorFaceSelect; vec4 colorFaceFreestyle; + vec4 colorGpencilVertex; + vec4 colorGpencilVertexSelect; vec4 colorNormal; vec4 colorVNormal; vec4 colorLNormal; diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl index 09b573d4bb5..45d9f54d943 100644 --- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl +++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl @@ -692,6 +692,10 @@ void SMAANeighborhoodBlendingVS(float2 texcoord, out float4 offset) //----------------------------------------------------------------------------- // Edge Detection Pixel Shaders (First Pass) +# ifndef SMAA_LUMA_WEIGHT +# define SMAA_LUMA_WEIGHT float4(0.2126, 0.7152, 0.0722, 0.0) +# endif + /** * Luma Edge Detection * @@ -716,7 +720,8 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, # endif // Calculate lumas: - float4 weights = float4(0.2126 * 0.5, 0.7152 * 0.5, 0.0722 * 0.5, 0.5); + // float4 weights = float4(0.2126, 0.7152, 0.0722, 0.0); + float4 weights = SMAA_LUMA_WEIGHT; float L = dot(SMAASamplePoint(colorTex, texcoord).rgba, weights); float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgba, weights); @@ -727,9 +732,11 @@ float2 SMAALumaEdgeDetectionPS(float2 texcoord, delta.xy = abs(L - float2(Lleft, Ltop)); float2 edges = step(threshold, delta.xy); +# ifndef SMAA_NO_DISCARD // Then discard if there is no edge: if (dot(edges, float2(1.0, 1.0)) == 0.0) discard; +# endif // Calculate right and bottom deltas: float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgba, weights); @@ -793,9 +800,11 @@ float2 SMAAColorEdgeDetectionPS(float2 texcoord, // We do the usual threshold: float2 edges = step(threshold, delta.xy); +# ifndef SMAA_NO_DISCARD // Then discard if there is no edge: if (dot(edges, float2(1.0, 1.0)) == 0.0) discard; +# endif // Calculate right and bottom deltas: float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 6605e1165d4..3faefd485bf 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -60,11 +60,11 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos) vec2 perp = vec2(-edge.y, edge.x); float dist = dot(perp, frag_co - edge_start); /* Add 0.1 to diffenrentiate with cleared pixels. */ - return vec4(perp * 0.5 + 0.5, dist * 0.25 + 0.5 + 0.1, 0.0); + return vec4(perp * 0.5 + 0.5, dist * 0.25 + 0.5 + 0.1, 1.0); } else { /* Default line if the origin is perfectly aligned with a pixel. */ - return vec4(1.0, 0.0, 0.5 + 0.1, 0.0); + return vec4(1.0, 0.0, 0.5 + 0.1, 1.0); } } @@ -89,7 +89,14 @@ uniform int baseInstance; # define instanceId gl_InstanceID # endif -# define resource_id (baseInstance + instanceId) +# ifdef UNIFORM_RESOURCE_ID +/* This is in the case we want to do a special instance drawcall but still want to have the + * right resourceId and all the correct ubo datas. */ +uniform int resourceId; +# define resource_id resourceId +# else +# define resource_id (baseInstance + instanceId) +# endif /* Use this to declare and pass the value if * the fragment shader uses the resource_id. */ diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index d1fd1ebd06f..ebde475a075 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -5105,10 +5105,16 @@ void ANIM_channel_draw_widgets(const bContext *C, /* Mask Layer. */ UI_block_emboss_set(block, UI_EMBOSS_NONE); - prop = RNA_struct_find_property(&ptr, "mask_layer"); + prop = RNA_struct_find_property(&ptr, "use_mask_layer"); gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop); if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) { - icon = (gpl->flag & GP_LAYER_USE_MASK) ? ICON_MOD_MASK : ICON_LAYER_ACTIVE; + icon = ICON_LAYER_ACTIVE; + if (gpl->flag & GP_LAYER_USE_MASK) { + icon = ICON_MOD_MASK; + } + else { + icon = ICON_LAYER_ACTIVE; + } uiDefAutoButR(block, &ptr, prop, diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 20f565990e9..8b5e22db61a 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -3135,7 +3135,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, if (gpl->flag & GP_LAYER_SELECT) { ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, gpl, ANIMTYPE_GPLAYER); /* update other layer status */ - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); BKE_gpencil_layer_autolock_set(gpd, false); DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); } diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 0b286b4ee2b..1f421d23b27 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -299,7 +299,7 @@ void ED_armature_bone_rename(Main *bmain, if (ob->type == OB_GPENCIL) { bGPdata *gpd = (bGPdata *)ob->data; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->parent != NULL) && (gpl->parent->data == arm)) { if (STREQ(gpl->parsubstr, oldname)) { BLI_strncpy(gpl->parsubstr, newname, MAXBONENAME); diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 21f1801f7eb..512e1ec6270 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -43,7 +43,6 @@ set(SRC gpencil_add_monkey.c gpencil_add_stroke.c gpencil_armature.c - gpencil_brush.c gpencil_convert.c gpencil_data.c gpencil_edit.c @@ -54,9 +53,14 @@ set(SRC gpencil_ops_versioning.c gpencil_paint.c gpencil_primitive.c + gpencil_sculpt_paint.c gpencil_select.c gpencil_undo.c gpencil_utils.c + gpencil_uv.c + gpencil_vertex_paint.c + gpencil_vertex_ops.c + gpencil_weight_paint.c gpencil_intern.h ) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 1a30555a584..ef9b6d2943b 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -33,6 +33,7 @@ #include "BLI_sys_types.h" #include "BLI_math.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLF_api.h" @@ -562,7 +563,7 @@ static void annotation_draw_strokes(bGPdata *UNUSED(gpd), { GPU_program_point_size(true); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* check if stroke can be drawn */ if (annotation_can_draw_stroke(gps, dflag) == false) { continue; @@ -625,7 +626,7 @@ static void annotation_draw_strokes(bGPdata *UNUSED(gpd), } /* Draw selected verts for strokes being edited */ -static void annotation_draw_strokes_edit(bGPdata *gpd, +static void annotation_draw_strokes_edit(bGPdata *UNUSED(gpd), bGPDlayer *gpl, const bGPDframe *gpf, int offsx, @@ -660,7 +661,7 @@ static void annotation_draw_strokes_edit(bGPdata *gpd, GPU_program_point_size(true); /* draw stroke verts */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* check if stroke can be drawn */ if (annotation_can_draw_stroke(gps, dflag) == false) { continue; @@ -689,6 +690,9 @@ static void annotation_draw_strokes_edit(bGPdata *gpd, vsize = bsize + 2; } + /* Why? */ + UNUSED_VARS(vsize); + float selectColor[4]; UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); selectColor[3] = alpha; @@ -709,31 +713,12 @@ static void annotation_draw_strokes_edit(bGPdata *gpd, immBegin(GPU_PRIM_POINTS, gps->totpoints); - /* Draw start and end point differently if enabled stroke direction hint */ - bool show_direction_hint = (gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1); - /* Draw all the stroke points (selected or not) */ bGPDspoint *pt = gps->points; for (int i = 0; i < gps->totpoints; i++, pt++) { /* size and color first */ - if (show_direction_hint && i == 0) { - /* start point in green bigger */ - immAttr3f(color, 0.0f, 1.0f, 0.0f); - immAttr1f(size, vsize + 4); - } - else if (show_direction_hint && (i == gps->totpoints - 1)) { - /* end point in red smaller */ - immAttr3f(color, 1.0f, 0.0f, 0.0f); - immAttr1f(size, vsize + 1); - } - else if (pt->flag & GP_SPOINT_SELECT) { - immAttr3fv(color, selectColor); - immAttr1f(size, vsize); - } - else { - immAttr3fv(color, gpl->color); - immAttr1f(size, bsize); - } + immAttr3fv(color, gpl->color); + immAttr1f(size, bsize); /* then position */ if (gps->flag & GP_STROKE_3DSPACE) { @@ -857,7 +842,7 @@ static void annotation_draw_data_layers( { float ink[4]; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* verify never thickness is less than 1 */ CLAMP_MIN(gpl->thickness, 1.0f); short lthick = gpl->thickness; @@ -872,7 +857,7 @@ static void annotation_draw_data_layers( } /* get frame to draw */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_USE_PREV); if (gpf == NULL) { continue; } diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index fe163e5b6e5..3e33f811225 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -537,9 +537,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure pts->pressure = pt->pressure; pts->strength = pt->strength; pts->time = pt->time; - - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; } /* increment counters */ @@ -604,14 +602,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; gps->thickness = gpl->thickness; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; + gps->fill_opacity_fac = 1.0f; + gps->hardeness = 1.0f; + copy_v2_fl(gps->aspect_ratio, 1.0f); + gps->uv_scale = 1.0f; gps->flag = gpd->runtime.sbuffer_sflag; gps->inittime = p->inittime; - - /* enable recalculation flag by default (only used if hq fill) */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->tot_triangles = 0; /* allocate enough memory for a continuous array for storage points */ gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); @@ -1207,7 +1204,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps ToolSettings *ts = scene->toolsettings; /* get active layer (or add a new one if non-existent) */ - p->gpl = BKE_gpencil_layer_getactive(p->gpd); + p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { /* tag for annotations */ p->gpd->flag |= GP_DATA_ANNOTATIONS; @@ -1235,7 +1232,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps */ bool has_layer_to_erase = false; - if (gpencil_layer_is_editable(p->gpl)) { + if (BKE_gpencil_layer_is_editable(p->gpl)) { /* Ensure that there's stuff to erase here (not including selection mask below)... */ if (p->gpl->actframe && p->gpl->actframe->strokes.first) { has_layer_to_erase = true; @@ -1263,7 +1260,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps add_frame_mode = GP_GETFRAME_ADD_NEW; } - p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 4d879306cec..db13f57b192 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -233,128 +233,8 @@ static void gp_draw_stroke_volumetric_3d(const bGPDspoint *points, } /* --------------- Stroke Fills ----------------- */ -/* calc bounding box in 2d using flat projection data */ -static void gp_calc_2d_bounding_box( - const float (*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand) -{ - copy_v2_v2(minv, points2d[0]); - copy_v2_v2(maxv, points2d[0]); - - for (int i = 1; i < totpoints; i++) { - /* min */ - if (points2d[i][0] < minv[0]) { - minv[0] = points2d[i][0]; - } - if (points2d[i][1] < minv[1]) { - minv[1] = points2d[i][1]; - } - /* max */ - if (points2d[i][0] > maxv[0]) { - maxv[0] = points2d[i][0]; - } - if (points2d[i][1] > maxv[1]) { - maxv[1] = points2d[i][1]; - } - } - /* If not expanded, use a perfect square */ - if (expand == false) { - if (maxv[0] > maxv[1]) { - maxv[1] = maxv[0]; - } - else { - maxv[0] = maxv[1]; - } - } -} - -/* calc texture coordinates using flat projected points */ -static void gp_calc_stroke_text_coordinates(const float (*points2d)[2], - int totpoints, - const float minv[2], - float maxv[2], - float (*r_uv)[2]) -{ - float d[2]; - d[0] = maxv[0] - minv[0]; - d[1] = maxv[1] - minv[1]; - for (int i = 0; i < totpoints; i++) { - r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0]; - r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1]; - } -} - -/* Triangulate stroke for high quality fill - * (this is done only if cache is null or stroke was modified). */ -static void gp_triangulate_stroke_fill(bGPDstroke *gps) -{ - BLI_assert(gps->totpoints >= 3); - - /* allocate memory for temporary areas */ - gps->tot_triangles = gps->totpoints - 2; - uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, - "GP Stroke temp triangulation"); - float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, - "GP Stroke temp 2d points"); - float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data"); - - int direction = 0; - - /* convert to 2d and triangulate */ - BKE_gpencil_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); - BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles); - - /* calc texture coordinates automatically */ - float minv[2]; - float maxv[2]; - /* first needs bounding box data */ - gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false); - /* calc uv data */ - gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv); - - /* Number of triangles */ - gps->tot_triangles = gps->totpoints - 2; - /* save triangulation data in stroke cache */ - if (gps->tot_triangles > 0) { - if (gps->triangles == NULL) { - gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, - "GP Stroke triangulation"); - } - else { - gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); - } - - for (int i = 0; i < gps->tot_triangles; i++) { - bGPDtriangle *stroke_triangle = &gps->triangles[i]; - memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3])); - /* copy texture coordinates */ - copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]); - copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]); - copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]); - } - } - else { - /* No triangles needed - Free anything allocated previously */ - if (gps->triangles) { - MEM_freeN(gps->triangles); - } - - gps->triangles = NULL; - } - - /* disable recalculation flag */ - if (gps->flag & GP_STROKE_RECALC_GEOMETRY) { - gps->flag &= ~GP_STROKE_RECALC_GEOMETRY; - } - - /* clear memory */ - MEM_SAFE_FREE(tmp_triangles); - MEM_SAFE_FREE(points2d); - MEM_SAFE_FREE(uv); -} - /* add a new fill point and texture coordinates to vertex buffer */ static void gp_add_filldata_tobuffer(const bGPDspoint *pt, - const float uv[2], uint pos, uint texcoord, short flag, @@ -375,48 +255,10 @@ static void gp_add_filldata_tobuffer(const bGPDspoint *pt, fpt[2] = 0.0f; /* 2d always is z=0.0f */ } - immAttr2f(texcoord, uv[0], uv[1]); /* texture coordinates */ - immVertex3fv(pos, fpt); /* position */ + immAttr2f(texcoord, pt->uv_fill[0], pt->uv_fill[1]); /* texture coordinates */ + immVertex3fv(pos, fpt); /* position */ } -#if 0 /* GPXX disabled, not used in annotations */ -/* assign image texture for filling stroke */ -static int gp_set_filling_texture(Image *image, short flag) -{ - ImBuf *ibuf; - uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D]; - int error = GL_NO_ERROR; - ImageUser iuser = {NULL}; - void *lock; - - iuser.ok = true; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(image, ibuf, NULL); - return (int)GL_INVALID_OPERATION; - } - - GPU_create_gl_tex( - bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, false, false, image); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (flag & GP_STYLE_COLOR_TEX_CLAMP) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - BKE_image_release_ibuf(image, ibuf, NULL); - - return error; -} -#endif - /* draw fills for shapes */ static void gp_draw_stroke_fill(bGPdata *gpd, bGPDstroke *gps, @@ -428,18 +270,12 @@ static void gp_draw_stroke_fill(bGPdata *gpd, const float color[4]) { BLI_assert(gps->totpoints >= 3); + BLI_assert(gps->tot_triangles >= 1); const bool use_mat = (gpd->mat != NULL); Material *ma = (use_mat) ? gpd->mat[gps->mat_nr] : BKE_material_default_gpencil(); MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; - /* Calculate triangles cache for filling area (must be done only after changes) */ - if ((gps->flag & GP_STROKE_RECALC_GEOMETRY) || (gps->tot_triangles == 0) || - (gps->triangles == NULL)) { - gp_triangulate_stroke_fill(gps); - } - BLI_assert(gps->tot_triangles >= 1); - GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -450,25 +286,13 @@ static void gp_draw_stroke_fill(bGPdata *gpd, immUniform1i("fill_type", gp_style->fill_style); immUniform1f("mix_factor", gp_style->mix_factor); - immUniform1f("gradient_angle", gp_style->gradient_angle); - immUniform1f("gradient_radius", gp_style->gradient_radius); - immUniform1f("pattern_gridsize", gp_style->pattern_gridsize); - immUniform2fv("gradient_scale", gp_style->gradient_scale); - immUniform2fv("gradient_shift", gp_style->gradient_shift); - immUniform1f("texture_angle", gp_style->texture_angle); immUniform2fv("texture_scale", gp_style->texture_scale); immUniform2fv("texture_offset", gp_style->texture_offset); immUniform1f("texture_opacity", gp_style->texture_opacity); - immUniform1i("t_mix", (gp_style->flag & GP_STYLE_FILL_TEX_MIX) != 0); - immUniform1i("t_flip", (gp_style->flag & GP_STYLE_COLOR_FLIP_FILL) != 0); -#if 0 /* GPXX disabled, not used in annotations */ - /* image texture */ - if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) || - (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) { - gp_set_filling_texture(gp_style->ima, gp_style->flag); - } -#endif + immUniform1i("t_mix", (gp_style->flag & GP_MATERIAL_FILL_TEX_MIX) != 0); + immUniform1i("t_flip", (gp_style->flag & GP_MATERIAL_FLIP_FILL) != 0); + /* Draw all triangles for filling the polygon (cache must be calculated before) */ immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3); /* TODO: use batch instead of immediate mode, to share vertices */ @@ -477,7 +301,6 @@ static void gp_draw_stroke_fill(bGPdata *gpd, for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { for (int j = 0; j < 3; j++) { gp_add_filldata_tobuffer(&gps->points[stroke_triangle->verts[j]], - stroke_triangle->uv[j], pos, texcoord, gps->flag, @@ -887,15 +710,15 @@ static void gp_draw_strokes(tGPDdraw *tgpw) Material *ma = (use_mat) ? tgpw->gpd->mat[gps->mat_nr] : BKE_material_default_gpencil(); MaterialGPencilStyle *gp_style = (ma) ? ma->gp_style : NULL; - if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE) || + if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE) || /* if onion and ghost flag do not draw*/ - (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) { + (tgpw->onion && (gp_style->flag & GP_MATERIAL_ONIONSKIN))) { continue; } /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */ if ((tgpw->disable_fill == 1) && (gp_style->fill_rgba[3] > 0.0f) && - ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_STYLE_FILL_SHOW)) { + ((gps->flag & GP_STROKE_NOFILL) == 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW)) { continue; } @@ -980,7 +803,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw) } } - if (gp_style->mode == GP_STYLE_MODE_DOTS) { + if (gp_style->mode == GP_MATERIAL_MODE_DOT) { /* volumetric stroke drawing */ if (tgpw->disable_fill != 1) { gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); @@ -1061,7 +884,7 @@ static void gp_draw_strokes(tGPDdraw *tgpw) copy_v4_v4(ink, tcolor); } } - if (gp_style->mode == GP_STYLE_MODE_DOTS) { + if (gp_style->mode == GP_MATERIAL_MODE_DOT) { /* blob/disk-based "volumetric" drawing */ gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, @@ -1116,113 +939,8 @@ static void gp_draw_strokes(tGPDdraw *tgpw) /* ----- General Drawing ------ */ -/* draw interpolate strokes (used only while operator is running) */ -void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type) -{ - tGPDdraw tgpw; - ARegion *region = CTX_wm_region(C); - RegionView3D *rv3d = region->regiondata; - tGPDinterpolate_layer *tgpil; - Object *obact = CTX_data_active_object(C); - /* Drawing code is expected to run with fully evaluated depsgraph. */ - Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C); - - float color[4]; - - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); - color[3] = 0.6f; - int dflag = 0; - /* if 3d stuff, enable flags */ - if (type == REGION_DRAW_POST_VIEW) { - dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); - } - - tgpw.rv3d = rv3d; - tgpw.depsgraph = depsgraph; - tgpw.ob = obact; - tgpw.gpd = tgpi->gpd; - tgpw.offsx = 0; - tgpw.offsy = 0; - tgpw.winx = tgpi->region->winx; - tgpw.winy = tgpi->region->winy; - tgpw.dflag = dflag; - - /* turn on alpha-blending */ - GPU_blend(true); - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - /* calculate parent position */ - ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat); - if (tgpil->interFrame) { - tgpw.gpl = tgpil->gpl; - tgpw.gpf = tgpil->interFrame; - tgpw.t_gpf = tgpil->interFrame; - tgpw.gps = NULL; - - tgpw.lthick = tgpil->gpl->line_change; - tgpw.opacity = 1.0; - copy_v4_v4(tgpw.tintcolor, color); - tgpw.onion = true; - tgpw.custonion = true; - if (obact->totcol == 0) { - tgpw.gpd->mat = NULL; - } - - gp_draw_strokes(&tgpw); - } - } - GPU_blend(false); -} - /* wrapper to draw strokes for filling operator */ void ED_gp_draw_fill(tGPDdraw *tgpw) { gp_draw_strokes(tgpw); } - -/* draw a short status message in the top-right corner */ -static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *region) -{ - - /* Cannot draw any status text when drawing OpenGL Renders */ - if (G.f & G_FLAG_RENDER_VIEWPORT) { - return; - } - - /* Get bounds of region - Necessary to avoid problems with region overlap. */ - const rcti *rect = ED_region_visible_rect(region); - - /* for now, this should only be used to indicate when we are in stroke editmode */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - const char *printable = IFACE_("GPencil Stroke Editing"); - float printable_size[2]; - - int font_id = BLF_default(); - - BLF_width_and_height( - font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - - int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect->ymax - U.widget_unit); - - /* text label */ - UI_FontThemeColor(font_id, TH_TEXT_HI); -#ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#else - BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#endif - - /* grease pencil icon... */ - // XXX: is this too intrusive? - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); - - xco -= U.widget_unit; - yco -= (int)printable_size[1] / 2; - - UI_icon_draw(xco, yco, ICON_GREASEPENCIL); - - GPU_blend(false); - } -} diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index f4636e81966..86355787b3c 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -58,15 +58,13 @@ /* Loops over the gp-frames for a gp-layer, and applies the given callback */ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *)) { - bGPDframe *gpf; - /* error checker */ if (gpl == NULL) { return false; } /* do loop */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* execute callback */ if (gpf_cb(gpf, scene)) { return true; @@ -83,7 +81,6 @@ bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPD /* make a listing all the gp-frames in a layer as cfraelems */ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) { - bGPDframe *gpf; CfraElem *ce; /* error checking */ @@ -92,7 +89,7 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) } /* loop through gp-frames, adding */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) { ce = MEM_callocN(sizeof(CfraElem), "CfraElem"); @@ -110,15 +107,13 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) /* check if one of the frames in this layer is selected */ bool ED_gplayer_frame_select_check(bGPDlayer *gpl) { - bGPDframe *gpf; - /* error checking */ if (gpl == NULL) { return false; } /* stop at the first one found */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if (gpf->flag & GP_FRAME_SELECT) { return true; } @@ -151,15 +146,13 @@ static void gpframe_select(bGPDframe *gpf, short select_mode) /* set all/none/invert select (like above, but with SELECT_* modes) */ void ED_gpencil_select_frames(bGPDlayer *gpl, short select_mode) { - bGPDframe *gpf; - /* error checking */ if (gpl == NULL) { return; } /* handle according to mode */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { gpframe_select(gpf, select_mode); } } @@ -185,7 +178,7 @@ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode) return; } - gpf = BKE_gpencil_layer_find_frame(gpl, selx); + gpf = BKE_gpencil_layer_frame_find(gpl, selx); if (gpf) { gpframe_select(gpf, select_mode); @@ -195,14 +188,12 @@ void ED_gpencil_select_frame(bGPDlayer *gpl, int selx, short select_mode) /* select the frames in this layer that occur within the bounds specified */ void ED_gplayer_frames_select_box(bGPDlayer *gpl, float min, float max, short select_mode) { - bGPDframe *gpf; - if (gpl == NULL) { return; } /* only select those frames which are in bounds */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if (IN_RANGE(gpf->framenum, min, max)) { gpframe_select(gpf, select_mode); } @@ -215,14 +206,12 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked, short tool, short select_mode) { - bGPDframe *gpf; - if (gpl == NULL) { return; } /* only select frames which are within the region */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* construct a dummy point coordinate to do this testing with */ float pt[2] = {0}; @@ -251,7 +240,6 @@ void ED_gplayer_frames_select_region(KeyframeEditData *ked, /* Delete selected frames */ bool ED_gplayer_frames_delete(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; bool changed = false; /* error checking */ @@ -260,11 +248,9 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl) } /* check for frames to delete */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; - + LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) { if (gpf->flag & GP_FRAME_SELECT) { - BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_frame_delete(gpl, gpf); changed = true; } } @@ -275,16 +261,13 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl) /* Duplicate selected frames from given gp-layer */ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; - /* error checking */ if (gpl == NULL) { return; } /* duplicate selected frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; + LISTBASE_FOREACH_MUTABLE (bGPDframe *, gpf, &gpl->frames) { /* duplicate this frame */ if (gpf->flag & GP_FRAME_SELECT) { @@ -304,13 +287,11 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) */ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type) { - bGPDframe *gpf; - if (gpl == NULL) { return; } - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { if (gpf->flag & GP_FRAME_SELECT) { gpf->key_type = type; } @@ -370,10 +351,9 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) for (ale = anim_data.first; ale; ale = ale->next) { ListBase copied_frames = {NULL, NULL}; bGPDlayer *gpl = (bGPDlayer *)ale->data; - bGPDframe *gpf; /* loop over frames, and copy only selected frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* if frame is selected, make duplicate it and its strokes */ if (gpf->flag & GP_FRAME_SELECT) { /* make a copy of this frame */ @@ -489,7 +469,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) gpfs->framenum += offset; /* get frame to copy data into (if no frame returned, then just ignore) */ - gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW); + gpf = BKE_gpencil_layer_frame_get(gpld, gpfs->framenum, GP_GETFRAME_ADD_NEW); if (gpf) { bGPDstroke *gps, *gpsn; @@ -502,21 +482,15 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) */ for (gps = gpfs->strokes.first; gps; gps = gps->next) { /* make a copy of stroke, then of its points array */ - gpsn = MEM_dupallocN(gps); - gpsn->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - gpsn->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, gpsn); - } - /* duplicate triangle information */ - gpsn->triangles = MEM_dupallocN(gps->triangles); + gpsn = BKE_gpencil_stroke_duplicate(gps, true); + /* append stroke to frame */ BLI_addtail(&gpf->strokes, gpsn); } /* if no strokes (i.e. new frame) added, free gpf */ if (BLI_listbase_is_empty(&gpf->strokes)) { - BKE_gpencil_layer_delframe(gpld, gpf); + BKE_gpencil_layer_frame_delete(gpld, gpf); } } diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 8b4620d233b..f08188c48ce 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -66,17 +66,20 @@ static int gpencil_monkey_color( ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); if (!stroke) { - ma->gp_style->flag &= ~GP_STYLE_STROKE_SHOW; + ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; } if (!fill) { - ma->gp_style->flag &= ~GP_STYLE_FILL_SHOW; + ma->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; } else { - ma->gp_style->flag |= GP_STYLE_FILL_SHOW; + ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW; } return idx; @@ -855,89 +858,117 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, CFRA); /* generate strokes */ - gps = BKE_gpencil_add_stroke(frameFills, color_Skin, 270, 75); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false); BKE_gpencil_stroke_add_points(gps, data0, 270, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data1, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false); BKE_gpencil_stroke_add_points(gps, data2, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); BKE_gpencil_stroke_add_points(gps, data3, 64, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data4, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 64, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); BKE_gpencil_stroke_add_points(gps, data5, 64, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 33, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data6, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Light, 18, 40); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false); BKE_gpencil_stroke_add_points(gps, data7, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); BKE_gpencil_stroke_add_points(gps, data8, 49, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 33, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data9, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Eyes, 49, 60); + gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); BKE_gpencil_stroke_add_points(gps, data10, 49, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); BKE_gpencil_stroke_add_points(gps, data11, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameFills, color_Skin_Shadow, 18, 40); + gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); BKE_gpencil_stroke_add_points(gps, data12, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data13, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data14, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 65, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false); BKE_gpencil_stroke_add_points(gps, data15, 65, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false); BKE_gpencil_stroke_add_points(gps, data16, 34, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data17, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 40); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false); BKE_gpencil_stroke_add_points(gps, data18, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 34, 40); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false); BKE_gpencil_stroke_add_points(gps, data19, 34, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data20, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 64, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false); BKE_gpencil_stroke_add_points(gps, data21, 64, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); BKE_gpencil_stroke_add_points(gps, data22, 26, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 26, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); BKE_gpencil_stroke_add_points(gps, data23, 26, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data24, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); BKE_gpencil_stroke_add_points(gps, data25, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 18, 40); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); BKE_gpencil_stroke_add_points(gps, data26, 18, mat); + BKE_gpencil_stroke_geometry_update(gps); - gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 60); + gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); BKE_gpencil_stroke_add_points(gps, data27, 33, mat); + BKE_gpencil_stroke_geometry_update(gps); /* update depsgraph */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index dfcf7f6e765..d4e17144ca2 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -65,10 +65,13 @@ static int gp_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct, ma = BKE_gpencil_object_material_new(bmain, ob, pct->name, &idx); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); if (fill) { - ma->gp_style->flag |= GP_STYLE_FILL_SHOW; + ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW; } return idx; @@ -240,8 +243,9 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) UNUSED_VARS(frame_color); /* generate stroke */ - gps = BKE_gpencil_add_stroke(frame_lines, color_black, 175, 75); + gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false); BKE_gpencil_stroke_add_points(gps, data0, 175, mat); + BKE_gpencil_stroke_geometry_update(gps); /* update depsgraph */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index c0871fd32fc..02913a19523 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -357,7 +357,7 @@ static void gpencil_add_verts_to_dgroups( } /* loop all strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; bGPDspoint *pt = NULL; @@ -368,7 +368,7 @@ static void gpencil_add_verts_to_dgroups( continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c deleted file mode 100644 index 14354ff38f4..00000000000 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ /dev/null @@ -1,2343 +0,0 @@ -/* - * 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) 2015, Blender Foundation - * This is a new part of Blender - * Brush based operators for editing Grease Pencil strokes - */ - -/** \file - * \ingroup edgpencil - */ - -#include -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_ghash.h" -#include "BLI_math.h" -#include "BLI_rand.h" -#include "BLI_utildefines.h" - -#include "PIL_time.h" - -#include "BLT_translation.h" - -#include "DNA_meshdata_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_object_types.h" - -#include "BKE_colortools.h" -#include "BKE_context.h" -#include "BKE_deform.h" -#include "BKE_gpencil.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_material.h" -#include "BKE_object_deform.h" -#include "BKE_report.h" - -#include "UI_interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" - -#include "UI_view2d.h" - -#include "ED_gpencil.h" -#include "ED_screen.h" -#include "ED_view3d.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "gpencil_intern.h" - -/* ************************************************ */ -/* General Brush Editing Context */ - -/* Context for brush operators */ -typedef struct tGP_BrushEditData { - /* Current editor/region/etc. */ - /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ - struct Main *bmain; - Scene *scene; - Object *object; - - ScrArea *sa; - ARegion *region; - - /* Current GPencil datablock */ - bGPdata *gpd; - - /* Brush Settings */ - GP_Sculpt_Settings *settings; - GP_Sculpt_Data *gp_brush; - GP_Sculpt_Data *gp_brush_old; - - eGP_Sculpt_Types brush_type; - eGP_Sculpt_Types brush_type_old; - eGP_Sculpt_Flag flag; - eGP_Sculpt_SelectMaskFlag mask; - - /* Space Conversion Data */ - GP_SpaceConversion gsc; - - /* Is the brush currently painting? */ - bool is_painting; - bool is_weight_mode; - bool is_transformed; - - /* Start of new sculpt stroke */ - bool first; - - /* Is multiframe editing enabled, and are we using falloff for that? */ - bool is_multiframe; - bool use_multiframe_falloff; - - /* Current frame */ - int cfra; - - /* Brush Runtime Data: */ - /* - position and pressure - * - the *_prev variants are the previous values - */ - float mval[2], mval_prev[2]; - float pressure, pressure_prev; - - /* - effect vector (e.g. 2D/3D translation for grab brush) */ - float dvec[3]; - - /* rotation for evaluated data */ - float rot_eval; - - /* - multiframe falloff factor */ - float mf_falloff; - - /* active vertex group */ - int vrgroup; - - /* brush geometry (bounding box) */ - rcti brush_rect; - - /* Custom data for certain brushes */ - /* - map from bGPDstroke's to structs containing custom data about those strokes */ - GHash *stroke_customdata; - /* - general customdata */ - void *customdata; - - /* Timer for in-place accumulation of brush effect */ - wmTimer *timer; - bool timerTick; /* is this event from a timer */ - - /* Object invert matrix */ - float inv_mat[4][4]; - - RNG *rng; -} tGP_BrushEditData; - -/* Callback for performing some brush operation on a single point */ -typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, - bGPDstroke *gps, - float rotation, - int pt_index, - const int radius, - const int co[2]); - -/* ************************************************ */ -/* Utility Functions */ - -/* apply lock axis reset */ -static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, - bGPDspoint *pt, - const float save_pt[3]) -{ - if (gso->sa->spacetype != SPACE_VIEW3D) { - return; - } - - const ToolSettings *ts = gso->scene->toolsettings; - const View3DCursor *cursor = &gso->scene->cursor; - const int axis = ts->gp_sculpt.lock_axis; - - /* lock axis control */ - switch (axis) { - case GP_LOCKAXIS_X: { - pt->x = save_pt[0]; - break; - } - case GP_LOCKAXIS_Y: { - pt->y = save_pt[1]; - break; - } - case GP_LOCKAXIS_Z: { - pt->z = save_pt[2]; - break; - } - case GP_LOCKAXIS_CURSOR: { - /* Compute a plane with cursor normal and position of the point before do the sculpt. */ - const float scale[3] = {1.0f, 1.0f, 1.0f}; - float plane_normal[3] = {0.0f, 0.0f, 1.0f}; - float plane[4]; - float mat[4][4]; - float r_close[3]; - - loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); - - mul_mat3_m4_v3(mat, plane_normal); - plane_from_point_normal_v3(plane, save_pt, plane_normal); - - /* find closest point to the plane with the new position */ - closest_to_plane_v3(r_close, plane, &pt->x); - copy_v3_v3(&pt->x, r_close); - break; - } - default: { - break; - } - } -} - -/* Context ---------------------------------------- */ - -/* Get the sculpting settings */ -static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene) -{ - return &scene->toolsettings->gp_sculpt; -} - -/* Get the active brush */ -static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) -{ - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; - GP_Sculpt_Data *gp_brush = NULL; - if (is_weight_mode) { - gp_brush = &gset->brush[gset->weighttype]; - } - else { - gp_brush = &gset->brush[gset->brushtype]; - } - - return gp_brush; -} - -/* Brush Operations ------------------------------- */ - -/* Invert behavior of brush? */ -static bool gp_brush_invert_check(tGP_BrushEditData *gso) -{ - /* The basic setting is the brush's setting (from the panel) */ - bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0); - - /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ - if (gso->flag & GP_SCULPT_FLAG_INVERT) { - invert ^= true; - } - - /* set temporary status */ - if (invert) { - gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT; - } - else { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - } - - return invert; -} - -/* Compute strength of effect */ -static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2]) -{ - GP_Sculpt_Data *gp_brush = gso->gp_brush; - - /* basic strength factor from brush settings */ - float influence = gp_brush->strength; - - /* use pressure? */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) { - influence *= gso->pressure; - } - - /* distance fading */ - if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - int mval_i[2]; - round_v2i_v2fl(mval_i, gso->mval); - float distance = (float)len_v2v2_int(mval_i, co); - float fac; - - CLAMP(distance, 0.0f, (float)radius); - fac = 1.0f - (distance / (float)radius); - - influence *= fac; - } - - /* apply multiframe falloff */ - influence *= gso->mf_falloff; - - /* return influence */ - return influence; -} - -/* Force recal filling data */ -static void gp_recalc_geometry(bGPDstroke *gps) -{ - bGPDstroke *gps_orig = gps->runtime.gps_orig; - if (gps_orig) { - gps_orig->flag |= GP_STROKE_RECALC_GEOMETRY; - gps_orig->tot_triangles = 0; - } - else { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - } -} - -/* ************************************************ */ -/* Brush Callbacks */ -/* This section defines the callbacks used by each brush to perform their magic. - * These are called on each point within the brush's radius. - */ - -/* ----------------------------------------------- */ -/* Smooth Brush */ - -/* A simple (but slower + inaccurate) - * smooth-brush implementation to test the algorithm for stroke smoothing. */ -static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - // GP_Sculpt_Data *gp_brush = gso->brush; - float inf = gp_brush_influence_calc(gso, radius, co); - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* perform smoothing */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - BKE_gpencil_smooth_stroke(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); - } - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); - } - - gp_recalc_geometry(gps); - - return true; -} - -/* ----------------------------------------------- */ -/* Line Thickness Brush */ - -/* Make lines thicker or thinner by the specified amounts */ -static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength by 10, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; - - /* apply */ - /* XXX: this is much too strong, - * and it should probably do some smoothing with the surrounding stuff. */ - if (gp_brush_invert_check(gso)) { - /* make line thinner - reduce stroke pressure */ - pt->pressure -= inf; - } - else { - /* make line thicker - increase stroke pressure */ - pt->pressure += inf; - } - - /* Pressure should stay within [0.0, 1.0] - * However, it is nice for volumetric strokes to be able to exceed - * the upper end of this range. Therefore, we don't actually clamp - * down on the upper end. - */ - if (pt->pressure < 0.0f) { - pt->pressure = 0.0f; - } - - return true; -} - -/* ----------------------------------------------- */ -/* Color Strength Brush */ - -/* Make color more or less transparent by the specified amounts */ -static bool gp_brush_strength_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; - CLAMP_MIN(inf, 0.01f); - - /* apply */ - if (gp_brush_invert_check(gso)) { - /* make line more transparent - reduce alpha factor */ - pt->strength -= inf; - } - else { - /* make line more opaque - increase stroke strength */ - pt->strength += inf; - } - /* Strength should stay within [0.0, 1.0] */ - CLAMP(pt->strength, 0.0f, 1.0f); - - /* smooth the strength */ - BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); - - return true; -} - -/* ----------------------------------------------- */ -/* Grab Brush */ - -/* Custom data per stroke for the Grab Brush - * - * This basically defines the strength of the effect for each - * affected stroke point that was within the initial range of - * the brush region. - */ -typedef struct tGPSB_Grab_StrokeData { - /* array of indices to corresponding points in the stroke */ - int *points; - /* array of influence weights for each of the included points */ - float *weights; - /* angles to calc transformation */ - float *rot_eval; - - /* capacity of the arrays */ - int capacity; - /* actual number of items currently stored */ - int size; -} tGPSB_Grab_StrokeData; - -/* initialise custom data for handling this stroke */ -static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) -{ - tGPSB_Grab_StrokeData *data = NULL; - - BLI_assert(gps->totpoints > 0); - - /* Check if there are buffers already (from a prior run) */ - if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { - /* Ensure that the caches are empty - * - Since we reuse these between different strokes, we don't - * want the previous invocation's data polluting the arrays - */ - data = BLI_ghash_lookup(gso->stroke_customdata, gps); - BLI_assert(data != NULL); - - data->size = 0; /* minimum requirement - so that we can repopulate again */ - - memset(data->points, 0, sizeof(int) * data->capacity); - memset(data->weights, 0, sizeof(float) * data->capacity); - memset(data->rot_eval, 0, sizeof(float) * data->capacity); - } - else { - /* Create new instance */ - data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); - - data->capacity = gps->totpoints; - data->size = 0; - - data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); - data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); - data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Rotations"); - - /* hook up to the cache */ - BLI_ghash_insert(gso->stroke_customdata, gps, data); - } -} - -/* store references to stroke points in the initial stage */ -static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, - bGPDstroke *gps, - float rot_eval, - int pt_index, - const int radius, - const int co[2]) -{ - tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); - float inf = gp_brush_influence_calc(gso, radius, co); - - BLI_assert(data != NULL); - BLI_assert(data->size < data->capacity); - - /* insert this point into the set of affected points */ - data->points[data->size] = pt_index; - data->weights[data->size] = inf; - data->rot_eval[data->size] = rot_eval; - data->size++; - - /* done */ - return true; -} - -/* Compute effect vector for grab brush */ -static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) -{ - /* Convert mouse-movements to movement vector */ - // TODO: incorporate pressure into this? - // XXX: screen-space strokes in 3D space will suffer! - if (gso->sa->spacetype == SPACE_VIEW3D) { - RegionView3D *rv3d = gso->region->regiondata; - float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - - /* convert from 2D screenspace to 3D... */ - mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* apply evaluated data transformation */ - if (gso->rot_eval != 0.0f) { - const float cval = cos(gso->rot_eval); - const float sval = sin(gso->rot_eval); - float r[2]; - r[0] = (mval_f[0] * cval) - (mval_f[1] * sval); - r[1] = (mval_f[0] * sval) + (mval_f[1] * cval); - copy_v2_v2(mval_f, r); - } - - ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac); - } - else { - /* 2D - just copy */ - // XXX: view2d? - gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - gso->dvec[2] = 0.0f; /* unused */ - } -} - -/* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, - bGPDstroke *gps, - const float diff_mat[4][4]) -{ - tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); - int i; - - /* Apply dvec to all of the stored points */ - for (i = 0; i < data->size; i++) { - bGPDspoint *pt = &gps->points[data->points[i]]; - float delta[3] = {0.0f}; - - /* get evaluated transformation */ - gso->rot_eval = data->rot_eval[i]; - gp_brush_grab_calc_dvec(gso); - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, data->weights[i]); - - float fpt[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - /* apply transformation */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); - /* apply */ - add_v3_v3v3(&pt->x, fpt, delta); - /* undo transformation to the init parent position */ - float inverse_diff_mat[4][4]; - invert_m4_m4(inverse_diff_mat, diff_mat); - mul_m4_v3(inverse_diff_mat, &pt->x); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - gp_recalc_geometry(gps); -} - -/* free customdata used for handling this stroke */ -static void gp_brush_grab_stroke_free(void *ptr) -{ - tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; - - /* free arrays */ - MEM_SAFE_FREE(data->points); - MEM_SAFE_FREE(data->weights); - MEM_SAFE_FREE(data->rot_eval); - - /* ... and this item itself, since it was also allocated */ - MEM_freeN(data); -} - -/* ----------------------------------------------- */ -/* Push Brush */ -/* NOTE: Depends on gp_brush_grab_calc_dvec() */ -static bool gp_brush_push_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - float inf = gp_brush_influence_calc(gso, radius, co); - float delta[3] = {0.0f}; - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, inf); - - /* apply */ - mul_mat3_m4_v3(gso->inv_mat, delta); /* only rotation component */ - add_v3_v3(&pt->x, delta); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - - /* done */ - return true; -} - -/* ----------------------------------------------- */ -/* Pinch Brush */ -/* Compute reference midpoint for the brush - this is what we'll be moving towards */ -static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) -{ - if (gso->sa->spacetype == SPACE_VIEW3D) { - /* Convert mouse position to 3D space - * See: gpencil_paint.c :: gp_stroke_convertcoords() - */ - RegionView3D *rv3d = gso->region->regiondata; - const float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - - float mval_f[2]; - copy_v2_v2(mval_f, gso->mval); - float mval_prj[2]; - float dvec[3]; - - if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == - V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac); - sub_v3_v3v3(gso->dvec, rvec, dvec); - } - else { - zero_v3(gso->dvec); - } - } - else { - /* Just 2D coordinates */ - // XXX: fix View2D offsets later - gso->dvec[0] = (float)gso->mval[0]; - gso->dvec[1] = (float)gso->mval[1]; - gso->dvec[2] = 0.0f; - } -} - -/* Shrink distance between midpoint and this point... */ -static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float fac, inf; - float vec[3]; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Scale down standard influence value to get it more manageable... - * - No damping = Unmanageable at > 0.5 strength - * - Div 10 = Not enough effect - * - Div 5 = Happy medium... (by trial and error) - */ - inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; - - /* 1) Make this point relative to the cursor/midpoint (dvec) */ - float fpt[3]; - mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); - sub_v3_v3v3(vec, fpt, gso->dvec); - - /* 2) Shrink the distance by pulling the point towards the midpoint - * (0.0 = at midpoint, 1 = at edge of brush region) - * OR - * Increase the distance (if inverting the brush action!) - */ - if (gp_brush_invert_check(gso)) { - /* Inflate (inverse) */ - fac = 1.0f + (inf * inf); /* squared to temper the effect... */ - } - else { - /* Shrink (default) */ - fac = 1.0f - (inf * inf); /* squared to temper the effect... */ - } - mul_v3_fl(vec, fac); - - /* 3) Translate back to original space, with the shrinkage applied */ - add_v3_v3v3(fpt, gso->dvec, vec); - mul_v3_m4v3(&pt->x, gso->object->imat, fpt); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - - gp_recalc_geometry(gps); - - /* done */ - return true; -} - -/* ----------------------------------------------- */ -/* Twist Brush - Rotate Around midpoint */ -/* Take the screenspace coordinates of the point, rotate this around the brush midpoint, - * convert the rotated point and convert it into "data" space - */ - -static bool gp_brush_twist_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float angle, inf; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Angle to rotate by */ - inf = gp_brush_influence_calc(gso, radius, co); - angle = DEG2RADF(1.0f) * inf; - - if (gp_brush_invert_check(gso)) { - /* invert angle that we rotate by */ - angle *= -1; - } - - /* Rotate in 2D or 3D space? */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* Perform rotation in 3D space... */ - RegionView3D *rv3d = gso->region->regiondata; - float rmat[3][3]; - float axis[3]; - float vec[3]; - - /* Compute rotation matrix - rotate around view vector by angle */ - negate_v3_v3(axis, rv3d->persinv[2]); - normalize_v3(axis); - - axis_angle_normalized_to_mat3(rmat, axis, angle); - - /* Rotate point */ - float fpt[3]; - mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); - sub_v3_v3v3(vec, fpt, gso->dvec); /* make relative to center - * (center is stored in dvec) */ - mul_m3_v3(rmat, vec); - add_v3_v3v3(fpt, vec, gso->dvec); /* restore */ - mul_v3_m4v3(&pt->x, gso->object->imat, fpt); - - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - else { - const float axis[3] = {0.0f, 0.0f, 1.0f}; - float vec[3] = {0.0f}; - float rmat[3][3]; - - /* Express position of point relative to cursor, ready to rotate */ - // XXX: There is still some offset here, but it's close to working as expected... - vec[0] = (float)(co[0] - gso->mval[0]); - vec[1] = (float)(co[1] - gso->mval[1]); - - /* rotate point */ - axis_angle_normalized_to_mat3(rmat, axis, angle); - mul_m3_v3(rmat, vec); - - /* Convert back to screen-coordinates */ - vec[0] += (float)gso->mval[0]; - vec[1] += (float)gso->mval[1]; - - /* Map from screen-coordinates to final coordinate space */ - if (gps->flag & GP_STROKE_2DSPACE) { - View2D *v2d = gso->gsc.v2d; - UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); - } - else { - // XXX - copy_v2_v2(&pt->x, vec); - } - } - - gp_recalc_geometry(gps); - - /* done */ - return true; -} - -/* ----------------------------------------------- */ -/* Randomize Brush */ -/* Apply some random jitter to the point */ -static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - bGPDspoint *pt = gps->points + pt_index; - float save_pt[3]; - copy_v3_v3(save_pt, &pt->x); - - /* Amount of jitter to apply depends on the distance of the point to the cursor, - * as well as the strength of the brush - */ - const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; - const float fac = BLI_rng_get_float(gso->rng) * inf; - /* need one flag enabled by default */ - if ((gso->settings->flag & - (GP_SCULPT_SETT_FLAG_APPLY_POSITION | GP_SCULPT_SETT_FLAG_APPLY_STRENGTH | - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS | GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0) { - gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION; - } - - /* apply random to position */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) { - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * view-space as needed. - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - /* scale the displacement by the random displacement, and apply */ - if (BLI_rng_get_float(gso->rng) > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->region->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); - /* compute lock axis */ - gpsculpt_compute_lock_axis(gso, pt, save_pt); - } - } - else { - /* ERROR */ - BLI_assert(!"3D stroke being sculpted in non-3D view"); - } - } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); - } - } - /* apply random to strength */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->strength += fac; - } - else { - pt->strength -= fac; - } - CLAMP_MIN(pt->strength, 0.0f); - CLAMP_MAX(pt->strength, 1.0f); - } - /* apply random to thickness (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->pressure += fac; - } - else { - pt->pressure -= fac; - } - /* only limit lower value */ - CLAMP_MIN(pt->pressure, 0.0f); - } - /* apply random to UV (use pressure) */ - if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) { - if (BLI_rng_get_float(gso->rng) > 0.5f) { - pt->uv_rot += fac; - } - else { - pt->uv_rot -= fac; - } - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - - gp_recalc_geometry(gps); - - /* done */ - return true; -} - -/* Weight Paint Brush */ -/* Change weight paint for vertex groups */ -static bool gp_brush_weight_apply(tGP_BrushEditData *gso, - bGPDstroke *gps, - float UNUSED(rot_eval), - int pt_index, - const int radius, - const int co[2]) -{ - /* create dvert */ - BKE_gpencil_dvert_ensure(gps); - - MDeformVert *dvert = gps->dvert + pt_index; - float inf; - - /* Compute strength of effect - * - We divide the strength by 10, so that users can set "sane" values. - * Otherwise, good default values are in the range of 0.093 - */ - inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; - - /* need a vertex group */ - if (gso->vrgroup == -1) { - if (gso->object) { - BKE_object_defgroup_add(gso->object); - DEG_relations_tag_update(gso->bmain); - gso->vrgroup = 0; - } - } - else { - bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); - if (defgroup->flag & DG_LOCK_WEIGHT) { - return false; - } - } - /* get current weight */ - MDeformWeight *dw = BKE_defvert_ensure_index(dvert, gso->vrgroup); - float curweight = dw ? dw->weight : 0.0f; - - if (gp_brush_invert_check(gso)) { - curweight -= inf; - } - else { - /* increase weight */ - curweight += inf; - /* verify maximum target weight */ - CLAMP_MAX(curweight, gso->gp_brush->weight); - } - - CLAMP(curweight, 0.0f, 1.0f); - if (dw) { - dw->weight = curweight; - } - - return true; -} - -/* ************************************************ */ -/* Non Callback-Based Brushes */ -/* Clone Brush ------------------------------------- */ -/* How this brush currently works: - * - If this is start of the brush stroke, paste immediately under the cursor - * by placing the midpoint of the buffer strokes under the cursor now - * - * - Otherwise, in: - * "Stamp Mode" - Move the newly pasted strokes so that their center follows the cursor - * "Continuous" - Repeatedly just paste new copies for where the brush is now - */ - -/* Custom state data for clone brush */ -typedef struct tGPSB_CloneBrushData { - /* midpoint of the strokes on the clipboard */ - float buffer_midpoint[3]; - - /* number of strokes in the paste buffer (and/or to be created each time) */ - size_t totitems; - - /* for "stamp" mode, the currently pasted brushes */ - bGPDstroke **new_strokes; - - /** Mapping from colors referenced per stroke, to the new colors in the "pasted" strokes. */ - GHash *new_colors; -} tGPSB_CloneBrushData; - -/* Initialise "clone" brush data */ -static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) -{ - tGPSB_CloneBrushData *data; - bGPDstroke *gps; - - /* init custom data */ - gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); - - /* compute midpoint of strokes on clipboard */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - const float dfac = 1.0f / ((float)gps->totpoints); - float mid[3] = {0.0f}; - - bGPDspoint *pt; - int i; - - /* compute midpoint of this stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float co[3]; - - mul_v3_v3fl(co, &pt->x, dfac); - add_v3_v3(mid, co); - } - - /* combine this stroke's data with the main data */ - add_v3_v3(data->buffer_midpoint, mid); - data->totitems++; - } - } - - /* Divide the midpoint by the number of strokes, to finish averaging it */ - if (data->totitems > 1) { - mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); - } - - /* Create a buffer for storing the current strokes */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, - "cloned strokes ptr array"); - } - - /* Init colormap for mapping between the pasted stroke's source color (names) - * and the final colors that will be used here instead. - */ - data->new_colors = gp_copybuf_validate_colormap(C); -} - -/* Free custom data used for "clone" brush */ -static void gp_brush_clone_free(tGP_BrushEditData *gso) -{ - tGPSB_CloneBrushData *data = gso->customdata; - - /* free strokes array */ - if (data->new_strokes) { - MEM_freeN(data->new_strokes); - data->new_strokes = NULL; - } - - /* free copybuf colormap */ - if (data->new_colors) { - BLI_ghash_free(data->new_colors, NULL, NULL); - data->new_colors = NULL; - } - - /* free the customdata itself */ - MEM_freeN(data); - gso->customdata = NULL; -} - -/* Create new copies of the strokes on the clipboard */ -static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) -{ - tGPSB_CloneBrushData *data = gso->customdata; - - Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); - bGPDstroke *gps; - - float delta[3]; - size_t strokes_added = 0; - - /* Compute amount to offset the points by */ - /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ - - gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ - sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); - - /* Copy each stroke into the layer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDstroke *new_stroke; - bGPDspoint *pt; - int i; - - bGPDlayer *gpl = NULL; - /* Try to use original layer. */ - if (gps->runtime.tmp_layerinfo != NULL) { - gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info)); - } - - /* if not available, use active layer. */ - if (gpl == NULL) { - gpl = CTX_data_active_gpencil_layer(C); - } - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); - - /* Make a new stroke */ - new_stroke = MEM_dupallocN(gps); - - new_stroke->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); - } - new_stroke->triangles = MEM_dupallocN(gps->triangles); - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); - - /* Fix color references */ - Material *ma = BLI_ghash_lookup(data->new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); - new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); - if (!ma || new_stroke->mat_nr < 0) { - new_stroke->mat_nr = 0; - } - /* Adjust all the stroke's points, so that the strokes - * get pasted relative to where the cursor is now - */ - for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { - /* Rotate around center new position */ - mul_mat3_m4_v3(gso->object->obmat, &pt->x); /* only rotation component */ - - /* assume that the delta can just be applied, and then everything works */ - add_v3_v3(&pt->x, delta); - mul_m4_v3(gso->object->imat, &pt->x); - } - - /* Store ref for later */ - if ((data->new_strokes) && (strokes_added < data->totitems)) { - data->new_strokes[strokes_added] = new_stroke; - strokes_added++; - } - } - } -} - -/* Move newly-added strokes around - "Stamp" mode of the Clone brush */ -static void gp_brush_clone_adjust(tGP_BrushEditData *gso) -{ - tGPSB_CloneBrushData *data = gso->customdata; - size_t snum; - - /* Compute the amount of movement to apply (overwrites dvec) */ - gso->rot_eval = 0.0f; - gp_brush_grab_calc_dvec(gso); - - /* For each of the stored strokes, apply the offset to each point */ - /* NOTE: Again this assumes that in the 3D view, - * we only have 3d space and not screenspace strokes... */ - for (snum = 0; snum < data->totitems; snum++) { - bGPDstroke *gps = data->new_strokes[snum]; - bGPDspoint *pt; - int i; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) { - /* "Smudge" Effect when falloff is enabled */ - float delta[3] = {0.0f}; - int sco[2] = {0}; - float influence; - - /* compute influence on point */ - gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); - influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco); - - /* adjust the amount of displacement to apply */ - mul_v3_v3fl(delta, gso->dvec, influence); - - /* apply */ - add_v3_v3(&pt->x, delta); - } - else { - /* Just apply the offset - All points move perfectly in sync with the cursor */ - add_v3_v3(&pt->x, gso->dvec); - } - } - } -} - -/* Entrypoint for applying "clone" brush */ -static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) -{ - /* Which "mode" are we operating in? */ - if (gso->first) { - /* Create initial clones */ - gp_brush_clone_add(C, gso); - } - else { - /* Stamp or Continuous Mode */ - if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { - /* Stamp - Proceed to translate the newly added strokes */ - gp_brush_clone_adjust(gso); - } - else { - /* Continuous - Just keep pasting everytime we move */ - /* TODO: The spacing of repeat should be controlled using a - * "stepsize" or similar property? */ - gp_brush_clone_add(C, gso); - } - } - - return true; -} - -/* ************************************************ */ -/* Header Info for GPencil Sculpt */ - -static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) -{ - const char *brush_name = NULL; - char str[UI_MAX_DRAW_STR] = ""; - - RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name); - - BLI_snprintf(str, - sizeof(str), - TIP_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" - " | Ctrl to Invert Action | Wheel Up/Down for Size " - " | Shift-Wheel Up/Down for Strength"), - (brush_name) ? brush_name : ""); - - ED_workspace_status_text(C, str); -} - -/* ************************************************ */ -/* Grease Pencil Sculpting Operator */ - -/* Init/Exit ----------------------------------------------- */ - -static bool gpsculpt_brush_init(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Object *ob = CTX_data_active_object(C); - - const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL; - /* set the brush using the tool */ -#if 0 - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype; -#endif - tGP_BrushEditData *gso; - - /* setup operator data */ - gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); - op->customdata = gso; - - gso->bmain = CTX_data_main(C); - /* store state */ - gso->settings = gpsculpt_get_settings(scene); - gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode); - gso->is_weight_mode = is_weight_mode; - - if (is_weight_mode) { - gso->brush_type = gso->settings->weighttype; - } - else { - gso->brush_type = gso->settings->brushtype; - } - - /* Random generator, only init once. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= POINTER_AS_UINT(gso); - gso->rng = BLI_rng_new(rng_seed); - - gso->is_painting = false; - gso->first = true; - - gso->gpd = ED_gpencil_data_get_active(C); - gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ - - /* some brushes cannot use pressure for radius */ - if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) { - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS; - } - - gso->scene = scene; - gso->object = ob; - if (ob) { - invert_m4_m4(gso->inv_mat, ob->obmat); - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { - gso->vrgroup = -1; - } - /* Check if some modifier can transform the stroke. */ - gso->is_transformed = BKE_gpencil_has_transform_modifiers(ob); - } - else { - unit_m4(gso->inv_mat); - gso->vrgroup = -1; - gso->is_transformed = false; - } - - gso->sa = CTX_wm_area(C); - gso->region = CTX_wm_region(C); - - /* save mask */ - gso->mask = ts->gpencil_selectmode_sculpt; - - /* multiframe settings */ - gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); - gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; - - /* Init multi-edit falloff curve data before doing anything, - * so we won't have to do it again later. */ - if (gso->is_multiframe) { - BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); - } - - /* initialise custom data for brushes */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_CLONE: { - bGPDstroke *gps; - bool found = false; - - /* check that there are some usable strokes in the buffer */ - for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - found = true; - break; - } - } - - if (found == false) { - /* STOP HERE! Nothing to paste! */ - BKE_report(op->reports, - RPT_ERROR, - "Copy some strokes to the clipboard before using the Clone brush to paste " - "copies of them"); - - MEM_freeN(gso); - op->customdata = NULL; - return false; - } - else { - /* initialise customdata */ - gp_brush_clone_init(C, gso); - } - break; - } - - case GP_SCULPT_TYPE_GRAB: { - /* initialise the cache needed for this brush */ - gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); - break; - } - - /* Others - No customdata needed */ - default: - break; - } - - /* setup space conversions */ - gp_point_conversion_init(C, &gso->gsc); - - /* update header */ - gpsculpt_brush_header_set(C, gso); - - /* setup cursor drawing */ - // WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_CROSS); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, true, NULL); - } - return true; -} - -static void gpsculpt_brush_exit(bContext *C, wmOperator *op) -{ - tGP_BrushEditData *gso = op->customdata; - wmWindow *win = CTX_wm_window(C); - - /* free brush-specific data */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: { - /* Free per-stroke customdata - * - Keys don't need to be freed, as those are the strokes - * - Values assigned to those keys do, as they are custom structs - */ - BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); - break; - } - - case GP_SCULPT_TYPE_CLONE: { - /* Free customdata */ - gp_brush_clone_free(gso); - break; - } - - default: - break; - } - - /* unregister timer (only used for realtime) */ - if (gso->timer) { - WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); - } - - if (gso->rng != NULL) { - BLI_rng_free(gso->rng); - } - - /* disable cursor and headerprints */ - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - if (gso->sa->spacetype != SPACE_VIEW3D) { - ED_gpencil_toggle_brush_cursor(C, false, NULL); - } - - /* disable temp invert flag */ - gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT; - - /* free operator data */ - MEM_freeN(gso); - op->customdata = NULL; -} - -/* poll callback for stroke sculpting operator(s) */ -static bool gpsculpt_brush_poll(bContext *C) -{ - /* NOTE: this is a bit slower, but is the most accurate... */ - return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; -} - -/* Init Sculpt Stroke ---------------------------------- */ - -static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso) -{ - bGPdata *gpd = gso->gpd; - - bGPDlayer *gpl; - Scene *scene = gso->scene; - int cfra = CFRA; - - /* only try to add a new frame if this is the first stroke, or the frame has changed */ - if ((gpd == NULL) || (cfra == gso->cfra)) { - return; - } - - /* go through each layer, and ensure that we've got a valid frame to use */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; - - /* Make a new frame to work on if the layer's frame - * and the current scene frame don't match up: - * - This is useful when animating as it saves that "uh-oh" moment when you realize you've - * spent too much time editing the wrong frame. - */ - if (gpf->framenum != cfra) { - BKE_gpencil_frame_addcopy(gpl, cfra); - /* Need tag to recalculate evaluated data to avoid crashes. */ - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - } - } - - /* save off new current frame, so that next update works fine */ - gso->cfra = cfra; -} - -/* Apply ----------------------------------------------- */ - -/* Get angle of the segment relative to the original segment before any transformation - * For strokes with one point only this is impossible to calculate because there isn't a - * valid reference point. - */ -static float gpsculpt_rotation_eval_get(tGP_BrushEditData *gso, - bGPDstroke *gps_eval, - bGPDspoint *pt_eval, - int idx_eval) -{ - /* If multiframe or no modifiers, return 0. */ - if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) { - return 0.0f; - } - - GP_SpaceConversion *gsc = &gso->gsc; - bGPDstroke *gps_orig = gps_eval->runtime.gps_orig; - bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig]; - bGPDspoint *pt_prev_eval = NULL; - bGPDspoint *pt_orig_prev = NULL; - if (idx_eval != 0) { - pt_prev_eval = &gps_eval->points[idx_eval - 1]; - } - else { - if (gps_eval->totpoints > 1) { - pt_prev_eval = &gps_eval->points[idx_eval + 1]; - } - else { - return 0.0f; - } - } - - if (pt_eval->runtime.idx_orig != 0) { - pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1]; - } - else { - if (gps_orig->totpoints > 1) { - pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1]; - } - else { - return 0.0f; - } - } - - /* create 2D vectors of the stroke segments */ - float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2]; - - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a); - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b); - sub_v2_v2(v_orig_a, v_orig_b); - - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a); - gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b); - sub_v2_v2(v_eval_a, v_eval_b); - - return angle_v2v2(v_orig_a, v_eval_a); -} - -/* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, - bGPDstroke *gps, - const float diff_mat[4][4], - GP_BrushApplyCb apply) -{ - GP_SpaceConversion *gsc = &gso->gsc; - rcti *rect = &gso->brush_rect; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? - gso->gp_brush->size * gso->pressure : - gso->gp_brush->size; - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); - bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; - bGPDspoint *pt_active = NULL; - - bGPDspoint *pt1, *pt2; - bGPDspoint *pt = NULL; - int pc1[2] = {0}; - int pc2[2] = {0}; - int i; - int index; - bool include_last = false; - bool changed = false; - float rot_eval = 0.0f; - if (gps->totpoints == 1) { - bGPDspoint pt_temp; - pt = &gps->points[0]; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); - - pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; - /* do boundbox check first */ - if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { - /* only check if point is inside */ - int mval_i[2]; - round_v2i_v2fl(mval_i, gso->mval); - if (len_v2v2_int(mval_i, pc1) <= radius) { - /* apply operation to this point */ - if (pt_active != NULL) { - rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, 0); - changed = apply(gso, gps_active, rot_eval, 0, radius, pc1); - } - } - } - } - else { - /* Loop over the points in the stroke, checking for intersections - * - an intersection means that we touched the stroke - */ - for (i = 0; (i + 1) < gps->totpoints; i++) { - /* Get points to work with */ - pt1 = gps->points + i; - pt2 = gps->points + i + 1; - - /* Skip if neither one is selected - * (and we are only allowed to edit/consider selected points) */ - if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && (!gso->is_weight_mode)) { - if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { - include_last = false; - continue; - } - } - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); - - gp_point_to_parent_space(pt2, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); - - /* Check that point segment of the boundbox of the selection stroke */ - if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || - ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { - /* Check if point segment of stroke had anything to do with - * brush region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_inside_circle( - gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { - /* Apply operation to these points */ - bool ok = false; - - /* To each point individually... */ - pt = &gps->points[i]; - pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; - index = (!is_multiedit) ? pt->runtime.idx_orig : i; - if (pt_active != NULL) { - rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); - ok = apply(gso, gps_active, rot_eval, index, radius, pc1); - } - - /* Only do the second point if this is the last segment, - * and it is unlikely that the point will get handled - * otherwise. - * - * NOTE: There is a small risk here that the second point wasn't really - * actually in-range. In that case, it only got in because - * the line linking the points was! - */ - if (i + 1 == gps->totpoints - 1) { - pt = &gps->points[i + 1]; - pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; - index = (!is_multiedit) ? pt->runtime.idx_orig : i + 1; - if (pt_active != NULL) { - rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i + 1); - ok |= apply(gso, gps_active, rot_eval, index, radius, pc2); - include_last = false; - } - } - else { - include_last = true; - } - - changed |= ok; - } - else if (include_last) { - /* This case is for cases where for whatever reason the second vert (1st here) - * doesn't get included because the whole edge isn't in bounds, - * but it would've qualified since it did with the previous step - * (but wasn't added then, to avoid double-ups). - */ - pt = &gps->points[i]; - pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; - index = (!is_multiedit) ? pt->runtime.idx_orig : i; - if (pt_active != NULL) { - rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); - changed |= apply(gso, gps_active, rot_eval, index, radius, pc1); - include_last = false; - } - } - } - } - } - - return changed; -} - -/* Apply sculpt brushes to strokes in the given frame */ -static bool gpsculpt_brush_do_frame(bContext *C, - tGP_BrushEditData *gso, - bGPDlayer *gpl, - bGPDframe *gpf, - const float diff_mat[4][4]) -{ - bool changed = false; - Object *ob = CTX_data_active_object(C); - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); - - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { - continue; - } - - switch (gso->brush_type) { - case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); - break; - } - - case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); - break; - } - - case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); - break; - } - - case GP_SCULPT_TYPE_GRAB: /* Grab points */ - { - bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; - if (gps_active != NULL) { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps_active); - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points); - } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps_active, diff_mat); - changed |= true; - } - } - break; - } - - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); - break; - } - - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); - break; - } - - case GP_SCULPT_TYPE_WEIGHT: /* Adjust vertex group weight */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply); - break; - } - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - /* Triangulation must be calculated if changed */ - gp_recalc_geometry(gps); - } - - return changed; -} - -/* Perform two-pass brushes which modify the existing strokes */ -static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Object *obact = gso->object; - Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact); - bGPdata *gpd = gso->gpd; - bool changed = false; - - /* Calculate brush-specific data which applies equally to all points */ - switch (gso->brush_type) { - case GP_SCULPT_TYPE_GRAB: /* Grab points */ - case GP_SCULPT_TYPE_PUSH: /* Push points */ - { - /* calculate amount of displacement to apply */ - gso->rot_eval = 0.0f; - gp_brush_grab_calc_dvec(gso); - break; - } - - case GP_SCULPT_TYPE_PINCH: /* Pinch points */ - case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */ - { - /* calculate midpoint of the brush (in data space) */ - gp_brush_calc_midpoint(gso); - break; - } - - case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */ - { - /* compute the displacement vector for the cursor (in data space) */ - gso->rot_eval = 0.0f; - gp_brush_grab_calc_dvec(gso); - break; - } - - default: - break; - } - - /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - /* If no active frame, don't do anything... */ - if (gpl->actframe == NULL) { - continue; - } - /* Get evaluated frames array data */ - int idx_eval = BLI_findindex(&gpd->layers, gpl); - bGPDframe *gpf_eval = &ob_eval->runtime.gpencil_evaluated_frames[idx_eval]; - if (gpf_eval == NULL) { - continue; - } - - /* calculate difference matrix */ - float diff_mat[4][4]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - - /* Active Frame or MultiFrame? */ - if (gso->is_multiframe) { - /* init multiframe falloff options */ - int f_init = 0; - int f_end = 0; - - if (gso->use_multiframe_falloff) { - BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); - } - - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - /* Always do active frame; Otherwise, only include selected frames */ - if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { - /* compute multiframe falloff factor */ - if (gso->use_multiframe_falloff) { - /* Faloff depends on distance to active frame (relative to the overall frame range) */ - gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( - gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); - } - else { - /* No falloff */ - gso->mf_falloff = 1.0f; - } - - /* affect strokes in this frame */ - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); - } - } - } - else { - /* Apply to active frame's strokes */ - gso->mf_falloff = 1.0f; - changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf_eval, diff_mat); - } - } - CTX_DATA_END; - - return changed; -} - -/* Calculate settings for applying brush */ -static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) -{ - tGP_BrushEditData *gso = op->customdata; - GP_Sculpt_Data *gp_brush = gso->gp_brush; - const int radius = ((gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? - gso->gp_brush->size * gso->pressure : - gso->gp_brush->size); - float mousef[2]; - int mouse[2]; - bool changed = false; - - /* Get latest mouse coordinates */ - RNA_float_get_array(itemptr, "mouse", mousef); - gso->mval[0] = mouse[0] = (int)(mousef[0]); - gso->mval[1] = mouse[1] = (int)(mousef[1]); - - gso->pressure = RNA_float_get(itemptr, "pressure"); - - if (RNA_boolean_get(itemptr, "pen_flip")) { - gso->flag |= GP_SCULPT_FLAG_INVERT; - } - else { - gso->flag &= ~GP_SCULPT_FLAG_INVERT; - } - - /* Store coordinates as reference, if operator just started running */ - if (gso->first) { - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - } - - /* Update brush_rect, so that it represents the bounding rectangle of brush */ - gso->brush_rect.xmin = mouse[0] - radius; - gso->brush_rect.ymin = mouse[1] - radius; - gso->brush_rect.xmax = mouse[0] + radius; - gso->brush_rect.ymax = mouse[1] + radius; - - /* Apply brush */ - if (gso->brush_type == GP_SCULPT_TYPE_CLONE) { - changed = gpsculpt_brush_apply_clone(C, gso); - } - else { - changed = gpsculpt_brush_apply_standard(C, gso); - } - - /* Updates */ - if (changed) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - } - - /* Store values for next step */ - gso->mval_prev[0] = gso->mval[0]; - gso->mval_prev[1] = gso->mval[1]; - gso->pressure_prev = gso->pressure; - gso->first = false; -} - -/* Running --------------------------------------------- */ - -/* helper - a record stroke, and apply paint event */ -static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) -{ - tGP_BrushEditData *gso = op->customdata; - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - PointerRNA itemptr; - float mouse[2]; - - mouse[0] = event->mval[0] + 1; - mouse[1] = event->mval[1] + 1; - - /* fill in stroke */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); - RNA_boolean_set(&itemptr, "is_start", gso->first); - - /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ - float pressure = event->tablet.pressure; - /* special exception here for too high pressure values on first touch in - * windows for some tablets: clamp the values to be sane */ - if (pressure >= 0.99f) { - pressure = 1.0f; - } - RNA_float_set(&itemptr, "pressure", pressure); - - if (!gso->is_weight_mode) { - if (event->shift) { - gso->gp_brush_old = gso->gp_brush; - gso->brush_type_old = gso->brush_type; - - gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH]; - gso->brush_type = GP_SCULPT_TYPE_SMOOTH; - } - else { - if (gso->gp_brush_old != NULL) { - gso->gp_brush = gso->gp_brush_old; - gso->brush_type = gso->brush_type_old; - } - } - } - - /* apply */ - gpsculpt_brush_apply(C, op, &itemptr); -} - -/* reapply */ -static int gpsculpt_brush_exec(bContext *C, wmOperator *op) -{ - if (!gpsculpt_brush_init(C, op)) { - return OPERATOR_CANCELLED; - } - - RNA_BEGIN (op->ptr, itemptr, "stroke") { - gpsculpt_brush_apply(C, op, &itemptr); - } - RNA_END; - - gpsculpt_brush_exit(C, op); - - return OPERATOR_FINISHED; -} - -/* start modal painting */ -static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - tGP_BrushEditData *gso = NULL; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; - bool needs_timer = false; - float brush_rate = 0.0f; - - /* the operator cannot work while play animation */ - if (is_playing) { - BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); - - return OPERATOR_CANCELLED; - } - - /* init painting data */ - if (!gpsculpt_brush_init(C, op)) { - return OPERATOR_CANCELLED; - } - - gso = op->customdata; - - /* initialise type-specific data (used for the entire session) */ - switch (gso->brush_type) { - /* Brushes requiring timer... */ - case GP_SCULPT_TYPE_THICKNESS: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_STRENGTH: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_PINCH: - brush_rate = 0.001f; // XXX: hardcoded - needs_timer = true; - break; - - case GP_SCULPT_TYPE_TWIST: - brush_rate = 0.01f; // XXX: hardcoded - needs_timer = true; - break; - - default: - break; - } - - /* register timer for increasing influence by hovering over an area */ - if (needs_timer) { - gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); - } - - /* register modal handler */ - WM_event_add_modal_handler(C, op); - - /* start drawing immediately? */ - if (is_modal == false) { - ARegion *region = CTX_wm_region(C); - - /* ensure that we'll have a new frame to draw on */ - gpsculpt_brush_init_stroke(C, gso); - - /* apply first dab... */ - gso->is_painting = true; - gpsculpt_brush_apply_event(C, op, event); - - /* redraw view with feedback */ - ED_region_tag_redraw(region); - } - - return OPERATOR_RUNNING_MODAL; -} - -/* painting - handle events */ -static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - tGP_BrushEditData *gso = op->customdata; - const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); - bool redraw_region = false; - bool redraw_toolsettings = false; - - /* The operator can be in 2 states: Painting and Idling */ - if (gso->is_painting) { - /* Painting */ - switch (event->type) { - /* Mouse Move = Apply somewhere else */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - /* apply brush effect at new position */ - gpsculpt_brush_apply_event(C, op, event); - - /* force redraw, so that the cursor will at least be valid */ - redraw_region = true; - break; - - /* Timer Tick - Only if this was our own timer */ - case TIMER: - if (event->customdata == gso->timer) { - gso->timerTick = true; - gpsculpt_brush_apply_event(C, op, event); - gso->timerTick = false; - } - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Painting mbut release = Stop painting (back to idle) */ - case LEFTMOUSE: - // BLI_assert(event->val == KM_RELEASE); - if (is_modal) { - /* go back to idling... */ - gso->is_painting = false; - } - else { - /* end sculpt session, since we're not modal */ - gso->is_painting = false; - - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - break; - - /* Abort painting if any of the usual things are tried */ - case MIDDLEMOUSE: - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - } - } - else { - /* Idling */ - BLI_assert(is_modal == true); - - switch (event->type) { - /* Painting mbut press = Start painting (switch to painting state) */ - case LEFTMOUSE: - /* do initial "click" apply */ - gso->is_painting = true; - gso->first = true; - - gpsculpt_brush_init_stroke(C, gso); - gpsculpt_brush_apply_event(C, op, event); - break; - - /* Exit modal operator, based on the "standard" ops */ - case RIGHTMOUSE: - case ESCKEY: - gpsculpt_brush_exit(C, op); - return OPERATOR_FINISHED; - - /* MMB is often used for view manipulations */ - case MIDDLEMOUSE: - return OPERATOR_PASS_THROUGH; - - /* Mouse movements should update the brush cursor - Just redraw the active region */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - redraw_region = true; - break; - - /* Adjust brush settings */ - /* FIXME: Step increments and modifier keys are hardcoded here! */ - case WHEELUPMOUSE: - case PADPLUSKEY: - if (event->shift) { - /* increase strength */ - gso->gp_brush->strength += 0.05f; - CLAMP_MAX(gso->gp_brush->strength, 1.0f); - } - else { - /* increase brush size */ - gso->gp_brush->size += 3; - CLAMP_MAX(gso->gp_brush->size, 300); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - case WHEELDOWNMOUSE: - case PADMINUS: - if (event->shift) { - /* decrease strength */ - gso->gp_brush->strength -= 0.05f; - CLAMP_MIN(gso->gp_brush->strength, 0.0f); - } - else { - /* decrease brush size */ - gso->gp_brush->size -= 3; - CLAMP_MIN(gso->gp_brush->size, 1); - } - - redraw_region = true; - redraw_toolsettings = true; - break; - - /* Change Frame - Allowed */ - case LEFTARROWKEY: - case RIGHTARROWKEY: - case UPARROWKEY: - case DOWNARROWKEY: - return OPERATOR_PASS_THROUGH; - - /* Camera/View Gizmo's - Allowed */ - /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ - case PAD0: - case PAD1: - case PAD2: - case PAD3: - case PAD4: - case PAD5: - case PAD6: - case PAD7: - case PAD8: - case PAD9: - return OPERATOR_PASS_THROUGH; - - /* Unhandled event */ - default: - break; - } - } - - /* Redraw region? */ - if (redraw_region) { - ARegion *region = CTX_wm_region(C); - ED_region_tag_redraw(region); - } - - /* Redraw toolsettings (brush settings)? */ - if (redraw_toolsettings) { - DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - } - - return OPERATOR_RUNNING_MODAL; -} - -/* Also used for weight paint. */ -void GPENCIL_OT_sculpt_paint(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Stroke Sculpt"; - ot->idname = "GPENCIL_OT_sculpt_paint"; - ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX - - /* api callbacks */ - ot->exec = gpsculpt_brush_exec; - ot->invoke = gpsculpt_brush_invoke; - ot->modal = gpsculpt_brush_modal; - ot->cancel = gpsculpt_brush_exit; - ot->poll = gpsculpt_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - PropertyRNA *prop; - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - prop = RNA_def_boolean( - ot->srna, - "wait_for_input", - true, - "Wait for Input", - "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); -} diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 255f17f13cc..934466475a5 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -152,7 +152,7 @@ static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), * - assumes that the active space is the 3D-View */ static void gp_strokepoint_convertcoords(bContext *C, - bGPdata *gpd, + bGPdata *UNUSED(gpd), bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, @@ -174,7 +174,7 @@ static void gp_strokepoint_convertcoords(bContext *C, /* apply parent transform */ float fpt[3]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); mul_v3_m4v3(fpt, diff_mat, &source_pt->x); copy_v3_v3(&pt->x, fpt); @@ -1270,7 +1270,7 @@ static void gp_layer_to_curve(bContext *C, Collection *collection = CTX_data_collection(C); Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; @@ -1410,7 +1410,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe int i; bool valid = true; - if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV)) || + if (!gpl || !(gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) || !(gps = gpf->strokes.first)) { return false; } @@ -1476,8 +1476,8 @@ static bool gp_convert_poll(bContext *C) /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), * and if we are not in edit mode! */ - return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_getactive(gpd)) && - (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV)) && + return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_active_get(gpd)) && + (gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV)) && (gpf->strokes.first) && (!GPENCIL_ANY_EDIT_MODE(gpd))); } @@ -1487,7 +1487,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); Scene *scene = CTX_data_scene(C); const int mode = RNA_enum_get(op->ptr, "type"); const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); @@ -1751,4 +1751,92 @@ void GPENCIL_OT_convert(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ************************************************ */ +/* Generate Grease Pencil from Image. */ +static bool image_to_gpencil_poll(bContext *C) +{ + SpaceLink *sl = CTX_wm_space_data(C); + if (sl->spacetype == SPACE_IMAGE) { + return true; + } + + return false; +} + +static int image_to_gpencil_exec(bContext *C, wmOperator *op) +{ + const float size = RNA_float_get(op->ptr, "size"); + const bool is_mask = RNA_boolean_get(op->ptr, "mask"); + + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceImage *sima = CTX_wm_space_image(C); + bool done = false; + + if (sima->image == NULL) { + return OPERATOR_CANCELLED; + } + + /* Create Object. */ + const float *cur = scene->cursor.location; + ushort local_view_bits = 0; + Object *ob = ED_gpencil_add_object(C, cur, local_view_bits); + DEG_relations_tag_update(bmain); /* added object */ + + /* Create material slot. */ + Material *ma = BKE_gpencil_object_material_new(bmain, ob, "Image Material", NULL); + MaterialGPencilStyle *gp_style = ma->gp_style; + gp_style->mode = GP_MATERIAL_MODE_SQUARE; + + /* Add layer and frame. */ + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true); + bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA); + done = BKE_gpencil_from_image(sima, gpf, size, is_mask); + + if (done) { + /* Delete any selected point. */ + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0); + } + + BKE_reportf(op->reports, RPT_INFO, "Object created"); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_image_to_grease_pencil(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Generate Grease Pencil Object using image as source"; + ot->idname = "GPENCIL_OT_image_to_grease_pencil"; + ot->description = "Generate a Grease Pencil Object using Image as source"; + + /* api callbacks */ + ot->exec = image_to_gpencil_exec; + ot->poll = image_to_gpencil_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_float(ot->srna, + "size", + 0.005f, + 0.0001f, + 10.0f, + "Point Size", + "Size used for graese pencil points", + 0.001f, + 1.0f); + RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, + "mask", + false, + "Generate Mask", + "Create an inverted image for masking using alpha channel"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index a8c3fb02e11..9b33e999a25 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -292,7 +292,7 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : ED_annotation_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* sanity checks */ if (ELEM(NULL, gpd, gpl)) { @@ -309,15 +309,18 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) * - if this is the only layer, this naturally becomes NULL */ if (gpl->prev) { - BKE_gpencil_layer_setactive(gpd, gpl->prev); + BKE_gpencil_layer_active_set(gpd, gpl->prev); } else { - BKE_gpencil_layer_setactive(gpd, gpl->next); + BKE_gpencil_layer_active_set(gpd, gpl->next); } /* delete the layer now... */ BKE_gpencil_layer_delete(gpd, gpl); + /* Reorder masking. */ + BKE_gpencil_layer_mask_sort_all(gpd); + /* notifiers */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -342,7 +345,7 @@ void GPENCIL_OT_layer_remove(wmOperatorType *ot) static bool gp_active_layer_annotation_poll(bContext *C) { bGPdata *gpd = ED_annotation_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); return (gpl != NULL); } @@ -373,7 +376,7 @@ static int gp_layer_move_exec(bContext *C, wmOperator *op) bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : ED_annotation_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); const int direction = RNA_enum_get(op->ptr, "type") * -1; @@ -384,6 +387,9 @@ static int gp_layer_move_exec(bContext *C, wmOperator *op) BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { + /* Reorder masking. */ + BKE_gpencil_layer_mask_sort_all(gpd); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -441,7 +447,7 @@ void GPENCIL_OT_layer_annotation_move(wmOperatorType *ot) static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); bGPDlayer *new_layer; /* sanity checks */ @@ -460,7 +466,7 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); - BKE_gpencil_layer_setactive(gpd, new_layer); + BKE_gpencil_layer_active_set(gpd, new_layer); /* notifiers */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -499,7 +505,7 @@ static bool gp_layer_duplicate_object_poll(bContext *C) } bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl == NULL) { return false; @@ -532,7 +538,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) Object *ob_src = CTX_data_active_object(C); bGPdata *gpd_src = (bGPdata *)ob_src->data; - bGPDlayer *gpl_src = BKE_gpencil_layer_getactive(gpd_src); + bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src); /* Sanity checks. */ if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { @@ -568,7 +574,7 @@ static int gp_layer_duplicate_object_exec(bContext *C, wmOperator *op) for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { /* Make copy of source stroke. */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true); /* Check if material is in destination object, * otherwise add the slot with the material. */ @@ -630,21 +636,21 @@ enum { static int gp_frame_duplicate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); Scene *scene = CTX_data_scene(C); int mode = RNA_enum_get(op->ptr, "mode"); /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) { + if (ELEM(NULL, gpd, gpl_active)) { return OPERATOR_CANCELLED; } if (mode == 0) { - BKE_gpencil_frame_addcopy(gpl, CFRA); + BKE_gpencil_frame_addcopy(gpl_active, CFRA); } else { - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_LOCKED) == 0) { BKE_gpencil_frame_addcopy(gpl, CFRA); } @@ -700,16 +706,13 @@ static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; } /* simply delete strokes which are no fill */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; @@ -778,8 +781,6 @@ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - bGPDstroke *gps = NULL; - bGPDstroke *gpsn = NULL; for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { @@ -788,9 +789,7 @@ static int gp_frame_clean_loose_exec(bContext *C, wmOperator *op) } /* simply delete strokes which are no loose */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; @@ -861,7 +860,7 @@ void GPENCIL_OT_frame_clean_loose(wmOperatorType *ot) static int gp_hide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd); bool unselected = RNA_boolean_get(op->ptr, "unselected"); /* sanity checks */ @@ -870,10 +869,8 @@ static int gp_hide_exec(bContext *C, wmOperator *op) } if (unselected) { - bGPDlayer *gpl; - /* hide unselected */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl != layer) { gpl->flag |= GP_LAYER_HIDE; } @@ -946,7 +943,6 @@ static void gp_reveal_select_frame(bContext *C, bGPDframe *frame, bool select) static int gp_reveal_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; const bool select = RNA_boolean_get(op->ptr, "select"); /* sanity checks */ @@ -954,8 +950,7 @@ static int gp_reveal_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_HIDE) { gpl->flag &= ~GP_LAYER_HIDE; @@ -1008,7 +1003,6 @@ void GPENCIL_OT_reveal(wmOperatorType *ot) static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; /* sanity checks */ if (gpd == NULL) { @@ -1016,7 +1010,7 @@ static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* make all layers non-editable */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag |= GP_LAYER_LOCKED; } @@ -1048,7 +1042,6 @@ void GPENCIL_OT_lock_all(wmOperatorType *ot) static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl; /* sanity checks */ if (gpd == NULL) { @@ -1056,7 +1049,7 @@ static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* make all layers editable again */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag &= ~GP_LAYER_LOCKED; } @@ -1087,8 +1080,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot) static int gp_isolate_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); - bGPDlayer *gpl; + bGPDlayer *layer = BKE_gpencil_layer_active_get(gpd); int flags = GP_LAYER_LOCKED; bool isolate = false; @@ -1102,7 +1094,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) } /* Test whether to isolate or clear all flags */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Skip if this is the active layer */ if (gpl == layer) { continue; @@ -1121,7 +1113,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) /* TODO: Include onion-skinning on this list? */ if (isolate) { /* Set flags on all "other" layers */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl == layer) { continue; } @@ -1132,7 +1124,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) } else { /* Clear flags - Restore everything else */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { gpl->flag &= ~flags; } } @@ -1172,7 +1164,7 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot) static int gp_merge_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl_next = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_next = BKE_gpencil_layer_active_get(gpd); bGPDlayer *gpl_current = gpl_next->prev; if (ELEM(NULL, gpd, gpl_current, gpl_next)) { @@ -1191,7 +1183,7 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op) /* try to find frame in current layer */ bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, POINTER_FROM_INT(gpf->framenum)); if (!frame) { - bGPDframe *actframe = BKE_gpencil_layer_getframe( + bGPDframe *actframe = BKE_gpencil_layer_frame_get( gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); /* duplicate strokes of current active frame */ @@ -1203,10 +1195,29 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op) BLI_movelisttolist(&frame->strokes, &gpf->strokes); } + /* Add Masks to destination layer. */ + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_next->mask_layers) { + /* Don't add merged layers or missing layer names. */ + if (!BKE_gpencil_layer_named_get(gpd, mask->name) || STREQ(mask->name, gpl_next->info) || + STREQ(mask->name, gpl_current->info)) { + continue; + } + if (!BKE_gpencil_layer_mask_named_get(gpl_current, mask->name)) { + bGPDlayer_Mask *mask_new = MEM_dupallocN(mask); + BLI_addtail(&gpl_current->mask_layers, mask_new); + gpl_current->act_mask++; + } + } + /* Set destination layer as active. */ + BKE_gpencil_layer_active_set(gpd, gpl_current); + /* Now delete next layer */ BKE_gpencil_layer_delete(gpd, gpl_next); BLI_ghash_free(gh_frames_cur, NULL, NULL); + /* Reorder masking. */ + BKE_gpencil_layer_mask_sort(gpd, gpl_current); + /* notifiers */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1268,7 +1279,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) } /* Set active layer */ - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); /* updates */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -1297,6 +1308,49 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot) RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); } +static int gp_layer_active_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + int layer_num = RNA_int_get(op->ptr, "layer"); + + /* Try to get layer */ + bGPDlayer *gpl = BLI_findlink(&gpd->layers, layer_num); + + if (gpl == NULL) { + BKE_reportf( + op->reports, RPT_ERROR, "Cannot change to non-existent layer (index = %d)", layer_num); + return OPERATOR_CANCELLED; + } + + /* Set active layer */ + BKE_gpencil_layer_active_set(gpd, gpl); + + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_active(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Active Layer"; + ot->idname = "GPENCIL_OT_layer_active"; + ot->description = "Active Grease Pencil layer"; + + /* callbacks */ + ot->exec = gp_layer_active_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* GPencil layer to use. */ + ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} /* ************************************************ */ /* ******************* Arrange Stroke Up/Down in drawing order ************************** */ @@ -1312,7 +1366,7 @@ static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl_act = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_act = BKE_gpencil_layer_active_get(gpd); /* sanity checks */ if (ELEM(NULL, gpd, gpl_act, gpl_act->actframe)) { @@ -1469,7 +1523,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op) } } /* try to find slot */ - int idx = BKE_gpencil_object_material_get_index(ob, ma); + int idx = BKE_gpencil_object_material_index_get(ob, ma); if (idx < 0) { return OPERATOR_CANCELLED; } @@ -1494,7 +1548,7 @@ static int gp_stroke_change_color_exec(bContext *C, wmOperator *op) continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* only if selected */ if (gps->flag & GP_STROKE_SELECT) { /* skip strokes that are invalid for current view */ @@ -1562,15 +1616,15 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) for (short i = 0; i < *totcol; i++) { Material *tmp_ma = BKE_object_material_get(ob, i + 1); if (tmp_ma) { - tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; + tmp_ma->gp_style->flag |= GP_MATERIAL_LOCKED; DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); } } /* loop all selected strokes and unlock any color */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { /* only if selected */ if (gps->flag & GP_STROKE_SELECT) { @@ -1581,7 +1635,7 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) /* unlock color */ Material *tmp_ma = BKE_object_material_get(ob, gps->mat_nr + 1); if (tmp_ma) { - tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + tmp_ma->gp_style->flag &= ~GP_MATERIAL_LOCKED; DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE); } } @@ -1619,28 +1673,273 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) /* ************************************************ */ /* Drawing Brushes Operators */ -/* ******************* Brush create presets ************************** */ -static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) +/* ******************* Brush resets ************************** */ +static int gp_brush_reset_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); ToolSettings *ts = CTX_data_tool_settings(C); - BKE_brush_gpencil_presets(bmain, ts); + const enum eContextObjectMode mode = CTX_data_mode_enum(C); + Brush *brush = NULL; + + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + Paint *paint = &ts->gp_paint->paint; + brush = paint->brush; + if (brush && brush->gpencil_settings) { + BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); + } + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + Paint *paint = &ts->gp_sculptpaint->paint; + brush = paint->brush; + if (brush && brush->gpencil_settings) { + BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); + } + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + Paint *paint = &ts->gp_weightpaint->paint; + brush = paint->brush; + if (brush && brush->gpencil_settings) { + BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); + } + break; + } + case CTX_MODE_VERTEX_GPENCIL: { + Paint *paint = &ts->gp_vertexpaint->paint; + brush = paint->brush; + if (brush && brush->gpencil_settings) { + BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type); + } + break; + } + default: + break; + } /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_reset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reset Brush"; + ot->idname = "GPENCIL_OT_brush_reset"; + ot->description = "Reset Brush to default parameters"; + + /* api callbacks */ + ot->exec = gp_brush_reset_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static Brush *gp_brush_get_first_by_mode(Main *bmain, + Paint *UNUSED(paint), + const enum eContextObjectMode mode, + char tool) +{ + Brush *brush_next = NULL; + for (Brush *brush = bmain->brushes.first; brush; brush = brush_next) { + brush_next = brush->id.next; + + if (brush->gpencil_settings == NULL) { + continue; + } + + if ((mode == CTX_MODE_PAINT_GPENCIL) && (brush->gpencil_tool == tool)) { + return brush; + } + + if ((mode == CTX_MODE_SCULPT_GPENCIL) && (brush->gpencil_sculpt_tool == tool)) { + return brush; + } + + if ((mode == CTX_MODE_WEIGHT_GPENCIL) && (brush->gpencil_weight_tool == tool)) { + return brush; + } + + if ((mode == CTX_MODE_VERTEX_GPENCIL) && (brush->gpencil_vertex_tool == tool)) { + return brush; + } + } + + return NULL; +} + +static void gp_brush_delete_mode_brushes(Main *bmain, + Paint *paint, + const enum eContextObjectMode mode) +{ + Brush *brush_active = paint->brush; + Brush *brush_next = NULL; + for (Brush *brush = bmain->brushes.first; brush; brush = brush_next) { + brush_next = brush->id.next; + + if ((brush->gpencil_settings == NULL) && (brush->ob_mode != OB_MODE_PAINT_GPENCIL)) { + continue; + } + + short preset = (brush->gpencil_settings) ? brush->gpencil_settings->preset_type : + GP_BRUSH_PRESET_UNKNOWN; + + if (preset != GP_BRUSH_PRESET_UNKNOWN) { + /* Verify to delete only the brushes of the current mode. */ + if (mode == CTX_MODE_PAINT_GPENCIL) { + if ((preset < GP_BRUSH_PRESET_AIRBRUSH) || (preset > GP_BRUSH_PRESET_TINT)) { + continue; + } + if ((brush_active) && (brush_active->gpencil_tool != brush->gpencil_tool)) { + continue; + } + } + + if (mode == CTX_MODE_SCULPT_GPENCIL) { + if ((preset < GP_BRUSH_PRESET_SMOOTH_STROKE) || (preset > GP_BRUSH_PRESET_CLONE_STROKE)) { + continue; + } + if ((brush_active) && (brush_active->gpencil_sculpt_tool != brush->gpencil_sculpt_tool)) { + continue; + } + } + + if (mode == CTX_MODE_WEIGHT_GPENCIL) { + if (preset != GP_BRUSH_PRESET_DRAW_WEIGHT) { + continue; + } + if ((brush_active) && (brush_active->gpencil_weight_tool != brush->gpencil_weight_tool)) { + continue; + } + } + + if (mode == CTX_MODE_VERTEX_GPENCIL) { + if ((preset < GP_BRUSH_PRESET_VERTEX_DRAW) || (preset > GP_BRUSH_PRESET_VERTEX_REPLACE)) { + continue; + } + if ((brush_active) && (brush_active->gpencil_vertex_tool != brush->gpencil_vertex_tool)) { + continue; + } + } + } + + /* Before delete, unpinn any material of the brush. */ + if ((brush->gpencil_settings) && (brush->gpencil_settings->material != NULL)) { + brush->gpencil_settings->material = NULL; + brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED; + } + + BKE_brush_delete(bmain, brush); + } +} + +static int gp_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const enum eContextObjectMode mode = CTX_data_mode_enum(C); + Paint *paint = NULL; + + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + paint = &ts->gp_paint->paint; + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + paint = &ts->gp_sculptpaint->paint; + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + paint = &ts->gp_weightpaint->paint; + break; + } + case CTX_MODE_VERTEX_GPENCIL: { + paint = &ts->gp_vertexpaint->paint; + break; + } + default: + break; + } + + char tool = '0'; + if (paint) { + Brush *brush_active = paint->brush; + if (brush_active) { + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + tool = brush_active->gpencil_tool; + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + tool = brush_active->gpencil_sculpt_tool; + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + tool = brush_active->gpencil_weight_tool; + break; + } + case CTX_MODE_VERTEX_GPENCIL: { + tool = brush_active->gpencil_vertex_tool; + break; + } + default: { + tool = brush_active->gpencil_tool; + break; + } + } + } + + gp_brush_delete_mode_brushes(bmain, paint, mode); + + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: { + BKE_brush_gpencil_paint_presets(bmain, ts); + break; + } + case CTX_MODE_SCULPT_GPENCIL: { + BKE_brush_gpencil_sculpt_presets(bmain, ts); + break; + } + case CTX_MODE_WEIGHT_GPENCIL: { + BKE_brush_gpencil_weight_presets(bmain, ts); + break; + } + case CTX_MODE_VERTEX_GPENCIL: { + BKE_brush_gpencil_vertex_presets(bmain, ts); + break; + } + default: { + break; + } + } + + BKE_paint_toolslots_brush_validate(bmain, paint); + + /* Set Again the first brush of the mode. */ + Brush *deft_brush = gp_brush_get_first_by_mode(bmain, paint, mode, tool); + if (deft_brush) { + BKE_paint_brush_set(paint, deft_brush); + } + /* notifiers */ + DEG_relations_tag_update(bmain); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); + } return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) +void GPENCIL_OT_brush_reset_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Create Preset Brushes"; - ot->idname = "GPENCIL_OT_brush_presets_create"; - ot->description = "Create a set of predefined Grease Pencil drawing brushes"; + ot->name = "Reset All Brushes"; + ot->idname = "GPENCIL_OT_brush_reset_all"; + ot->description = "Delete all mode brushes and recreate a default set"; /* api callbacks */ - ot->exec = gp_brush_presets_create_exec; + ot->exec = gp_brush_reset_all_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2323,9 +2622,9 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) BKE_object_defgroup_unique_name(vgroup, ob_active); BLI_addtail(&ob_active->defbase, vgroup); /* update vertex groups in strokes in original data */ - for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { - for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_src->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { MDeformVert *dvert; int i; for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { @@ -2372,12 +2671,12 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) float inverse_diff_mat[4][4]; /* recalculate all stroke points */ - ED_gpencil_parent_location(depsgraph, ob_iter, gpd_src, gpl_src, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); Material *ma_src = NULL; for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* Reassign material. Look old material and try to find in destination. */ ma_src = BKE_gpencil_material(ob_src, gps->mat_nr + 1); @@ -2468,7 +2767,8 @@ static bool gpencil_active_color_poll(bContext *C) return false; } -/* **************** Lock and hide any color non used in current layer ************************** */ +/* **************** Lock and hide any color non used in current layer ************************** + */ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -2491,16 +2791,16 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_gpencil_material(ob, i + 1); if (ma) { gp_style = ma->gp_style; - gp_style->flag |= GP_STYLE_COLOR_LOCKED; - gp_style->flag |= GP_STYLE_COLOR_HIDE; + gp_style->flag |= GP_MATERIAL_LOCKED; + gp_style->flag |= GP_MATERIAL_HIDE; DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); } } /* loop all selected strokes and unlock any color used in active layer */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { /* skip strokes that are invalid for current view */ @@ -2514,8 +2814,8 @@ static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) gp_style = ma->gp_style; /* unlock/unhide color if not unlocked before */ if (gp_style != NULL) { - gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; - gp_style->flag &= ~GP_STYLE_COLOR_HIDE; + gp_style->flag &= ~GP_MATERIAL_LOCKED; + gp_style->flag &= ~GP_MATERIAL_HIDE; } } } @@ -2555,11 +2855,11 @@ static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) MaterialGPencilStyle *active_color = BKE_gpencil_material_settings(ob, ob->actcol); MaterialGPencilStyle *gp_style; - int flags = GP_STYLE_COLOR_LOCKED; + int flags = GP_MATERIAL_LOCKED; bool isolate = false; if (RNA_boolean_get(op->ptr, "affect_visibility")) { - flags |= GP_STYLE_COLOR_HIDE; + flags |= GP_MATERIAL_HIDE; } if (ELEM(NULL, gpd, active_color)) { @@ -2677,7 +2977,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op) if (ma) { color = ma->gp_style; if (active_color != color) { - color->flag |= GP_STYLE_COLOR_HIDE; + color->flag |= GP_MATERIAL_HIDE; DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); } } @@ -2685,7 +2985,7 @@ static int gpencil_color_hide_exec(bContext *C, wmOperator *op) } else { /* hide selected/active */ - active_color->flag |= GP_STYLE_COLOR_HIDE; + active_color->flag |= GP_MATERIAL_HIDE; } /* updates */ @@ -2739,7 +3039,7 @@ static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_gpencil_material(ob, i + 1); if (ma) { gp_style = ma->gp_style; - gp_style->flag &= ~GP_STYLE_COLOR_HIDE; + gp_style->flag &= ~GP_MATERIAL_HIDE; DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); } } @@ -2792,7 +3092,7 @@ static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_gpencil_material(ob, i + 1); if (ma) { gp_style = ma->gp_style; - gp_style->flag |= GP_STYLE_COLOR_LOCKED; + gp_style->flag |= GP_MATERIAL_LOCKED; DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); } } @@ -2845,7 +3145,7 @@ static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_gpencil_material(ob, i + 1); if (ma) { gp_style = ma->gp_style; - gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + gp_style->flag &= ~GP_MATERIAL_LOCKED; DEG_id_tag_update(&ma->id, ID_RECALC_COPY_ON_WRITE); } } @@ -2900,7 +3200,7 @@ static int gpencil_color_select_exec(bContext *C, wmOperator *op) if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { /* verify something to do */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; @@ -3057,3 +3357,118 @@ bool ED_gpencil_add_lattice_modifier(const bContext *C, return true; } + +/* Masking operators */ +static int gp_layer_mask_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return OPERATOR_CANCELLED; + } + + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + if (gpl_active == NULL) { + return OPERATOR_CANCELLED; + } + char name[128]; + RNA_string_get(op->ptr, "name", name); + bGPDlayer *gpl = BKE_gpencil_layer_named_get(gpd, name); + + if (gpl == NULL) { + BKE_report(op->reports, RPT_ERROR, "Unable to find layer to add"); + return OPERATOR_CANCELLED; + } + + if (gpl == gpl_active) { + BKE_report(op->reports, RPT_ERROR, "Cannot add active layer as mask"); + return OPERATOR_CANCELLED; + } + + if (BKE_gpencil_layer_mask_named_get(gpl_active, name)) { + BKE_report(op->reports, RPT_ERROR, "Layer already added"); + return OPERATOR_CANCELLED; + } + + if (gpl_active->act_mask == 256) { + BKE_report(op->reports, RPT_ERROR, "Maximum number of masking layers reached"); + return OPERATOR_CANCELLED; + } + + BKE_gpencil_layer_mask_add(gpl_active, name); + + /* Reorder masking. */ + BKE_gpencil_layer_mask_sort(gpd, gpl_active); + + /* notifiers */ + if (gpd) { + DEG_id_tag_update(&gpd->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + } + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_mask_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Mask Layer"; + ot->idname = "GPENCIL_OT_layer_mask_add"; + ot->description = "Add new layer as masking"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_mask_add_exec; + ot->poll = gp_add_poll; + + /* properties */ + RNA_def_string(ot->srna, "name", NULL, 128, "Layer", "Name of the layer"); +} + +static int gp_layer_mask_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return OPERATOR_CANCELLED; + } + + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + if (gpl == NULL) { + return OPERATOR_CANCELLED; + } + if (gpl->act_mask > 0) { + bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1); + if (mask != NULL) { + BKE_gpencil_layer_mask_remove(gpl, mask); + if ((gpl->mask_layers.first != NULL) && (gpl->act_mask == 0)) { + gpl->act_mask = 1; + } + } + } + + /* Reorder masking. */ + BKE_gpencil_layer_mask_sort(gpd, gpl); + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Mask Layer"; + ot->idname = "GPENCIL_OT_layer_mask_remove"; + ot->description = "Remove Layer Mask"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_mask_remove_exec; + ot->poll = gp_active_layer_poll; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e5caeb93c73..f8ad34e8d14 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -355,7 +355,7 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) Paint *paint = &ts->gp_paint->paint; /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { - BKE_brush_gpencil_presets(bmain, ts); + BKE_brush_gpencil_paint_presets(bmain, ts); } BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint); } @@ -414,6 +414,9 @@ static bool gpencil_sculptmode_toggle_poll(bContext *C) static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const bool back = RNA_boolean_get(op->ptr, "back"); struct wmMsgBus *mbus = CTX_wm_message_bus(C); @@ -450,6 +453,12 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) ob->mode = mode; } + if (mode == OB_MODE_SCULPT_GPENCIL) { + /* be sure we have brushes */ + BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint); + BKE_paint_toolslots_brush_validate(bmain, &ts->gp_sculptpaint->paint); + } + /* setup other modes */ ED_gpencil_setup_modes(C, gpd, mode); /* set cache as dirty */ @@ -504,6 +513,9 @@ static bool gpencil_weightmode_toggle_poll(bContext *C) static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); + ToolSettings *ts = CTX_data_tool_settings(C); + const bool back = RNA_boolean_get(op->ptr, "back"); struct wmMsgBus *mbus = CTX_wm_message_bus(C); @@ -540,6 +552,12 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) ob->mode = mode; } + if (mode == OB_MODE_WEIGHT_GPENCIL) { + /* be sure we have brushes */ + BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint); + BKE_paint_toolslots_brush_validate(bmain, &ts->gp_weightpaint->paint); + } + /* setup other modes */ ED_gpencil_setup_modes(C, gpd, mode); /* set cache as dirty */ @@ -580,6 +598,104 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +/* Vertex Paint Mode Management */ + +static bool gpencil_vertexmode_toggle_poll(bContext *C) +{ + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; +} +static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) { + return OPERATOR_CANCELLED; + } + + /* Just toggle paintmode flag... */ + gpd->flag ^= GP_DATA_STROKE_VERTEXMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) { + mode = OB_MODE_VERTEX_GPENCIL; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + if (mode == OB_MODE_VERTEX_GPENCIL) { + /* be sure we have brushes */ + BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint); + BKE_paint_toolslots_brush_validate(bmain, &ts->gp_vertexpaint->paint); + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + if (is_object) { + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + } + if (G.background == false) { + WM_toolsystem_update_from_context_view3d(C); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertexmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Vertex Mode Toggle"; + ot->idname = "GPENCIL_OT_vertexmode_toggle"; + ot->description = "Enter/Exit vertex paint mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_vertexmode_toggle_exec; + ot->poll = gpencil_vertexmode_toggle_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + prop = RNA_def_boolean( + ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + /* ************************************************ */ /* Stroke Editing Operators */ @@ -659,23 +775,17 @@ static void gp_duplicate_points(const bGPDstroke *gps, else if (i == gps->totpoints - 1) { len = i - start_idx + 1; } - // printf("copying from %d to %d = %d\n", start_idx, i, len); /* make copies of the relevant data */ if (len) { bGPDstroke *gpsd; /* make a stupid copy first of the entire stroke (to get the flags too) */ - gpsd = MEM_dupallocN(gps); + gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false); /* saves original layer name */ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); - /* initialize triangle memory - will be calculated on next redraw */ - gpsd->triangles = NULL; - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->tot_triangles = 0; - /* now, make a new points array, and copy of the relevant parts */ gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy"); memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); @@ -695,8 +805,11 @@ static void gp_duplicate_points(const bGPDstroke *gps, } } + BKE_gpencil_stroke_geometry_update(gpsd); + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; + BLI_addtail(new_strokes, gpsd); /* cleanup + reset for next */ @@ -746,17 +859,12 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) bGPDstroke *gpsd; /* make direct copies of the stroke and its points */ - gpsd = MEM_dupallocN(gps); + gpsd = BKE_gpencil_stroke_duplicate(gps, true); + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); - gpsd->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - gpsd->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, gpsd); - } - /* triangle information - will be calculated on next redraw */ - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->triangles = NULL; + /* Initialize triangle information. */ + BKE_gpencil_stroke_geometry_update(gpsd); /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; @@ -827,6 +935,7 @@ static void copy_move_point(bGPDstroke *gps, pt_final->flag = pt->flag; pt_final->uv_fac = pt->uv_fac; pt_final->uv_rot = pt->uv_rot; + copy_v4_v4(pt_final->vert_color, pt->vert_color); if (gps->dvert != NULL) { MDeformVert *dvert = &temp_dverts[from_idx]; @@ -864,7 +973,7 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) pt = &gps->points[i]; if (pt->flag == GP_SPOINT_SELECT) { /* duplicate original stroke data */ - bGPDstroke *gps_new = MEM_dupallocN(gps); + bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false); gps_new->prev = gps_new->next = NULL; /* add new points array */ @@ -876,14 +985,15 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps_new->triangles = NULL; - gps_new->tot_triangles = 0; BLI_insertlinkafter(&gpf->strokes, gps, gps_new); /* copy selected point data to new stroke */ copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + BKE_gpencil_stroke_geometry_update(gps_new); + /* deselect orinal point */ pt->flag &= ~GP_SPOINT_SELECT; } @@ -926,7 +1036,6 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) copy_move_point(gps, temp_points, temp_dverts, i, i2, false); i2++; } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; /* If first point, add new point at the beginning. */ if (do_first) { @@ -951,6 +1060,9 @@ static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps) pt->flag |= GP_SPOINT_SELECT; } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + MEM_SAFE_FREE(temp_points); MEM_SAFE_FREE(temp_dverts); } @@ -1190,7 +1302,8 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) bGPDstroke *gpsd; /* make direct copies of the stroke and its points */ - gpsd = MEM_dupallocN(gps); + gpsd = BKE_gpencil_stroke_duplicate(gps, false); + /* saves original layer name */ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); gpsd->points = MEM_dupallocN(gps->points); @@ -1199,10 +1312,8 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) BKE_gpencil_stroke_weights_duplicate(gps, gpsd); } - /* triangles cache - will be recalculated on next redraw */ - gpsd->flag |= GP_STROKE_RECALC_GEOMETRY; - gpsd->tot_triangles = 0; - gpsd->triangles = NULL; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gpsd); /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; @@ -1267,6 +1378,9 @@ void GPENCIL_OT_copy(wmOperatorType *ot) static bool gp_strokes_paste_poll(bContext *C) { + if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) { + return false; + } /* 1) Must have GP datablock to paste to * - We don't need to have an active layer though, as that can easily get added * - If the active layer is locked, we can't paste there, @@ -1285,20 +1399,17 @@ typedef enum eGP_PasteMode { static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ + bGPdata *gpd = (bGPdata *)ob->data; + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */ Scene *scene = CTX_data_scene(C); bGPDframe *gpf; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); + const bool on_back = RNA_boolean_get(op->ptr, "paste_back"); GHash *new_colors; - /* check for various error conditions */ - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); - return OPERATOR_CANCELLED; - } - else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + /* Check for various error conditions. */ + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); return OPERATOR_CANCELLED; } @@ -1312,7 +1423,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) /* no active layer - let's just create one */ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } - else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { + else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { BKE_report( op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); return OPERATOR_CANCELLED; @@ -1330,17 +1441,6 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } if (ok == false) { - /* XXX: this check is not 100% accurate - * (i.e. image editor is incompatible with normal 2D strokes), - * but should be enough to give users a good idea of what's going on. - */ - if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) { - BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View"); - } - else { - BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors"); - } - return OPERATOR_CANCELLED; } } @@ -1362,14 +1462,15 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) new_colors = gp_copybuf_validate_colormap(C); /* Copy over the strokes from the buffer (and adjust the colors) */ - for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + bGPDstroke *gps_init = (!on_back) ? gp_strokes_copypastebuf.first : gp_strokes_copypastebuf.last; + for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ if (type != GP_COPY_TO_ACTIVE) { gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info)); if (gpl == NULL) { /* no layer - use active (only if layer deleted before paste) */ - gpl = CTX_data_active_gpencil_layer(C); + gpl = BKE_gpencil_layer_active_get(gpd); } } @@ -1378,26 +1479,26 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) * we are obliged to add a new frame if one * doesn't exist already */ - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); if (gpf) { /* Create new stroke */ - bGPDstroke *new_stroke = MEM_dupallocN(gps); + bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true); new_stroke->runtime.tmp_layerinfo[0] = '\0'; + new_stroke->next = new_stroke->prev = NULL; - new_stroke->points = MEM_dupallocN(gps->points); - if (gps->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(gps->dvert); - BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); - } - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - new_stroke->triangles = NULL; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); + if (on_back) { + BLI_addhead(&gpf->strokes, new_stroke); + } + else { + BLI_addtail(&gpf->strokes, new_stroke); + } /* Remap material */ Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); - new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma); + new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma); CLAMP_MIN(new_stroke->mat_nr, 0); } } @@ -1415,6 +1516,8 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) void GPENCIL_OT_paste(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem copy_type[] = { {GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""}, {GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""}, @@ -1435,6 +1538,10 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", ""); + + prop = RNA_def_boolean( + ot->srna, "paste_back", 0, "Paste on Back", "Add pasted strokes behind all strokes"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ******************* Move To Layer ****************************** */ @@ -1477,7 +1584,6 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ if ((gpl == target_layer) || (gpf == NULL)) { @@ -1485,8 +1591,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) } /* make copies of selected strokes, and deselect these once we're done */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -1514,7 +1619,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Paste them all in one go */ if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, GP_GETFRAME_ADD_NEW); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); BLI_movelisttolist(&gpf->strokes, &strokes); BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); @@ -1554,32 +1659,13 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) /* ********************* Add Blank Frame *************************** */ -/* Basically the same as the drawing op */ -static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) -{ - if (ED_operator_regionactive(C)) { - /* check if current context can support GPencil data */ - if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { - return 1; - } - else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); - } - } - else { - CTX_wm_operator_poll_msg_set(C, "Active region not set"); - } - - return 0; -} - static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); int cfra = CFRA; - bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd); const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); @@ -1599,7 +1685,7 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) } /* 1) Check for an existing frame on the current frame */ - bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra); + bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, cfra); if (gpf) { /* Shunt all frames after (and including) the existing one later by 1-frame */ for (; gpf; gpf = gpf->next) { @@ -1608,7 +1694,7 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) } /* 2) Now add a new frame, with nothing in it */ - gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_ADD_NEW); + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_ADD_NEW); } CTX_DATA_END; @@ -1650,7 +1736,7 @@ void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) static bool gp_actframe_delete_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only if there's an active layer with an active frame */ return (gpl && gpl->actframe); @@ -1659,7 +1745,7 @@ static bool gp_actframe_delete_poll(bContext *C) static bool gp_annotation_actframe_delete_poll(bContext *C) { bGPdata *gpd = ED_annotation_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only if there's an active layer with an active frame */ return (gpl && gpl->actframe); @@ -1673,11 +1759,11 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) : ED_annotation_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -1690,7 +1776,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) } /* delete it... */ - BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_frame_delete(gpl, gpf); /* notifiers */ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -1747,14 +1833,14 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); if (gpf == NULL) { continue; } /* delete it... */ - BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_frame_delete(gpl, gpf); /* we successfully modified something */ success = true; @@ -1821,15 +1907,13 @@ static int gp_delete_selected_strokes(bContext *C) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; } /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -2060,9 +2144,8 @@ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) gps->dvert = new_dvert; gps->totpoints = tot; - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); /* deselect the stroke, since none of its selected points will still be selected */ gps->flag &= ~GP_STROKE_SELECT; @@ -2104,7 +2187,7 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst const int totpoints = gps_first->totpoints + gps_last->totpoints; /* create new stroke */ - bGPDstroke *join_stroke = MEM_dupallocN(gps_first); + bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false); join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); join_stroke->totpoints = totpoints; @@ -2132,6 +2215,7 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst pt_final->strength = pt->strength; pt_final->time = delta; pt_final->flag = pt->flag; + copy_v4_v4(pt_final->vert_color, pt->vert_color); /* retiming with fixed time interval (we cannot determine real time) */ delta += 0.01f; @@ -2170,6 +2254,8 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst /* add new stroke at head */ BLI_addhead(&gpf->strokes, join_stroke); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(join_stroke); /* remove first stroke */ BLI_remlink(&gpf->strokes, gps_first); @@ -2246,18 +2332,14 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, /* Create each new stroke... */ for (idx = 0; idx < num_islands; idx++) { tGPDeleteIsland *island = &islands[idx]; - new_stroke = MEM_dupallocN(gps); + new_stroke = BKE_gpencil_stroke_duplicate(gps, false); /* if cyclic and first stroke, save to join later */ if ((is_cyclic) && (gps_first == NULL)) { gps_first = new_stroke; } - /* initialize triangle memory - to be calculated on next redraw */ - new_stroke->triangles = NULL; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; new_stroke->flag &= ~GP_STROKE_CYCLIC; - new_stroke->tot_triangles = 0; /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ new_stroke->totpoints = island->end_idx - island->start_idx + 1; @@ -2320,6 +2402,9 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, BKE_gpencil_free_stroke(new_stroke); } else { + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); + if (next_stroke) { BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke); } @@ -2355,15 +2440,13 @@ static int gp_delete_selected_points(bContext *C) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; } /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -2379,7 +2462,7 @@ static int gp_delete_selected_points(bContext *C) gps->flag &= ~GP_STROKE_SELECT; /* delete unwanted points by splitting stroke into several smaller ones */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0); changed = true; } @@ -2532,16 +2615,16 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) Object *obact = CTX_data_active_object(C); const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; /* calculate difference matrix object */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; int i; @@ -2568,7 +2651,7 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) /* return data */ copy_v3_v3(&pt->x, fpt); - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + gp_apply_parent_point(depsgraph, obact, gpl, pt); } } } @@ -2609,16 +2692,16 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = scene->cursor.location; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; int i; @@ -2652,7 +2735,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { copy_v3_v3(&pt->x, cursor_global); - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + gp_apply_parent_point(depsgraph, obact, gpl, pt); } } } @@ -2707,16 +2790,16 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) INIT_MINMAX(min, max); /* calculate midpoints from selected points */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *pt; int i; @@ -2783,7 +2866,7 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot) static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* sanity checks */ if (ELEM(NULL, gpd, gpl, gpl->frames.first)) { @@ -2791,8 +2874,8 @@ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) } /* loop all strokes */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* Apply thickness */ if ((gps->thickness == 0) && (gpl->line_change == 0)) { gps->thickness = gpl->thickness; @@ -2866,8 +2949,8 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) continue; } /* skip hidden or locked colors */ - if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || - (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) || + (gp_style->flag & GP_MATERIAL_LOCKED)) { continue; } @@ -2891,7 +2974,7 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) /* Create new geometry. */ if ((gps->flag & GP_STROKE_CYCLIC) && (geometry)) { - BKE_gpencil_close_stroke(gps); + BKE_gpencil_stroke_close(gps); } } @@ -2980,8 +3063,8 @@ static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op) continue; } /* skip hidden or locked colors */ - if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || - (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) || + (gp_style->flag & GP_MATERIAL_LOCKED)) { continue; } @@ -3061,6 +3144,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps) pt.pressure = point->pressure; pt.strength = point->strength; pt.time = point->time; + copy_v4_v4(pt.vert_color, point->vert_color); /* replace first point with last point */ point2 = &gps->points[end]; @@ -3071,6 +3155,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps) point->pressure = point2->pressure; point->strength = point2->strength; point->time = point2->time; + copy_v4_v4(point->vert_color, point2->vert_color); /* replace last point with first saved before */ point = &gps->points[end]; @@ -3081,6 +3166,7 @@ static void gpencil_flip_stroke(bGPDstroke *gps) point->pressure = pt.pressure; point->strength = pt.strength; point->time = pt.time; + copy_v4_v4(point->vert_color, pt.vert_color); end--; } @@ -3111,6 +3197,7 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, newpoint->pressure = pressure; newpoint->strength = strength; newpoint->time = point->time + deltatime; + copy_v4_v4(newpoint->vert_color, point->vert_color); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[idx]; @@ -3184,8 +3271,7 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, static int gp_stroke_join_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); - bGPDstroke *gps, *gpsn; + bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd); Object *ob = CTX_data_active_object(C); bGPDframe *gpf_a = NULL; @@ -3215,8 +3301,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) continue; } - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { if (gps->flag & GP_STROKE_SELECT) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -3241,15 +3326,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) /* create a new stroke if was not created before (only created if something to join) */ if (new_stroke == NULL) { - new_stroke = MEM_dupallocN(stroke_a); - new_stroke->points = MEM_dupallocN(stroke_a->points); - if (stroke_a->dvert != NULL) { - new_stroke->dvert = MEM_dupallocN(stroke_a->dvert); - BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke); - } - new_stroke->triangles = NULL; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + new_stroke = BKE_gpencil_stroke_duplicate(stroke_a, true); /* if new, set current color */ if (type == GP_STROKE_JOINCOPY) { @@ -3263,6 +3340,9 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) /* if join only, delete old strokes */ if (type == GP_STROKE_JOIN) { if (stroke_a) { + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); + BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); BLI_remlink(&gpf->strokes, stroke_a); BKE_gpencil_free_stroke(stroke_a); @@ -3287,6 +3367,8 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) if (activegpl->actframe == NULL) { activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); BLI_addtail(&activegpl->actframe->strokes, new_stroke); } @@ -3347,7 +3429,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (gps->flag & GP_STROKE_SELECT) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -3460,7 +3542,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) GP_REPROJECT_TOP, GP_REPROJECT_CURSOR)) { if (mode != GP_REPROJECT_CURSOR) { - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin); + ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, origin); } else { copy_v3_v3(origin, scene->cursor.location); @@ -3495,7 +3577,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) copy_v3_v3(&pt->x, &pt2.x); /* apply parent again */ - gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); + gp_apply_parent_point(depsgraph, ob, gpl, pt); } /* Project screen-space back to 3D space (from current perspective) * so that all points have been treated the same way. */ @@ -3602,6 +3684,43 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", ""); } +static int gp_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return OPERATOR_CANCELLED; + } + + bGPdata *gpd = (bGPdata *)ob->data; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + BKE_gpencil_stroke_geometry_update(gps); + } + } + } + /* update changed data */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_recalc_geometry(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Recalculate internal geometry"; + ot->idname = "GPENCIL_OT_recalc_geometry"; + ot->description = "Update all internal geometry data"; + + /* callbacks */ + ot->exec = gp_recalc_geometry_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ******************* Stroke subdivide ************************** */ /* helper to smooth */ static void gp_smooth_stroke(bContext *C, wmOperator *op) @@ -3629,19 +3748,19 @@ static void gp_smooth_stroke(bContext *C, wmOperator *op) /* perform smoothing */ if (smooth_position) { - BKE_gpencil_smooth_stroke(gps, i, factor); + BKE_gpencil_stroke_smooth(gps, i, factor); } if (smooth_strength) { - BKE_gpencil_smooth_stroke_strength(gps, i, factor); + BKE_gpencil_stroke_smooth_strength(gps, i, factor); } if (smooth_thickness) { /* thickness need to repeat process several times */ for (int r2 = 0; r2 < r * 20; r2++) { - BKE_gpencil_smooth_stroke_thickness(gps, i, factor); + BKE_gpencil_stroke_smooth_thickness(gps, i, factor); } } if (smooth_uv) { - BKE_gpencil_smooth_stroke_uv(gps, i, factor); + BKE_gpencil_stroke_smooth_uv(gps, i, factor); } } } @@ -3710,7 +3829,6 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) if (gps->dvert != NULL) { gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; /* loop and interpolate */ i2 = 0; @@ -3724,6 +3842,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) pt_final->strength = pt->strength; pt_final->time = pt->time; pt_final->flag = pt->flag; + copy_v4_v4(pt_final->vert_color, pt->vert_color); if (gps->dvert != NULL) { dvert = &temp_dverts[i]; @@ -3747,6 +3866,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); pt_final->strength = interpf(pt->strength, next->strength, 0.5f); CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->flag |= GP_SPOINT_SELECT; @@ -3780,9 +3900,8 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(temp_dverts); } - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } } GP_EDITABLE_STROKES_END(gpstroke_iter); @@ -3850,7 +3969,7 @@ static int gp_stroke_simplify_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { /* simplify stroke using Ramer-Douglas-Peucker algorithm */ - BKE_gpencil_simplify_stroke(gps, factor); + BKE_gpencil_stroke_simplify_adaptive(gps, factor); } } GP_EDITABLE_STROKES_END(gpstroke_iter); @@ -3899,7 +4018,7 @@ static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { for (int i = 0; i < steps; i++) { - BKE_gpencil_simplify_fixed(gps); + BKE_gpencil_stroke_simplify_fixed(gps); } } } @@ -3949,7 +4068,7 @@ static int gp_stroke_sample_exec(bContext *C, wmOperator *op) /* Go through each editable + selected stroke */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_sample_stroke(gps, length, true); + BKE_gpencil_stroke_sample(gps, length, true); } } GP_EDITABLE_STROKES_END(gpstroke_iter); @@ -4001,14 +4120,12 @@ static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op)) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; } - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -4016,7 +4133,7 @@ static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op)) } if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_trim_stroke(gps); + BKE_gpencil_stroke_trim(gps); } } /* if not multiedit, exit loop*/ @@ -4066,7 +4183,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base_old = CTX_data_active_base(C); + Base *base_prev = CTX_data_active_base(C); bGPdata *gpd_src = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); @@ -4096,7 +4213,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) /* Take into account user preferences for duplicating actions. */ short dupflag = (U.dupflag & USER_DUP_ACT); - base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag); + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag); ob_dst = base_new->object; ob_dst->mode = OB_MODE_OBJECT; /* create new grease pencil datablock */ @@ -4111,7 +4228,6 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; @@ -4119,8 +4235,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) gpf_dst = NULL; - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -4139,7 +4254,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) /* add frame if not created before */ if (gpf_dst == NULL) { - gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); + gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); } /* add duplicate materials */ @@ -4152,7 +4267,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) /* selected points mode */ if (mode == GP_SEPARATE_POINT) { /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true); /* Reassign material. */ gps_dst->mat_nr = idx; @@ -4169,7 +4284,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0); /* delete selected points from origin stroke */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0); } /* selected strokes mode */ else if (mode == GP_SEPARATE_STROKE) { @@ -4200,10 +4315,10 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) if (gpl) { /* try to set a new active layer in source datablock */ if (gpl->prev) { - BKE_gpencil_layer_setactive(gpd_src, gpl->prev); + BKE_gpencil_layer_active_set(gpd_src, gpl->prev); } else if (gpl->next) { - BKE_gpencil_layer_setactive(gpd_src, gpl->next); + BKE_gpencil_layer_active_set(gpd_src, gpl->next); } /* unlink from source datablock */ BLI_remlink(&gpd_src->layers, gpl); @@ -4212,8 +4327,8 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) BLI_addtail(&gpd_dst->layers, gpl); /* add duplicate materials */ - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; @@ -4227,8 +4342,8 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) /* Ensure destination object has one active layer. */ if (gpd_dst->layers.first != NULL) { - if (BKE_gpencil_layer_getactive(gpd_dst) == NULL) { - BKE_gpencil_layer_setactive(gpd_dst, gpd_dst->layers.first); + if (BKE_gpencil_layer_active_get(gpd_dst) == NULL) { + BKE_gpencil_layer_active_set(gpd_dst, gpd_dst->layers.first); } } @@ -4288,14 +4403,12 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - bGPDstroke *gps, *gpsn; if (gpf == NULL) { continue; } - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) { @@ -4308,7 +4421,7 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) /* split selected strokes */ if (gps->flag & GP_STROKE_SELECT) { /* make copy of source stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true); /* link to same frame */ BLI_addtail(&gpf->strokes, gps_dst); @@ -4322,11 +4435,11 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0); /* delete selected points from origin stroke */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0); + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0); } } /* select again tagged points */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { bGPDspoint *ptn = gps->points; for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) { if (ptn->flag & GP_SPOINT_TAG) { @@ -4566,8 +4679,7 @@ static int gpencil_cutter_lasso_select(bContext *C, GP_EDITABLE_STROKES_END(gpstroke_iter); /* dissolve selected points */ - bGPDstroke *gpsn; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_LOCKED) { continue; } @@ -4576,8 +4688,7 @@ static int gpencil_cutter_lasso_select(bContext *C, if (gpf == NULL) { continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { if (gps->flag & GP_STROKE_SELECT) { gpencil_cutter_dissolve(gpl, gps); } @@ -4662,11 +4773,11 @@ bool ED_object_gpencil_exit(struct Main *bmain, Object *ob) bGPdata *gpd = (bGPdata *)ob->data; gpd->flag &= ~(GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | - GP_DATA_STROKE_WEIGHTMODE); + GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE); ob->restore_mode = ob->mode; ob->mode &= ~(OB_MODE_PAINT_GPENCIL | OB_MODE_EDIT_GPENCIL | OB_MODE_SCULPT_GPENCIL | - OB_MODE_WEIGHT_GPENCIL); + OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL); /* Inform all CoW versions that we changed the mode. */ DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_COPY_ON_WRITE); @@ -4687,7 +4798,7 @@ static bool gp_merge_by_distance_poll(bContext *C) return false; } - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL)); } @@ -4707,7 +4818,7 @@ static int gp_merge_by_distance_exec(bContext *C, wmOperator *op) /* Go through each editable selected stroke */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_merge_distance_stroke(gpf_, gps, threshold, unselected); + BKE_gpencil_stroke_merge_distance(gpf_, gps, threshold, unselected); } } GP_EDITABLE_STROKES_END(gpstroke_iter); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index d76ab85ad31..c928c1e933a 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -134,6 +134,9 @@ typedef struct tGPDfill { /* scaling factor */ short fill_factor; + /* Frame to use. */ + int active_cfra; + /** number of elements currently in cache */ short sbuffer_used; /** temporary points */ @@ -228,7 +231,6 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4]) Object *ob = tgpf->ob; bGPdata *gpd = tgpf->gpd; - int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); tGPDdraw tgpw; tgpw.rv3d = tgpf->rv3d; @@ -245,9 +247,9 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4]) GPU_blend(true); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* calculate parent position */ - ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat); + BKE_gpencil_parent_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat); /* do not draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) { @@ -256,25 +258,25 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4]) /* if active layer and no keyframe, create a new one */ if (gpl == tgpf->gpl) { - if ((gpl->actframe == NULL) || (gpl->actframe->framenum != cfra_eval)) { - BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) { + BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW); } } /* get frame to draw */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV); if (gpf == NULL) { continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* check if stroke can be drawn */ if ((gps->points == NULL) || (gps->totpoints < 2)) { continue; } /* check if the color is visible */ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) { + if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) { continue; } @@ -291,7 +293,7 @@ static void gp_draw_datablock(tGPDfill *tgpf, const float ink[4]) tgpw.onion = true; tgpw.custonion = true; - bool textured_stroke = (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE); + bool textured_stroke = (gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE); /* normal strokes */ if (((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) || @@ -666,6 +668,62 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf) BLI_stack_free(stack); } +/* Check if there are some pixel not filled with green. If no points, means nothing to fill. */ +static bool gpencil_check_borders(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int idx; + int pixel = 0; + float color[4]; + bool found = false; + + /* horizontal lines */ + for (idx = 0; idx < ibuf->x; idx++) { + /* bottom line */ + get_pixel(ibuf, idx, color); + if (color[1] != 1.0f) { + found = true; + break; + } + /* top line */ + pixel = idx + (ibuf->x * (ibuf->y - 1)); + get_pixel(ibuf, pixel, color); + if (color[1] != 1.0f) { + found = true; + break; + } + } + if (!found) { + /* vertical lines */ + for (idx = 0; idx < ibuf->y; idx++) { + /* left line */ + get_pixel(ibuf, ibuf->x * idx, color); + if (color[1] != 1.0f) { + found = true; + break; + } + /* right line */ + pixel = ibuf->x * idx + (ibuf->x - 1); + get_pixel(ibuf, pixel, color); + if (color[1] != 1.0f) { + found = true; + break; + } + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; + + return found; +} + /* clean external border of image to avoid infinite loops */ static void gpencil_clean_borders(tGPDfill *tgpf) { @@ -1006,8 +1064,6 @@ static void gpencil_points_from_stack(tGPDfill *tgpf) /* create a grease pencil stroke using points in buffer */ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) { - const int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); - ToolSettings *ts = tgpf->scene->toolsettings; const char *align_flag = &ts->gpencil_v3d_align; const bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); @@ -1026,16 +1082,23 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) return; } - /* get frame or create a new one */ - tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + /* Get frame or create a new one. */ + tgpf->gpf = BKE_gpencil_layer_frame_get(tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW); + + /* Set frame as selected. */ + tgpf->gpf->flag |= GP_FRAME_SELECT; /* create new stroke */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->fill_opacity_fac = 1.0f; + gps->hardeness = brush->gpencil_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); gps->inittime = 0.0f; + /* Apply the vertex color to fill. */ + ED_gpencil_fill_vertex_color_set(ts, brush, gps); + /* the polygon must be closed, so enabled cyclic */ gps->flag |= GP_STROKE_CYCLIC; gps->flag |= GP_STROKE_3DSPACE; @@ -1054,11 +1117,6 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) gps->totpoints = tgpf->sbuffer_used; gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_used, "gp_stroke_points"); - /* initialize triangle memory to dummy data */ - gps->tot_triangles = 0; - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - /* add stroke to frame */ if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)) { BLI_addhead(&tgpf->gpf->strokes, gps); @@ -1093,6 +1151,9 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) pt->strength = 1.0f; pt->time = 0.0f; + /* Apply the vertex color to point. */ + ED_gpencil_point_vertex_color_set(ts, brush, pt); + if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr); if (dw) { @@ -1115,7 +1176,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) float smoothfac = 1.0f; for (int r = 0; r < 1; r++) { for (int i = 0; i < gps->totpoints; i++) { - BKE_gpencil_smooth_stroke(gps, i, smoothfac - reduce); + BKE_gpencil_stroke_smooth(gps, i, smoothfac - reduce); } reduce += 0.25f; // reduce the factor } @@ -1124,7 +1185,8 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) if ((tgpf->lock_axis > GP_LOCKAXIS_VIEW) && ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) { float origin[3]; - ED_gp_get_drawing_reference(tgpf->scene, tgpf->ob, tgpf->gpl, ts->gpencil_v3d_align, origin); + ED_gpencil_drawing_reference_get( + tgpf->scene, tgpf->ob, tgpf->gpl, ts->gpencil_v3d_align, origin); ED_gp_project_stroke_to_plane( tgpf->scene, tgpf->ob, tgpf->rv3d, gps, origin, tgpf->lock_axis - 1); } @@ -1132,7 +1194,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) /* if parented change position relative to parent object */ for (int a = 0; a < tgpf->sbuffer_used; a++) { pt = &gps->points[a]; - gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt); + gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpl, pt); } /* if camera view, reproject flat to view to avoid perspective effect */ @@ -1142,14 +1204,17 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) /* simplify stroke */ for (int b = 0; b < tgpf->fill_simplylvl; b++) { - BKE_gpencil_simplify_fixed(gps); + BKE_gpencil_stroke_simplify_fixed(gps); } + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* ----------------------- */ /* Drawing */ /* Helper: Draw status message while the user is running the operator */ -static void gpencil_fill_status_indicators(bContext *C, tGPDfill *UNUSED(tgpf)) +static void gpencil_fill_status_indicators(bContext *C) { const char *status_str = TIP_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back"); ED_workspace_status_text(C, status_str); @@ -1228,10 +1293,11 @@ static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op)) /* set GP datablock */ tgpf->gpd = gpd; - tgpf->gpl = BKE_gpencil_layer_getactive(gpd); + tgpf->gpl = BKE_gpencil_layer_active_get(gpd); if (tgpf->gpl == NULL) { tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); } + tgpf->lock_axis = ts->gp_sculpt.lock_axis; tgpf->oldkey = -1; @@ -1339,7 +1405,7 @@ static int gpencil_fill_init(bContext *C, wmOperator *op) tGPDfill *tgpf; /* cannot paint in locked layer */ bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if ((gpl) && (gpl->flag & GP_LAYER_LOCKED)) { return 0; } @@ -1401,7 +1467,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_PAINT_BRUSH); - gpencil_fill_status_indicators(C, tgpf); + gpencil_fill_status_indicators(C); DEG_id_tag_update(&tgpf->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); @@ -1416,6 +1482,7 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPDfill *tgpf = op->customdata; + Scene *scene = tgpf->scene; int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ @@ -1426,7 +1493,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) break; case LEFTMOUSE: tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); - /* first time the event is not enabled to show help lines */ + /* first time the event is not enabled to show help lines. */ if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) { ARegion *region = BKE_area_find_region_xy( CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); @@ -1437,42 +1504,55 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) in_bounds = BLI_rcti_isect_pt(®ion->winrct, event->x, event->y); if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) { - /* TODO GPXX: Verify the mouse click is right for any window size */ tgpf->center[0] = event->mval[0]; tgpf->center[1] = event->mval[1]; + /* Set active frame as current for filling. */ + tgpf->active_cfra = CFRA; + /* render screen to temp image */ if (gp_render_offscreen(tgpf)) { /* apply boundary fill */ gpencil_boundaryfill_area(tgpf); - /* clean borders to avoid infinite loops */ - gpencil_clean_borders(tgpf); + /* Check if detected some border to fill. */ + if (gpencil_check_borders(tgpf)) { - /* analyze outline */ - gpencil_get_outline_points(tgpf); + /* clean borders to avoid infinite loops */ + gpencil_clean_borders(tgpf); - /* create array of points from stack */ - gpencil_points_from_stack(tgpf); + /* analyze outline */ + gpencil_get_outline_points(tgpf); - /* create z-depth array for reproject */ - gpencil_get_depth_array(tgpf); + /* create array of points from stack */ + gpencil_points_from_stack(tgpf); - /* create stroke and reproject */ - gpencil_stroke_from_buffer(tgpf); - } + /* create z-depth array for reproject */ + gpencil_get_depth_array(tgpf); - /* restore size */ - tgpf->region->winx = (short)tgpf->bwinx; - tgpf->region->winy = (short)tgpf->bwiny; - tgpf->region->winrct = tgpf->brect; + /* create stroke and reproject */ + gpencil_stroke_from_buffer(tgpf); + } + else { + BKE_report(op->reports, RPT_ERROR, "Fill canceled. No edges detected"); + } + } /* free temp stack data */ if (tgpf->stack) { BLI_stack_free(tgpf->stack); } + /* Free memory. */ + MEM_SAFE_FREE(tgpf->sbuffer); + MEM_SAFE_FREE(tgpf->depth_arr); + + /* restore size */ + tgpf->region->winx = (short)tgpf->bwinx; + tgpf->region->winy = (short)tgpf->bwiny; + tgpf->region->winrct = tgpf->brect; + /* push undo data */ gpencil_undo_push(tgpf->gpd); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 077b5b88118..2d36e426835 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -111,6 +111,8 @@ typedef struct tGPDinterpolate_layer { } tGPDinterpolate_layer; typedef struct tGPDinterpolate { + /** Current depsgraph from context */ + struct Depsgraph *depsgraph; /** current scene from context */ struct Scene *scene; /** area where painting originated */ @@ -138,10 +140,6 @@ typedef struct tGPDinterpolate { int flag; NumInput num; /* numeric input */ - /** handle for drawing strokes while operator is running 3d stuff */ - void *draw_handle_3d; - /** handle for drawing strokes while operator is running screen stuff */ - void *draw_handle_screen; } tGPDinterpolate; /* Temporary primitive operation data */ @@ -155,6 +153,8 @@ typedef struct tGPDprimitive { struct Scene *scene; /** current active gp object */ struct Object *ob; + /** current evaluated gp object */ + struct Object *ob_eval; /** area where painting originated */ struct ScrArea *sa; /** region where painting originated */ @@ -166,7 +166,7 @@ typedef struct tGPDprimitive { /** current GP datablock */ struct bGPdata *gpd; /** current material */ - struct Material *mat; + struct Material *material; /** current brush */ struct Brush *brush; @@ -233,10 +233,6 @@ typedef struct tGPDprimitive { } tGPDprimitive; /* Modal Operator Drawing Callbacks ------------------------ */ - -void ED_gp_draw_interpolation(const struct bContext *C, - struct tGPDinterpolate *tgpi, - const int type); void ED_gp_draw_fill(struct tGPDdraw *tgpw); /* ***************************************************** */ @@ -284,7 +280,6 @@ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], */ void gp_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, - bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps); /** @@ -292,7 +287,6 @@ void gp_apply_parent(struct Depsgraph *depsgraph, */ void gp_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, - bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt); @@ -342,7 +336,6 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, int gp_delete_selected_point_wrap(bContext *C); void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide); -void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, struct RNG *rng); /* Layers Enums -------------------------------------- */ @@ -367,6 +360,14 @@ void GPENCIL_OT_annotate(struct wmOperatorType *ot); void GPENCIL_OT_draw(struct wmOperatorType *ot); void GPENCIL_OT_fill(struct wmOperatorType *ot); +/* Vertex Paint. */ +void GPENCIL_OT_vertex_paint(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_color_brightness_contrast(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_color_hsv(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_color_invert(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_color_levels(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_color_set(struct wmOperatorType *ot); + /* Guides ----------------------- */ void GPENCIL_OT_guide_rotate(struct wmOperatorType *ot); @@ -390,6 +391,7 @@ void GPENCIL_OT_selectmode_toggle(struct wmOperatorType *ot); void GPENCIL_OT_paintmode_toggle(struct wmOperatorType *ot); void GPENCIL_OT_sculptmode_toggle(struct wmOperatorType *ot); void GPENCIL_OT_weightmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_vertexmode_toggle(struct wmOperatorType *ot); void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); @@ -405,6 +407,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot); void GPENCIL_OT_select_first(struct wmOperatorType *ot); void GPENCIL_OT_select_last(struct wmOperatorType *ot); void GPENCIL_OT_select_alternate(struct wmOperatorType *ot); +void GPENCIL_OT_select_color(struct wmOperatorType *ot); void GPENCIL_OT_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_delete(struct wmOperatorType *ot); @@ -415,16 +418,19 @@ void GPENCIL_OT_extrude(struct wmOperatorType *ot); void GPENCIL_OT_move_to_layer(struct wmOperatorType *ot); void GPENCIL_OT_layer_change(struct wmOperatorType *ot); +void GPENCIL_OT_layer_active(struct wmOperatorType *ot); void GPENCIL_OT_snap_to_grid(struct wmOperatorType *ot); void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot); void GPENCIL_OT_reproject(struct wmOperatorType *ot); +void GPENCIL_OT_recalc_geometry(struct wmOperatorType *ot); /* stroke sculpting -- */ void GPENCIL_OT_sculpt_paint(struct wmOperatorType *ot); +void GPENCIL_OT_weight_paint(struct wmOperatorType *ot); /* buttons editing --- */ @@ -440,6 +446,9 @@ void GPENCIL_OT_layer_annotation_move(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot); +void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot); +void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot); + void GPENCIL_OT_hide(struct wmOperatorType *ot); void GPENCIL_OT_reveal(struct wmOperatorType *ot); @@ -459,6 +468,7 @@ void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_frame_clean_loose(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); +void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot); enum { GP_STROKE_JOIN = -1, @@ -498,8 +508,16 @@ void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot); void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot); void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot); void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot); -void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); +void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot); +void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot); + +void GPENCIL_OT_transform_fill(struct wmOperatorType *ot); +void GPENCIL_OT_reset_transform_fill(struct wmOperatorType *ot); + +void GPENCIL_OT_brush_reset(struct wmOperatorType *ot); +void GPENCIL_OT_brush_reset_all(struct wmOperatorType *ot); /* undo stack ---------- */ @@ -617,7 +635,7 @@ struct GP_EditableStrokes_Iter { bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ + BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ /* loop over strokes */ \ bGPDstroke *gpsn_; \ @@ -643,10 +661,6 @@ struct GP_EditableStrokes_Iter { } \ (void)0 -#define GPENCIL_ANY_SCULPT_MASK(flag) \ - ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \ - GP_SCULPT_MASK_SELECTMODE_SEGMENT))) - /** * Iterate over all editable strokes using evaluated data in the current context, * stopping on each usable layer + stroke pair (i.e. gpl and gps) @@ -662,26 +676,21 @@ struct GP_EditableStrokes_Iter { struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \ Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \ Object *obact_ = CTX_data_active_object(C); \ - Object *obeval_ = DEG_get_evaluated_object(depsgraph_, obact_); \ - bGPdata *gpd_ = CTX_data_gpencil_data(C); \ + Object *ob_eval_ = (Object *)DEG_get_evaluated_id(depsgraph_, &obact_->id); \ + bGPdata *gpd_ = (bGPdata *)ob_eval_->data; \ const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ - int idx_eval = 0; \ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { \ - if (gpencil_layer_is_editable(gpl)) { \ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_->layers) { \ + if (BKE_gpencil_layer_is_editable(gpl)) { \ bGPDframe *init_gpf_ = gpl->actframe; \ if (is_multiedit_) { \ init_gpf_ = gpl->frames.first; \ } \ for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \ if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \ - ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, gpstroke_iter.diff_mat); \ + BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \ invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \ - /* get evaluated frame with modifiers applied */ \ - bGPDframe *gpf_eval_ = (!is_multiedit_) ? \ - &obeval_->runtime.gpencil_evaluated_frames[idx_eval] : \ - gpf_; \ /* loop over strokes */ \ - for (bGPDstroke *gps = gpf_eval_->strokes.first; gps; gps = gps->next) { \ + for (bGPDstroke *gps = gpf_->strokes.first; gps; gps = gps->next) { \ /* skip strokes that are invalid for current view */ \ if (ED_gpencil_stroke_can_use(C, gps) == false) \ continue; \ @@ -698,7 +707,6 @@ struct GP_EditableStrokes_Iter { } \ } \ } \ - idx_eval++; \ } \ } \ (void)0 diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 940fb85f91a..bd00a492035 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -115,31 +115,38 @@ static void gp_interpolate_update_points(const bGPDstroke *gps_from, pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - - /* GPXX interpolate dverts */ -#if 0 - MDeformVert *dvert = &new_stroke->dvert[i]; - dvert->totweight = 0; - dvert->dw = NULL; -#endif } } /* ****************** Interpolate Interactive *********************** */ +/* Helper: free all temp strokes for display. */ +static void gp_interpolate_free_temp_strokes(bGPDframe *gpf) +{ + if (gpf == NULL) { + return; + } + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_TAG) { + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); + } + } +} /* Helper: Update all strokes interpolated */ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) { bGPdata *gpd = tgpi->gpd; - tGPDinterpolate_layer *tgpil; const float shift = tgpi->shift; - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - bGPDstroke *new_stroke; + LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) { const float factor = tgpil->factor + shift; - for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; - new_stroke = new_stroke->next) { + bGPDframe *gpf = tgpil->gpl->actframe; + /* Free temp strokes. */ + gp_interpolate_free_temp_strokes(gpf); + + LISTBASE_FOREACH (bGPDstroke *, new_stroke, &tgpil->interFrame->strokes) { bGPDstroke *gps_from, *gps_to; int stroke_idx; @@ -156,6 +163,13 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) /* update points position */ if ((gps_from) && (gps_to)) { gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + + /* Add temp strokes. */ + if (gpf) { + bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true); + gps_eval->flag |= GP_STROKE_TAG; + BLI_addtail(&gpf->strokes, gps_eval); + } } } } @@ -172,19 +186,18 @@ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; /* get layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* all layers or only active */ if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { continue; } /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } /* read strokes */ - for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; - gps_from = gps_from->next) { + LISTBASE_FOREACH (bGPDstroke *, gps_from, &gpl->actframe->strokes) { bGPDstroke *gps_to; int fFrame; @@ -232,7 +245,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) tgpi->high_limit = 2.0f - tgpi->init_factor; /* set layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { tGPDinterpolate_layer *tgpil; /* all layers or only active */ @@ -240,7 +253,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) continue; } /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } @@ -262,8 +275,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); /* create new strokes data with interpolated points reading original stroke */ - for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; - gps_from = gps_from->next) { + LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) { bGPDstroke *gps_to; int fFrame; @@ -293,7 +305,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true); if (valid) { /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ @@ -305,8 +317,6 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) sizeof(*new_stroke->dvert) * gps_to->totpoints); } new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; } /* update points position */ gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); @@ -318,37 +328,16 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) if (new_stroke->dvert != NULL) { new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert)); } - new_stroke->tot_triangles = 0; - new_stroke->triangles = MEM_recallocN(new_stroke->triangles, - sizeof(*new_stroke->triangles)); - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); /* add to strokes */ BLI_addtail(&tgpil->interFrame->strokes, new_stroke); } } } -/* ----------------------- */ -/* Drawing Callbacks */ - -/* Drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *C, - ARegion *UNUSED(region), - void *arg) -{ - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); -} - -/* Drawing callback for modal operator in 3d mode */ -static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(region), void *arg) -{ - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); -} - /* ----------------------- */ /* Helper: calculate shift based on position of mouse (we only use x-axis for now. @@ -415,25 +404,23 @@ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpol static void gpencil_interpolate_exit(bContext *C, wmOperator *op) { tGPDinterpolate *tgpi = op->customdata; - tGPDinterpolate_layer *tgpil; bGPdata *gpd = tgpi->gpd; /* don't assume that operator data exists at all */ if (tgpi) { - /* remove drawing handler */ - if (tgpi->draw_handle_screen) { - ED_region_draw_cb_exit(tgpi->region->type, tgpi->draw_handle_screen); - } - if (tgpi->draw_handle_3d) { - ED_region_draw_cb_exit(tgpi->region->type, tgpi->draw_handle_3d); - } - /* clear status message area */ ED_area_status_text(tgpi->sa, NULL); ED_workspace_status_text(C, NULL); + /* Clear any temp stroke. */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + gp_interpolate_free_temp_strokes(gpf); + } + } + /* finally, free memory used by temp data */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) { BKE_gpencil_free_strokes(tgpil->interFrame); MEM_freeN(tgpil->interFrame); } @@ -455,6 +442,7 @@ static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinte bGPdata *gpd = CTX_data_gpencil_data(C); /* set current scene and window */ + tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); tgpi->scene = CTX_data_scene(C); tgpi->sa = CTX_wm_area(C); tgpi->region = CTX_wm_region(C); @@ -550,15 +538,6 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent tgpi = op->customdata; } - /* Enable custom drawing handlers - * It needs 2 handlers because strokes can in 3d space and screen space - * and each handler use different coord system - */ - tgpi->draw_handle_screen = ED_region_draw_cb_activate( - tgpi->region->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); - tgpi->draw_handle_3d = ED_region_draw_cb_activate( - tgpi->region->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); - /* set cursor to indicate modal */ WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); @@ -579,8 +558,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent tGPDinterpolate *tgpi = op->customdata; wmWindow *win = CTX_wm_window(C); bGPDframe *gpf_dst; - bGPDstroke *gps_src, *gps_dst; - tGPDinterpolate_layer *tgpil; + bGPDstroke *gps_dst; const bool has_numinput = hasNumInput(&tgpi->num); switch (event->type) { @@ -593,26 +571,22 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent WM_cursor_modal_restore(win); /* insert keyframes as required... */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) { + gpf_dst = BKE_gpencil_layer_frame_get(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; /* copy strokes */ BLI_listbase_clear(&gpf_dst->strokes); - for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { + LISTBASE_FOREACH (bGPDstroke *, gps_src, &tgpil->interFrame->strokes) { if (gps_src->totpoints == 0) { continue; } /* 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); - if (gps_src->dvert != NULL) { - gps_dst->dvert = MEM_dupallocN(gps_src->dvert); - BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); - } - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; + gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps_dst); + BLI_addtail(&gpf_dst->strokes, gps_dst); } } @@ -973,7 +947,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* loop all layer to check if need interpolation */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { bGPDframe *prevFrame, *nextFrame; bGPDstroke *gps_from, *gps_to; int cframe, fFrame; @@ -983,7 +957,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) continue; } /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { continue; } @@ -1017,7 +991,6 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) /* create new strokes data with interpolated points reading original stroke */ for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *new_stroke = NULL; /* only selected */ if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && @@ -1042,12 +1015,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) /* create a new frame if needed */ if (interFrame == NULL) { - interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); + interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW); interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; } /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true); /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { @@ -1064,14 +1037,16 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); } + new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; } /* update points position */ gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(new_stroke); + /* add to strokes */ BLI_addtail(&interFrame->strokes, new_stroke); } diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c index 91339709162..9a7ad8d7220 100644 --- a/source/blender/editors/gpencil/gpencil_merge.c +++ b/source/blender/editors/gpencil/gpencil_merge.c @@ -37,6 +37,7 @@ #include "BKE_gpencil.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_report.h" #include "WM_api.h" #include "WM_types.h" @@ -60,6 +61,7 @@ typedef struct tGPencilPointCache { float x, y, z; float pressure; float strength; + float vert_color[4]; } tGPencilPointCache; /* helper function to sort points */ @@ -93,6 +95,7 @@ static void gpencil_insert_points_to_stroke(bGPDstroke *gps, pt_dst->uv_fac = 1.0f; pt_dst->uv_rot = 0; pt_dst->flag |= GP_SPOINT_SELECT; + copy_v4_v4(pt_dst->vert_color, point_elem->vert_color); } } @@ -113,7 +116,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(bmain, ts); + BKE_brush_gpencil_paint_presets(bmain, ts); } Brush *brush = paint->brush; @@ -125,25 +128,21 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo else { add_frame_mode = GP_GETFRAME_ADD_NEW; } - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode); /* stroke */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); gps->totpoints = totpoints; gps->inittime = 0.0f; gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->hardeness = brush->gpencil_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); gps->flag |= GP_STROKE_SELECT; gps->flag |= GP_STROKE_3DSPACE; gps->mat_nr = ob->actcol - 1; /* allocate memory for points */ gps->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, "gp_stroke_points"); - /* initialize triangle memory to dummy data */ - gps->tot_triangles = 0; - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; if (cyclic) { gps->flag |= GP_STROKE_CYCLIC; @@ -181,17 +180,14 @@ static void gpencil_get_elements_len(bContext *C, int *totstrokes, int *totpoint static void gpencil_dissolve_points(bContext *C) { - bGPDstroke *gps, *gpsn; - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { bGPDframe *gpf = gpl->actframe; if (gpf == NULL) { continue; } - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false, 0); + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0); } } CTX_DATA_END; @@ -224,7 +220,7 @@ static void gpencil_calc_points_factor(bContext *C, if (gpf == NULL) { continue; } - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (gps->flag & GP_STROKE_SELECT) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (clear_stroke) { @@ -239,6 +235,7 @@ static void gpencil_calc_points_factor(bContext *C, copy_v3_v3(&pt2->x, &pt->x); pt2->pressure = pt->pressure; pt2->strength = pt->strength; + copy_v4_v4(pt2->vert_color, pt->vert_color); pt->flag &= ~GP_SPOINT_SELECT; if (clear_point) { pt->flag |= GP_SPOINT_TAG; @@ -288,6 +285,7 @@ static void gpencil_calc_points_factor(bContext *C, copy_v3_v3(&sort_pt->x, &pt2->x); sort_pt->pressure = pt2->pressure; sort_pt->strength = pt2->strength; + copy_v4_v4(sort_pt->vert_color, pt2->vert_color); sort_pt->gps = gps_array[i]; @@ -336,6 +334,7 @@ static int gpencil_insert_to_array(tGPencilPointCache *src_array, dst_elem->pressure = src_elem->pressure; dst_elem->strength = src_elem->strength; dst_elem->factor = src_elem->factor; + copy_v4_v4(dst_elem->vert_color, src_elem->vert_color); } return last; @@ -458,7 +457,7 @@ static bool gp_strokes_merge_poll(bContext *C) /* check hidden or locked materials */ MaterialGPencilStyle *gp_style = ma->gp_style; - if ((gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + if ((gp_style->flag & GP_MATERIAL_HIDE) || (gp_style->flag & GP_MATERIAL_LOCKED)) { return false; } @@ -569,3 +568,100 @@ void GPENCIL_OT_stroke_merge(wmOperatorType *ot) RNA_def_boolean(ot->srna, "clear_point", 0, "Dissolve Points", "Dissolve old selected points"); RNA_def_boolean(ot->srna, "clear_stroke", 0, "Delete Strokes", "Delete old selected strokes"); } + +/* Merge similar materials. */ +static bool gp_stroke_merge_material_poll(bContext *C) +{ + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + return true; +} + +static int gp_stroke_merge_material_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const float hue_threshold = RNA_float_get(op->ptr, "hue_threshold"); + const float sat_threshold = RNA_float_get(op->ptr, "sat_threshold"); + const float val_threshold = RNA_float_get(op->ptr, "val_threshold"); + + /* Review materials. */ + GHash *mat_table = BLI_ghash_int_new(__func__); + + short *totcol = BKE_object_material_len_p(ob); + if (totcol == 0) { + return OPERATOR_CANCELLED; + } + + bool changed = BKE_gpencil_merge_materials_table_get( + ob, hue_threshold, sat_threshold, val_threshold, mat_table); + + int removed = BLI_ghash_len(mat_table); + + /* Update stroke material index. */ + if (changed) { + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + if (BLI_ghash_haskey(mat_table, POINTER_FROM_INT(gps->mat_nr))) { + int *idx = BLI_ghash_lookup(mat_table, POINTER_FROM_INT(gps->mat_nr)); + gps->mat_nr = POINTER_AS_INT(idx); + } + } + } + } + CTX_DATA_END; + } + + /* Free hash memory. */ + BLI_ghash_free(mat_table, NULL, NULL); + + /* notifiers */ + if (changed) { + BKE_reportf(op->reports, RPT_INFO, "Merged %d materiales of %d", removed, *totcol); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + else { + BKE_report(op->reports, RPT_INFO, "Nothing to merge"); + } + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_merge_material(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Merge Grease Pencil Materials"; + ot->idname = "GPENCIL_OT_stroke_merge_material"; + ot->description = "Replace materials in strokes merging similar"; + + /* api callbacks */ + ot->exec = gp_stroke_merge_material_exec; + ot->poll = gp_stroke_merge_material_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_float( + ot->srna, "hue_threshold", 0.001f, 0.0f, 1.0f, "Hue Threshold", "", 0.0f, 1.0f); + prop = RNA_def_float( + ot->srna, "sat_threshold", 0.001f, 0.0f, 1.0f, "Saturation Threshold", "", 0.0f, 1.0f); + prop = RNA_def_float( + ot->srna, "val_threshold", 0.001f, 0.0f, 1.0f, "Value Threshold", "", 0.0f, 1.0f); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 7a541cd8f03..275075c38db 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -95,6 +95,60 @@ static bool gp_stroke_paintmode_poll_with_tool(bContext *C, const char gpencil_t WM_toolsystem_active_tool_is_brush(C) && (brush->gpencil_tool == gpencil_tool)); } +static bool gp_stroke_vertexmode_poll_with_tool(bContext *C, const char gpencil_vertex_tool) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (!gpd) { + return false; + } + + ToolSettings *ts = CTX_data_tool_settings(C); + if (!ts || !ts->gp_vertexpaint) { + return false; + } + + Brush *brush = BKE_paint_brush(&ts->gp_vertexpaint->paint); + return ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) && (brush && brush->gpencil_settings) && + WM_toolsystem_active_tool_is_brush(C) && + (brush->gpencil_vertex_tool == gpencil_vertex_tool)); +} + +static bool gp_stroke_sculptmode_poll_with_tool(bContext *C, const char gpencil_sculpt_tool) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (!gpd) { + return false; + } + + ToolSettings *ts = CTX_data_tool_settings(C); + if (!ts || !ts->gp_sculptpaint) { + return false; + } + + Brush *brush = BKE_paint_brush(&ts->gp_sculptpaint->paint); + return ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) && (brush && brush->gpencil_settings) && + WM_toolsystem_active_tool_is_brush(C) && + (brush->gpencil_sculpt_tool == gpencil_sculpt_tool)); +} + +static bool gp_stroke_weightmode_poll_with_tool(bContext *C, const char gpencil_weight_tool) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (!gpd) { + return false; + } + + ToolSettings *ts = CTX_data_tool_settings(C); + if (!ts || !ts->gp_weightpaint) { + return false; + } + + Brush *brush = BKE_paint_brush(&ts->gp_weightpaint->paint); + return ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) && (brush && brush->gpencil_settings) && + WM_toolsystem_active_tool_is_brush(C) && + (brush->gpencil_weight_tool == gpencil_weight_tool)); +} + /* Poll callback for stroke painting (draw brush) */ static bool gp_stroke_paintmode_draw_poll(bContext *C) { @@ -113,6 +167,12 @@ static bool gp_stroke_paintmode_fill_poll(bContext *C) return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_FILL); } +/* Poll callback for stroke painting (tint) */ +static bool gp_stroke_paintmode_tint_poll(bContext *C) +{ + return gp_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT); +} + /* Poll callback for stroke sculpting mode */ static bool gp_stroke_sculptmode_poll(bContext *C) { @@ -125,9 +185,8 @@ static bool gp_stroke_sculptmode_poll(bContext *C) return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); } else { - /* weight paint is a submode of sculpt */ if ((ob) && (ob->type == OB_GPENCIL)) { - return GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd); + return GPENCIL_SCULPT_MODE(gpd); } } @@ -141,12 +200,113 @@ static bool gp_stroke_weightmode_poll(bContext *C) Object *ob = CTX_data_active_object(C); if ((ob) && (ob->type == OB_GPENCIL)) { - return (gpd && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)); + return GPENCIL_WEIGHT_MODE(gpd); } return 0; } +/* Poll callback for stroke vertex paint mode */ +static bool gp_stroke_vertexmode_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); + + if ((ob) && (ob->type == OB_GPENCIL)) { + return (gpd && (gpd->flag & GP_DATA_STROKE_VERTEXMODE)); + } + + return 0; +} + +/* Poll callback for vertex painting (draw) */ +static bool gp_stroke_vertexmode_draw_poll(bContext *C) +{ + return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_DRAW); +} + +/* Poll callback for vertex painting (blur) */ +static bool gp_stroke_vertexmode_blur_poll(bContext *C) +{ + return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_BLUR); +} + +/* Poll callback for vertex painting (average) */ +static bool gp_stroke_vertexmode_average_poll(bContext *C) +{ + return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_AVERAGE); +} + +/* Poll callback for vertex painting (smear) */ +static bool gp_stroke_vertexmode_smear_poll(bContext *C) +{ + return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_SMEAR); +} + +/* Poll callback for vertex painting (replace) */ +static bool gp_stroke_vertexmode_replace_poll(bContext *C) +{ + return gp_stroke_vertexmode_poll_with_tool(C, GPVERTEX_TOOL_REPLACE); +} + +/* Poll callback for sculpt (Smooth) */ +static bool gp_stroke_sculptmode_smooth_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_SMOOTH); +} +/* Poll callback for sculpt (Thickness) */ +static bool gp_stroke_sculptmode_thickness_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_THICKNESS); +} + +/* Poll callback for sculpt (Strength) */ +static bool gp_stroke_sculptmode_strength_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_STRENGTH); +} + +/* Poll callback for sculpt (Grab) */ +static bool gp_stroke_sculptmode_grab_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_GRAB); +} + +/* Poll callback for sculpt (Push) */ +static bool gp_stroke_sculptmode_push_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PUSH); +} + +/* Poll callback for sculpt (Twist) */ +static bool gp_stroke_sculptmode_twist_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_TWIST); +} + +/* Poll callback for sculpt (Pinch) */ +static bool gp_stroke_sculptmode_pinch_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_PINCH); +} +/* Poll callback for sculpt (Randomize) */ +static bool gp_stroke_sculptmode_randomize_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_RANDOMIZE); +} + +/* Poll callback for sculpt (Clone) */ +static bool gp_stroke_sculptmode_clone_poll(bContext *C) +{ + return gp_stroke_sculptmode_poll_with_tool(C, GPSCULPT_TOOL_CLONE); +} + +/* Poll callback for weight paint (Draw) */ +static bool gp_stroke_weightmode_draw_poll(bContext *C) +{ + return gp_stroke_weightmode_poll_with_tool(C, GPWEIGHT_TOOL_DRAW); +} + /* Stroke Editing Keymap - Only when editmode is enabled */ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) { @@ -177,6 +337,13 @@ static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf) keymap->poll = gp_stroke_paintmode_fill_poll; } +/* keys for draw with a tint brush */ +static void ed_keymap_gpencil_painting_tint(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Tint)", 0, 0); + keymap->poll = gp_stroke_paintmode_tint_poll; +} + /* Stroke Painting Keymap - Only when paintmode is enabled */ static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf) { @@ -200,6 +367,106 @@ static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf) wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0); keymap->poll = gp_stroke_weightmode_poll; } + +static void ed_keymap_gpencil_vertexpainting(wmKeyConfig *keyconf) +{ + /* set poll callback - so that this keymap only gets enabled when stroke vertex is enabled */ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex Mode", 0, 0); + keymap->poll = gp_stroke_vertexmode_poll; +} + +/* keys for vertex with a draw brush */ +static void ed_keymap_gpencil_vertexpainting_draw(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0); + keymap->poll = gp_stroke_vertexmode_draw_poll; +} + +/* keys for vertex with a blur brush */ +static void ed_keymap_gpencil_vertexpainting_blur(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0); + keymap->poll = gp_stroke_vertexmode_blur_poll; +} +/* keys for vertex with a average brush */ +static void ed_keymap_gpencil_vertexpainting_average(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Average)", 0, 0); + keymap->poll = gp_stroke_vertexmode_average_poll; +} +/* keys for vertex with a smear brush */ +static void ed_keymap_gpencil_vertexpainting_smear(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0); + keymap->poll = gp_stroke_vertexmode_smear_poll; +} +/* keys for vertex with a replace brush */ +static void ed_keymap_gpencil_vertexpainting_replace(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0); + keymap->poll = gp_stroke_vertexmode_replace_poll; +} +/* keys for sculpt with a smooth brush */ +static void ed_keymap_gpencil_sculptpainting_smooth(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0); + keymap->poll = gp_stroke_sculptmode_smooth_poll; +} +/* keys for sculpt with a thickness brush */ +static void ed_keymap_gpencil_sculptpainting_thickness(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0); + keymap->poll = gp_stroke_sculptmode_thickness_poll; +} +/* keys for sculpt with a strength brush */ +static void ed_keymap_gpencil_sculptpainting_strength(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0); + keymap->poll = gp_stroke_sculptmode_strength_poll; +} +/* keys for sculpt with a grab brush */ +static void ed_keymap_gpencil_sculptpainting_grab(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0); + keymap->poll = gp_stroke_sculptmode_grab_poll; +} +/* keys for sculpt with a push brush */ +static void ed_keymap_gpencil_sculptpainting_push(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0); + keymap->poll = gp_stroke_sculptmode_push_poll; +} +/* keys for sculpt with a twist brush */ +static void ed_keymap_gpencil_sculptpainting_twist(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0); + keymap->poll = gp_stroke_sculptmode_twist_poll; +} +/* keys for sculpt with a pinch brush */ +static void ed_keymap_gpencil_sculptpainting_pinch(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0); + keymap->poll = gp_stroke_sculptmode_pinch_poll; +} +/* keys for sculpt with a randomize brush */ +static void ed_keymap_gpencil_sculptpainting_randomize(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0); + keymap->poll = gp_stroke_sculptmode_randomize_poll; +} +/* keys for sculpt with a clone brush */ +static void ed_keymap_gpencil_sculptpainting_clone(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0); + keymap->poll = gp_stroke_sculptmode_clone_poll; +} +/* keys for weight with a draw brush */ +static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight (Draw)", 0, 0); + keymap->poll = gp_stroke_weightmode_draw_poll; +} + /* ==================== */ void ED_keymap_gpencil(wmKeyConfig *keyconf) @@ -210,8 +477,25 @@ void ED_keymap_gpencil(wmKeyConfig *keyconf) ed_keymap_gpencil_painting_draw(keyconf); ed_keymap_gpencil_painting_erase(keyconf); ed_keymap_gpencil_painting_fill(keyconf); + ed_keymap_gpencil_painting_tint(keyconf); ed_keymap_gpencil_sculpting(keyconf); + ed_keymap_gpencil_sculptpainting_smooth(keyconf); + ed_keymap_gpencil_sculptpainting_thickness(keyconf); + ed_keymap_gpencil_sculptpainting_strength(keyconf); + ed_keymap_gpencil_sculptpainting_grab(keyconf); + ed_keymap_gpencil_sculptpainting_push(keyconf); + ed_keymap_gpencil_sculptpainting_twist(keyconf); + ed_keymap_gpencil_sculptpainting_pinch(keyconf); + ed_keymap_gpencil_sculptpainting_randomize(keyconf); + ed_keymap_gpencil_sculptpainting_clone(keyconf); ed_keymap_gpencil_weightpainting(keyconf); + ed_keymap_gpencil_weightpainting_draw(keyconf); + ed_keymap_gpencil_vertexpainting(keyconf); + ed_keymap_gpencil_vertexpainting_draw(keyconf); + ed_keymap_gpencil_vertexpainting_blur(keyconf); + ed_keymap_gpencil_vertexpainting_average(keyconf); + ed_keymap_gpencil_vertexpainting_smear(keyconf); + ed_keymap_gpencil_vertexpainting_replace(keyconf); } /* ****************************************** */ @@ -226,6 +510,12 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_draw); WM_operatortype_append(GPENCIL_OT_fill); + WM_operatortype_append(GPENCIL_OT_vertex_paint); + WM_operatortype_append(GPENCIL_OT_vertex_color_brightness_contrast); + WM_operatortype_append(GPENCIL_OT_vertex_color_hsv); + WM_operatortype_append(GPENCIL_OT_vertex_color_invert); + WM_operatortype_append(GPENCIL_OT_vertex_color_levels); + WM_operatortype_append(GPENCIL_OT_vertex_color_set); /* Guides ----------------------- */ @@ -238,6 +528,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_paintmode_toggle); WM_operatortype_append(GPENCIL_OT_sculptmode_toggle); WM_operatortype_append(GPENCIL_OT_weightmode_toggle); + WM_operatortype_append(GPENCIL_OT_vertexmode_toggle); WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); WM_operatortype_append(GPENCIL_OT_select); @@ -253,6 +544,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_first); WM_operatortype_append(GPENCIL_OT_select_last); WM_operatortype_append(GPENCIL_OT_select_alternate); + WM_operatortype_append(GPENCIL_OT_select_color); WM_operatortype_append(GPENCIL_OT_duplicate); WM_operatortype_append(GPENCIL_OT_delete); @@ -263,6 +555,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_move_to_layer); WM_operatortype_append(GPENCIL_OT_layer_change); + WM_operatortype_append(GPENCIL_OT_layer_active); WM_operatortype_append(GPENCIL_OT_set_active_material); @@ -271,8 +564,10 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); WM_operatortype_append(GPENCIL_OT_reproject); + WM_operatortype_append(GPENCIL_OT_recalc_geometry); WM_operatortype_append(GPENCIL_OT_sculpt_paint); + WM_operatortype_append(GPENCIL_OT_weight_paint); /* Editing (Buttons) ------------ */ @@ -288,6 +583,9 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_duplicate); WM_operatortype_append(GPENCIL_OT_layer_duplicate_object); + WM_operatortype_append(GPENCIL_OT_layer_mask_add); + WM_operatortype_append(GPENCIL_OT_layer_mask_remove); + WM_operatortype_append(GPENCIL_OT_hide); WM_operatortype_append(GPENCIL_OT_reveal); WM_operatortype_append(GPENCIL_OT_lock_all); @@ -306,6 +604,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_convert); + WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil); + WM_operatortype_append(GPENCIL_OT_stroke_arrange); WM_operatortype_append(GPENCIL_OT_stroke_change_color); WM_operatortype_append(GPENCIL_OT_stroke_lock_color); @@ -325,8 +625,16 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_cutter); WM_operatortype_append(GPENCIL_OT_stroke_trim); WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance); + WM_operatortype_append(GPENCIL_OT_stroke_merge_material); + + WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); + WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); + + WM_operatortype_append(GPENCIL_OT_transform_fill); + WM_operatortype_append(GPENCIL_OT_reset_transform_fill); - WM_operatortype_append(GPENCIL_OT_brush_presets_create); + WM_operatortype_append(GPENCIL_OT_brush_reset); + WM_operatortype_append(GPENCIL_OT_brush_reset_all); /* vertex groups */ WM_operatortype_append(GPENCIL_OT_vertex_group_assign); diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c index 3d56cb0fcb1..e5b9d902210 100644 --- a/source/blender/editors/gpencil/gpencil_ops_versioning.c +++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c @@ -130,7 +130,6 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) copy_v4_v4(gp_style->fill_rgba, palcolor->fill); /* set basic settings */ - 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); @@ -138,13 +137,13 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) gp_style->texture_opacity = 1.0f; gp_style->texture_pixsize = 100.0f; - gp_style->flag |= GP_STYLE_STROKE_SHOW; - gp_style->flag |= GP_STYLE_FILL_SHOW; + gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + gp_style->flag |= GP_MATERIAL_FILL_SHOW; /* fix strokes */ - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { gps->mat_nr = ob->totcol - 1; gps->colorname[0] = '\0'; @@ -174,7 +173,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { /* fix layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* unlock/unhide layer */ gpl->flag &= ~GP_LAYER_LOCKED; gpl->flag &= ~GP_LAYER_HIDE; @@ -182,8 +181,8 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op) gpl->opacity = 1.0f; /* disable tint */ gpl->tintcolor[3] = 0.0f; - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if ((gps->colorname[0] != '\0') && (STREQ(gps->colorname, palcolor->info))) { /* copy color settings */ copy_v4_v4(gpl->color, palcolor->color); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index afc0e66a8a6..fea589746c4 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -149,6 +149,8 @@ typedef struct tGPsdata { /** current object. */ Object *ob; + /** Obeject eval. */ + Object *ob_eval; /** window where painting originated. */ wmWindow *win; /** area where painting originated. */ @@ -215,6 +217,8 @@ typedef struct tGPsdata { float imat[4][4]; float mat[4][4]; + float diff_mat[4][4]; + /** custom color - hack for enforcing a particular color for track/mask editing. */ float custom_color[4]; @@ -270,11 +274,6 @@ static void gp_update_cache(bGPdata *gpd) } } -static bool gp_stroke_added_check(tGPsdata *p) -{ - return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); -} - static void gp_stroke_added_enable(tGPsdata *p) { BLI_assert(p->gpf->strokes.last != NULL); @@ -353,7 +352,7 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3]) if (p->ownerPtr.type == &RNA_Object) { ob = (Object *)p->ownerPtr.data; } - ED_gp_get_drawing_reference(p->scene, ob, p->gpl, *p->align_flag, vec); + ED_gpencil_drawing_reference_get(p->scene, ob, p->gpl, *p->align_flag, vec); } /* Stroke Editing ---------------------------- */ @@ -483,49 +482,26 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[ } } -/* apply jitter to stroke */ -static void gp_brush_jitter(bGPdata *gpd, - Brush *brush, - tGPspoint *pt, - const float mval[2], - const float pressure, - float r_mval[2], - RNG *rng) +/* Apply jitter to stroke point. */ +static void gp_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude) { - float tmp_pressure = pressure; - if (brush->gpencil_settings->draw_jitter > 0.0f) { - float curvef = BKE_curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); - tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - } - /* exponential value */ - const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * - (brush->gpencil_settings->draw_jitter + 2.0f); - const float fac = BLI_rng_get_float(rng) * exfactor * tmp_pressure; - /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ - float mvec[2], svec[2]; - /* mouse movement in ints -> floats */ + /* Jitter is applied perpendicular to the mouse movement vector (2D space). */ + float mvec[2]; + /* Mouse movement in ints -> floats. */ if (gpd->runtime.sbuffer_used > 1) { - mvec[0] = (mval[0] - (pt - 1)->x); - mvec[1] = (mval[1] - (pt - 1)->y); + tGPspoint *pt_prev = pt - 1; + sub_v2_v2v2(mvec, &pt->x, &pt_prev->x); normalize_v2(mvec); } else { mvec[0] = 0.0f; mvec[1] = 0.0f; } - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - /* scale the displacement by the random, and apply */ - if (BLI_rng_get_float(rng) > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); - } - - r_mval[0] = mval[0] + svec[0]; - r_mval[1] = mval[1] + svec[1]; + /* Rotate mvec by 90 degrees... */ + SWAP(float, mvec[0], mvec[1]); + mvec[0] -= mvec[0]; + /* Scale by displacement amount, and apply. */ + madd_v2_v2fl(&pt->x, mvec, amplitude); } /* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ @@ -581,6 +557,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) { bGPdata *gpd = p->gpd; + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; const short num_points = gpd->runtime.sbuffer_used; /* Do nothing if not enough points to smooth out */ @@ -628,9 +605,12 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) strength += ptd->strength * average_fac; } - /* Based on influence factor, blend between original and optimal smoothed coordinate. */ - interp_v2_v2v2(c, c, sco, inf); - copy_v2_v2(&ptc->x, c); + /* Based on influence factor, blend between original and optimal smoothed coordinate but not + * for Guide mode. */ + if (!guide->use_guide) { + interp_v2_v2v2(c, c, sco, inf); + copy_v2_v2(&ptc->x, c); + } /* Interpolate pressure. */ ptc->pressure = interpf(ptc->pressure, pressure, inf); /* Interpolate strength. */ @@ -661,140 +641,59 @@ static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int t tGPspoint *ptd = &points[i]; float sco[2] = {0.0f}; + float pressure = 0.0f; + float strength = 0.0f; /* Compute smoothed coordinate by taking the ones nearby */ if (pta) { madd_v2_v2fl(sco, &pta->x, average_fac); + pressure += pta->pressure * average_fac; + strength += pta->strength * average_fac; } else { madd_v2_v2fl(sco, &ptc->x, average_fac); + pressure += ptc->pressure * average_fac; + strength += ptc->strength * average_fac; } if (ptb) { madd_v2_v2fl(sco, &ptb->x, average_fac); + pressure += ptb->pressure * average_fac; + strength += ptb->strength * average_fac; } else { madd_v2_v2fl(sco, &ptc->x, average_fac); + pressure += ptc->pressure * average_fac; + strength += ptc->strength * average_fac; } madd_v2_v2fl(sco, &ptc->x, average_fac); + pressure += ptc->pressure * average_fac; + strength += ptc->strength * average_fac; madd_v2_v2fl(sco, &ptd->x, average_fac); + pressure += ptd->pressure * average_fac; + strength += ptd->strength * average_fac; /* Based on influence factor, blend between original and optimal smoothed coordinate. */ interp_v2_v2v2(&ptc->x, &ptc->x, sco, inf); - } -} - -/* Smooth all the sections created with fake events to avoid abrupt transitions. - * - * As the fake events add points between two real events, this produces a straight line, but if - * there is 3 or more real points that used fakes, the stroke is not smooth and produces abrupt - * angles. - * This function reads these segments and finds the real points and smooth with the surrounding - * points. */ -static void gp_smooth_fake_segments(tGPsdata *p) -{ - bGPdata *gpd = p->gpd; - Brush *brush = p->brush; - if (brush->gpencil_settings->input_samples < 2) { - return; - } - - tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - tGPspoint *pt = NULL; - /* Index where segment starts. */ - int from_idx = 0; - /* Index where segment ends. */ - int to_idx = 0; - - bool doit = false; - /* Loop all points except the extremes. */ - for (int i = 1; i < gpd->runtime.sbuffer_used - 1; i++) { - pt = &points[i]; - bool is_fake = (bool)(pt->tflag & GP_TPOINT_FAKE); - to_idx = i; - - /* Detect fake points in the stroke. */ - if ((!doit) && (is_fake)) { - from_idx = i; - doit = true; - } - /* If detect control point after fake points, select a segment with same length in both sides, - * except if it is more than stroke length. */ - if ((doit) && (!is_fake)) { - if (i + (i - from_idx) < gpd->runtime.sbuffer_used - 1) { - to_idx = i + (i - from_idx); - /* Smooth this segments (need loop to get cumulative smooth). */ - for (int r = 0; r < 5; r++) { - gp_smooth_segment(gpd, 0.1f, from_idx, to_idx); - } - } - else { - break; - } - /* Reset to new segments. */ - from_idx = i; - doit = false; - } - } -} - -/* Smooth the section added with fake events when pen moves very fast. */ -static void gp_smooth_fake_events(tGPsdata *p, int size_before, int size_after) -{ - bGPdata *gpd = p->gpd; - const short totpoints = size_after - size_before - 1; - /* Do nothing if not enough data to smooth out. */ - if (totpoints < 1) { - return; - } - - /* Back two points to get smoother effect. */ - size_before -= 2; - CLAMP_MIN(size_before, 1); - - tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; - /* Extreme points. */ - const tGPspoint *pta = &points[size_before - 1]; - const tGPspoint *ptb = &points[size_after - 1]; - tGPspoint *pt1, *pt2; - int i; - - /* Get total length of the segment to smooth. */ - float totlen = 0.0f; - for (i = size_before; i < size_after; i++) { - pt1 = &points[i - 1]; - pt2 = &points[i]; - totlen += len_v2v2(&pt1->x, &pt2->x); - } - /* Smooth interpolating the position of the points. */ - float pointlen = 0.0f; - for (i = size_before; i < size_after - 1; i++) { - pt1 = &points[i - 1]; - pt2 = &points[i]; - pointlen += len_v2v2(&pt1->x, &pt2->x); - pt2->pressure = interpf(ptb->pressure, pta->pressure, pointlen / totlen); - pt2->strength = interpf(ptb->strength, pta->strength, pointlen / totlen); + /* Interpolate pressure. */ + ptc->pressure = interpf(ptc->pressure, pressure, inf); + /* Interpolate strength. */ + ptc->strength = interpf(ptc->strength, strength, inf); } } /* add current stroke-point to buffer (returns whether point was successfully added) */ -static short gp_stroke_addpoint( - tGPsdata *p, const float mval[2], float pressure, double curtime, bool is_fake) +static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime) { bGPdata *gpd = p->gpd; Brush *brush = p->brush; + BrushGpencilSettings *brush_settings = p->brush->gpencil_settings; tGPspoint *pt; - ToolSettings *ts = p->scene->toolsettings; Object *obact = (Object *)p->ownerPtr.data; - Depsgraph *depsgraph = p->depsgraph; RegionView3D *rv3d = p->region->regiondata; - View3D *v3d = p->sa->spacedata.first; - MaterialGPencilStyle *gp_style = p->material->gp_style; - const int def_nr = obact->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); /* check painting mode */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { @@ -843,111 +742,73 @@ static short gp_stroke_addpoint( return GP_STROKEADD_INVALID; } + /* Set vertex colors for buffer. */ + ED_gpencil_sbuffer_vertex_color_set( + p->depsgraph, p->ob, p->scene->toolsettings, p->brush, p->material); + /* get pointer to destination point */ pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used); - /* Set if point was created by fake events. */ - if (is_fake) { - pt->tflag |= GP_TPOINT_FAKE; - } - else { - pt->tflag &= ~GP_TPOINT_FAKE; - } - /* store settings */ + pt->strength = brush_settings->draw_strength; + pt->pressure = 1.0f; + pt->uv_rot = 0.0f; + copy_v2_v2(&pt->x, mval); + /* pressure */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { - float curvef = BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_sensitivity, 0, pressure); - pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; - } - else { - pt->pressure = 1.0f; + if (brush_settings->flag & GP_BRUSH_USE_PRESSURE) { + pt->pressure *= BKE_curvemapping_evaluateF(brush_settings->curve_sensitivity, 0, pressure); } - /* Apply jitter to position */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_jitter > 0.0f)) { - float r_mval[2]; - const float jitpress = (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) ? - pressure : - 1.0f; - gp_brush_jitter(gpd, brush, pt, mval, jitpress, r_mval, p->rng); - copy_v2_v2(&pt->x, r_mval); - } - else { - copy_v2_v2(&pt->x, mval); + /* color strength */ + if (brush_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + pt->strength *= BKE_curvemapping_evaluateF(brush_settings->curve_strength, 0, pressure); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } - /* apply randomness to pressure */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_press > 0.0f)) { - float curvef = BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_sensitivity, 0, pressure); - float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * - BLI_rng_get_float(p->rng); + + if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { + /* Apply jitter to position */ + if (brush_settings->draw_jitter > 0.0f) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + float jitpress = 1.0f; + if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { + jitpress = BKE_curvemapping_evaluateF(brush_settings->curve_jitter, 0, pressure); + } + /* FIXME the +2 means minimum jitter is 4 which is a bit strange for UX. */ + const float exp_factor = brush_settings->draw_jitter + 2.0f; + const float fac = rand * square_f(exp_factor) * jitpress; + gp_brush_jitter(gpd, pt, fac); } - else { - pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * - BLI_rng_get_float(p->rng); + /* apply randomness to pressure */ + if (brush_settings->draw_random_press > 0.0f) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + pt->pressure *= 1.0 + rand * 2.0 * brush_settings->draw_random_press; + CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); } - CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); - } - - /* apply randomness to uv texture rotation */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->uv_random > 0.0f)) { - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random; + /* apply randomness to uv texture rotation */ + if (brush_settings->uv_random > 0.0f) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + pt->uv_rot += rand * M_PI * brush_settings->uv_random; + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); } - else { - pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; + /* apply randomness to color strength */ + if (brush_settings->draw_random_strength) { + float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f; + pt->strength *= 1.0 + rand * brush_settings->draw_random_strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } - CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); - } - else { - pt->uv_rot = 0.0f; } /* apply angle of stroke to brush size */ - if (brush->gpencil_settings->draw_angle_factor != 0.0f) { + if (brush_settings->draw_angle_factor != 0.0f) { gp_brush_angle(gpd, brush, pt, mval); } - /* color strength */ - if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { - float curvef = BKE_curvemapping_evaluateF( - brush->gpencil_settings->curve_strength, 0, pressure); - float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - - pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; - } - else { - pt->strength = brush->gpencil_settings->draw_strength; - } - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - - /* apply randomness to color strength */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_strength > 0.0f)) { - if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * - BLI_rng_get_float(p->rng); - } - else { - pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * - BLI_rng_get_float(p->rng); - } - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - } - /* point time */ pt->time = (float)(curtime - p->inittime); /* point uv (only 3d view) */ if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_used > 0)) { - float pixsize = gp_style->texture_pixsize / 1000000.0f; tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; bGPDspoint spt, spt2; @@ -961,11 +822,8 @@ static short gp_stroke_addpoint( /* reproject previous */ ED_gpencil_tpoint_to_point(p->region, origin, ptb, &spt2); ED_gp_project_point_to_plane(p->scene, obact, rv3d, origin, p->lock_axis - 1, &spt2); - p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + p->totpixlen += len_v3v3(&spt.x, &spt2.x); pt->uv_fac = p->totpixlen; - if ((gp_style) && (gp_style->sima)) { - pt->uv_fac /= gp_style->sima->gen_x; - } } else { p->totpixlen = 0.0f; @@ -975,7 +833,7 @@ static short gp_stroke_addpoint( /* increment counters */ gpd->runtime.sbuffer_used++; - /* smooth while drawing previous points with a reduction factor for previous */ + /* Smooth while drawing previous points with a reduction factor for previous. */ if (brush->gpencil_settings->active_smooth > 0.0f) { for (int s = 0; s < 3; s++) { gp_smooth_buffer(p, @@ -984,100 +842,11 @@ static short gp_stroke_addpoint( } } - return GP_STROKEADD_NORMAL; - } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - - /* enable special flag for drawing engine */ - gpd->flag |= GP_DATA_STROKE_POLYGON; - - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->runtime.sbuffer); - - /* store settings */ - copy_v2_v2(&pt->x, mval); - /* T44932 - Pressure vals are unreliable, so ignore for now */ - pt->pressure = 1.0f; - pt->strength = 1.0f; - pt->time = (float)(curtime - p->inittime); - - /* if there's stroke for this poly line session add (or replace last) point - * to stroke. This allows to draw lines more interactively (see new segment - * during mouse slide, e.g.) - */ - if (gp_stroke_added_check(p)) { - bGPDstroke *gps = p->gpf->strokes.last; - bGPDspoint *pts; - MDeformVert *dvert = NULL; - - /* First time point is adding to temporary buffer (need to allocate new point in stroke) */ - if (gpd->runtime.sbuffer_used == 0) { - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - if (gps->dvert != NULL) { - gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); - } - gps->totpoints++; - } - - pts = &gps->points[gps->totpoints - 1]; - if (gps->dvert != NULL) { - dvert = &gps->dvert[gps->totpoints - 1]; - } - /* special case for poly lines: normally, - * depth is needed only when creating new stroke from buffer, - * but poly lines are converting to stroke instantly, - * so initialize depth buffer before converting coordinates - */ - if (gpencil_project_check(p)) { - view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_autodist_init(p->depsgraph, - p->region, - v3d, - (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); - } - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts); - /* copy pressure and time */ - pts->pressure = pt->pressure; - pts->strength = pt->strength; - pts->time = pt->time; - pts->uv_fac = pt->uv_fac; - pts->uv_rot = pt->uv_rot; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - } - } - - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); - } - - /* increment counters */ - if (gpd->runtime.sbuffer_used == 0) { - gpd->runtime.sbuffer_used++; - } + /* Update evaluated data. */ + ED_gpencil_sbuffer_update_eval(gpd, p->ob_eval); return GP_STROKEADD_NORMAL; } - /* return invalid state for now... */ return GP_STROKEADD_INVALID; } @@ -1143,42 +912,25 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) return; } - /* special case for poly line -- for already added stroke during session - * coordinates are getting added to stroke immediately to allow more - * interactive behavior - */ - if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* be sure to hide any lazy cursor */ - ED_gpencil_toggle_brush_cursor(p->C, true, NULL); - - if (gp_stroke_added_check(p)) { - return; - } - } - /* allocate memory for a new stroke */ gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); /* copy appropriate settings for stroke */ gps->totpoints = totelem; gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); + gps->fill_opacity_fac = 1.0f; + gps->hardeness = brush->gpencil_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); gps->flag = gpd->runtime.sbuffer_sflag; gps->inittime = p->inittime; - - /* enable recalculation flag by default (only used if hq fill) */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + gps->uv_scale = 1.0f; /* allocate enough memory for a continuous array for storage points */ const int subdivide = brush->gpencil_settings->draw_subdivide; gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = NULL; - /* initialize triangle memory to dummy data */ - gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; /* drawing batch cache is dirty now */ gp_update_cache(p->gpd); /* set pointer to first non-initialized point */ @@ -1187,6 +939,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) dvert = gps->dvert + (gps->totpoints - totelem); } + /* Apply the vertex color to fill. */ + ED_gpencil_fill_vertex_color_set(ts, brush, gps); + /* copy points from the buffer to the stroke */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only -> only endpoints */ @@ -1201,6 +956,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + /* Apply the vertex color to point. */ + ED_gpencil_point_vertex_color_set(ts, brush, pt); + pt++; if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { @@ -1231,6 +989,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + /* Apply the vertex color to point. */ + ED_gpencil_point_vertex_color_set(ts, brush, pt); if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { BKE_gpencil_dvert_ensure(gps); @@ -1252,7 +1012,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt = gps->points; for (i = 0; i < gps->totpoints; i++, pt++) { /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); + gp_apply_parent_point(depsgraph, obact, gpl, pt); } /* if camera view, reproject flat to view to avoid perspective effect */ @@ -1260,40 +1020,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); } } - else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { - /* first point */ - ptc = gpd->runtime.sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* reproject to plane (only in 3d space) */ - gp_reproject_toplane(p, gps); - /* if parented change position relative to parent object */ - gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); - /* if camera view, reproject flat to view to avoid perspective effect */ - if (is_camera) { - ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); - } - /* copy pressure and time */ - pt->pressure = ptc->pressure; - pt->strength = ptc->strength; - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = ptc->time; - - if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { - BKE_gpencil_dvert_ensure(gps); - MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr); - if (dw) { - dw->weight = ts->vgroup_weight; - } - } - else { - if (dvert != NULL) { - dvert->totweight = 0; - dvert->dw = NULL; - } - } - } else { float *depth_arr = NULL; @@ -1370,9 +1096,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } } - /* Smooth any point created with fake events when the mouse/pen move very fast. */ - gp_smooth_fake_segments(p); - pt = gps->points; dvert = gps->dvert; @@ -1389,6 +1112,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->time = ptc->time; pt->uv_fac = ptc->uv_fac; pt->uv_rot = ptc->uv_rot; + /* Apply the vertex color to point. */ + ED_gpencil_point_vertex_color_set(ts, brush, pt); if (dvert != NULL) { dvert->totweight = 0; @@ -1401,11 +1126,6 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { gp_subdivide_stroke(gps, subdivide); } - /* apply randomness to stroke */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && - (brush->gpencil_settings->draw_random_sub > 0.0f)) { - gp_randomize_stroke(gps, brush, p->rng); - } /* Smooth stroke after subdiv - only if there's something to do for each iteration, * the factor is reduced to get a better smoothing @@ -1415,8 +1135,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) float reduce = 0.0f; for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); - BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); + BKE_gpencil_stroke_smooth(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac); } reduce += 0.25f; /* reduce the factor */ } @@ -1425,23 +1145,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* Simplify adaptive */ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (brush->gpencil_settings->simplify_f > 0.0f)) { - BKE_gpencil_simplify_stroke(gps, brush->gpencil_settings->simplify_f); - } - - /* smooth thickness */ - if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && - (brush->gpencil_settings->thick_smoothfac > 0.0f)) { - for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) { - for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); - } - } + BKE_gpencil_stroke_simplify_adaptive(gps, brush->gpencil_settings->simplify_f); } /* reproject to plane (only in 3d space) */ gp_reproject_toplane(p, gps); /* change position relative to parent object */ - gp_apply_parent(depsgraph, obact, gpd, gpl, gps); + gp_apply_parent(depsgraph, obact, gpl, gps); /* if camera view, reproject flat to view to avoid perspective effect */ if (is_camera) { ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps); @@ -1463,15 +1173,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } } - /* calculate UVs along the stroke */ - ED_gpencil_calc_stroke_uv(obact, gps); - /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke * is added on listbase head because the drawing order is inverse and the head stroke is the * first to draw. This is very useful for artist when drawing the background. */ - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && - (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { + if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) { BLI_addhead(&p->gpf->strokes, gps); } else { @@ -1492,9 +1198,12 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* post process stroke */ if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) { - BKE_gpencil_trim_stroke(gps); + BKE_gpencil_stroke_trim(gps); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + gp_stroke_added_enable(p); } @@ -1540,7 +1249,7 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, float diff_mat[4][4]; /* calculate difference matrix if parent object */ - ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(p->depsgraph, obact, gpl, diff_mat); if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); @@ -1642,9 +1351,7 @@ static void gp_stroke_soft_refine(bGPDstroke *gps) } /* eraser tool - evaluation per stroke */ -/* TODO: this could really do with some optimization (KD-Tree/BVH?) */ static void gp_stroke_eraser_dostroke(tGPsdata *p, - bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const float mval[2], @@ -1652,21 +1359,15 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, const int radius, const rcti *rect) { - Depsgraph *depsgraph = p->depsgraph; - Object *obact = (Object *)p->ownerPtr.data; Brush *eraser = p->eraser; bGPDspoint *pt0, *pt1, *pt2; int pc0[2] = {0}; int pc1[2] = {0}; int pc2[2] = {0}; int i; - float diff_mat[4][4]; int mval_i[2]; round_v2i_v2fl(mval_i, mval); - /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); - if (gps->totpoints == 0) { /* just free stroke */ gp_free_stroke(p->gpd, gpf, gps); @@ -1675,7 +1376,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_parent_space(gps->points, p->diff_mat, &pt_temp); gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { @@ -1699,7 +1400,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* get points to work with */ pt1 = gps->points + i; bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_parent_space(pt1, p->diff_mat, &npt); gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); /* do boundbox check first */ @@ -1751,7 +1452,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, bGPDspoint npt; if (pt0) { - gp_point_to_parent_space(pt0, diff_mat, &npt); + gp_point_to_parent_space(pt0, p->diff_mat, &npt); gp_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]); } else { @@ -1759,10 +1460,10 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, copy_v2_v2_int(pc0, pc1); } - gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_parent_space(pt1, p->diff_mat, &npt); gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); - gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_parent_space(pt2, p->diff_mat, &npt); gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); /* Check that point segment of the boundbox of the eraser stroke */ @@ -1862,8 +1563,6 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* erase strokes which fall under the eraser strokes */ static void gp_stroke_doeraser(tGPsdata *p) { - bGPDlayer *gpl; - bGPDstroke *gps, *gpn; rcti rect; Brush *brush = p->brush; Brush *eraser = p->eraser; @@ -1903,29 +1602,36 @@ static void gp_stroke_doeraser(tGPsdata *p) * only a subset of layers, it is harder to perform the same erase operation * on multiple layers... */ - for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) { bGPDframe *gpf = gpl->actframe; /* only affect layer if it's editable (and visible) */ - if (gpencil_layer_is_editable(gpl) == false) { + if (BKE_gpencil_layer_is_editable(gpl) == false) { continue; } else if (gpf == NULL) { continue; } + /* calculate difference matrix */ + BKE_gpencil_parent_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat); /* loop over strokes, checking segments for intersections */ - for (gps = gpf->strokes.first; gps; gps = gpn) { - gpn = gps->next; + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { /* check if the color is editable */ if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { continue; } + + /* Check if the stroke collide with mouse. */ + if (!ED_gpencil_stroke_check_collision(&p->gsc, gps, p->mval, calc_radius, p->diff_mat)) { + continue; + } + /* Not all strokes in the datablock may be valid in the current editor/context * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) */ if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { - gp_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, p->mvalo, calc_radius, &rect); + gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->mvalo, calc_radius, &rect); } } } @@ -1964,7 +1670,7 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) { Brush *brush_dft = NULL; Paint *paint = &ts->gp_paint->paint; - Brush *brush_old = paint->brush; + Brush *brush_prev = paint->brush; for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) { if (brush->gpencil_settings == NULL) { continue; @@ -1987,15 +1693,15 @@ static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) } /* create a new soft eraser brush */ else { - brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); + brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL); brush_dft->size = 30.0f; - brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; brush_dft->gpencil_tool = GPAINT_TOOL_ERASE; brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; /* reset current brush */ - BKE_paint_brush_set(paint, brush_old); + BKE_paint_brush_set(paint, brush_prev); return brush_dft; } @@ -2032,7 +1738,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(bmain, ts); + BKE_brush_gpencil_paint_presets(bmain, ts); changed = true; } /* be sure curves are initializated */ @@ -2054,10 +1760,7 @@ static void gp_init_drawing_brush(bContext *C, tGPsdata *p) /* use radius of eraser */ p->radius = (short)p->eraser->size; - /* GPXX: Need this update to synchronize brush with draw manager. - * Maybe this update can be removed when the new tool system - * will be in place, but while, we need this to keep drawing working. - */ + /* Need this update to synchronize brush with draw manager. */ if (changed) { DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); } @@ -2069,33 +1772,11 @@ static void gp_init_colors(tGPsdata *p) bGPdata *gpd = p->gpd; Brush *brush = p->brush; - MaterialGPencilStyle *gp_style = NULL; - /* use brush material */ p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); - /* assign color information to temp tGPsdata */ - gp_style = p->material->gp_style; - if (gp_style) { - - /* set colors */ - if (gp_style->flag & GP_STYLE_STROKE_SHOW) { - copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); - } - else { - /* if no stroke, use fill */ - copy_v4_v4(gpd->runtime.scolor, gp_style->fill_rgba); - } - copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba); - /* add some alpha to make easy the filling without hide strokes */ - if (gpd->runtime.sfill[3] > 0.8f) { - gpd->runtime.sfill[3] = 0.8f; - } - - gpd->runtime.mode = (short)gp_style->mode; - gpd->runtime.bstroke_style = gp_style->stroke_style; - gpd->runtime.bfill_style = gp_style->fill_style; - } + gpd->runtime.matid = BKE_object_material_slot_find_index(p->ob, p->material); + gpd->runtime.sbuffer_brush = brush; } /* (re)init new painting data */ @@ -2131,9 +1812,6 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) switch (curarea->spacetype) { /* supported views first */ case SPACE_VIEW3D: { - /* View3D *v3d = curarea->spacedata.first; */ - /* RegionView3D *rv3d = region->regiondata; */ - /* set current area * - must verify that region data is 3D-view (and not something else) */ @@ -2162,10 +1840,11 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) local_view_bits = v3d->local_view_uuid; } /* create new default object */ - obact = ED_gpencil_add_object(C, p->scene, cur, local_view_bits); + obact = ED_gpencil_add_object(C, cur, local_view_bits); } /* assign object after all checks to be sure we have one active */ p->ob = obact; + p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob); break; } @@ -2204,21 +1883,14 @@ static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) gp_init_drawing_brush(C, p); /* setup active color */ - if (curarea->spacetype == SPACE_VIEW3D) { - /* region where paint was originated */ - p->gpd->runtime.ar = CTX_wm_region(C); - - /* NOTE: This is only done for 3D view, as Materials aren't used for - * annotations in 2D editors - */ - int totcol = p->ob->totcol; + /* region where paint was originated */ + p->gpd->runtime.ar = CTX_wm_region(C); + int totcol = p->ob->totcol; + gp_init_colors(p); - gp_init_colors(p); - - /* check whether the material was newly added */ - if (totcol != p->ob->totcol) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); - } + /* check whether the material was newly added */ + if (totcol != p->ob->totcol) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL); } /* lock axis (in some modes, disable) */ @@ -2274,7 +1946,6 @@ static void gp_session_cleanup(tGPsdata *p) /* free stroke buffer */ if (gpd->runtime.sbuffer) { - /* printf("\t\tGP - free sbuffer\n"); */ MEM_SAFE_FREE(gpd->runtime.sbuffer); gpd->runtime.sbuffer = NULL; } @@ -2300,12 +1971,13 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps { Scene *scene = p->scene; ToolSettings *ts = scene->toolsettings; + bool changed = false; /* get active layer (or add a new one if non-existent) */ - p->gpl = BKE_gpencil_layer_getactive(p->gpd); + p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); - + changed = true; if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); } @@ -2318,46 +1990,28 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps return; } - /* get active frame (add a new one if not matching frame) */ + /* Eraser mode: If no active strokes, just return. */ if (paintmode == GP_PAINTMODE_ERASER) { - /* Eraser mode: - * 1) Add new frames to all frames that we might touch, - * 2) Ensure that p->gpf refers to the frame used for the active layer - * (to avoid problems with other tools which expect it to exist) - */ bool has_layer_to_erase = false; - for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) { /* Skip if layer not editable */ - if (gpencil_layer_is_editable(gpl) == false) { + if (BKE_gpencil_layer_is_editable(gpl) == false) { continue; } - /* Add a new frame if needed (and based off the active frame, - * as we need some existing strokes to erase) - * - * Note: We don't add a new frame if there's nothing there now, so - * -> If there are no frames at all, don't add one - * -> If there are no strokes in that frame, don't add a new empty frame - */ if (gpl->actframe && gpl->actframe->strokes.first) { - gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); has_layer_to_erase = true; + break; } - - /* XXX: we omit GP_FRAME_PAINT here for now, - * as it is only really useful for doing - * paintbuffer drawing - */ } - /* Ensure this gets set... */ - p->gpf = p->gpl->actframe; - if (has_layer_to_erase == false) { p->status = GP_STATUS_ERROR; return; } + /* Ensure this gets set... */ + p->gpf = p->gpl->actframe; } else { /* Drawing Modes - Add a new frame if needed on the active layer */ @@ -2370,7 +2024,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps add_frame_mode = GP_GETFRAME_ADD_NEW; } - p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_frame_get(p->gpl, CFRA, add_frame_mode); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -2397,8 +2051,6 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* set special fill stroke mode */ if (p->disable_fill == true) { p->gpd->runtime.sbuffer_sflag |= GP_STROKE_NOFILL; - /* replace stroke color with fill color */ - copy_v4_v4(p->gpd->runtime.scolor, p->gpd->runtime.sfill); } /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ @@ -2443,6 +2095,14 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps } } } + if (!changed) { + /* Copy the brush to avoid a full tag (very slow). */ + bGPdata *gpd_eval = (bGPdata *)p->ob_eval->data; + gpd_eval->runtime.sbuffer_brush = p->gpd->runtime.sbuffer_brush; + } + else { + gp_update_cache(p->gpd); + } } /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ @@ -2487,7 +2147,6 @@ static void gp_paint_cleanup(tGPsdata *p) p->gpf->flag &= ~GP_FRAME_PAINT; } } - /* ------------------------------- */ /* Helper callback for drawing the cursor itself */ @@ -2591,10 +2250,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) else { /* drawing batch cache is dirty now */ bGPdata *gpd = CTX_data_gpencil_data(C); - if (gpd) { - gpd->flag &= ~GP_DATA_STROKE_POLYGON; - gp_update_cache(gpd); - } + gp_update_cache(gpd); } /* clear undo stack */ @@ -2665,23 +2321,6 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) /* ------------------------------- */ -/* ensure that the correct cursor icon is set */ -static void gpencil_draw_cursor_set(tGPsdata *p) -{ - UNUSED_VARS(p); - return; - /* Disable while we get a better cursor handling for direct input devices (Cintiq/Ipad)*/ -#if 0 - Brush *brush = p->brush; - if ((p->paintmode == GP_PAINTMODE_ERASER) || (brush->gpencil_tool == GPAINT_TOOL_ERASE)) { - WM_cursor_modal_set(p->win, WM_CURSOR_CROSS); /* XXX need a better cursor */ - } - else { - WM_cursor_modal_set(p->win, WM_CURSOR_NONE); - } -#endif -} - /* update UI indicators of status, including cursor and header prints */ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) { @@ -2724,13 +2363,6 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) } break; } - case GP_PAINTMODE_DRAW_POLY: { - ED_workspace_status_text( - C, - TIP_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " - "Release Shift/ESC/Enter to end (or click outside this area)")); - break; - } default: /* unhandled future cases */ { ED_workspace_status_text( @@ -2753,88 +2385,6 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* ------------------------------- */ -/* create a new stroke point at the point indicated by the painting context */ -static void gpencil_draw_apply( - bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph, bool is_fake) -{ - bGPdata *gpd = p->gpd; - tGPspoint *pt = NULL; - - /* handle drawing/erasing -> test for erasing first */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - /* do 'live' erasing now */ - gp_stroke_doeraser(p); - - /* store used values */ - copy_v2_v2(p->mvalo, p->mval); - p->opressure = p->pressure; - } - /* Only add current point to buffer if mouse moved - * (even though we got an event, it might be just noise). */ - else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { - - /* if lazy mouse, interpolate the last and current mouse positions */ - if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { - float now_mouse[2]; - float last_mouse[2]; - copy_v2_v2(now_mouse, p->mval); - copy_v2_v2(last_mouse, p->mvalo); - interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); - copy_v2_v2(p->mval, now_mouse); - } - - /* try to add point */ - short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake); - - /* handle errors while adding point */ - if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { - /* finish off old stroke */ - gp_paint_strokeend(p); - /* And start a new one!!! Else, projection errors! */ - gp_paint_initstroke(p, p->paintmode, depsgraph); - - /* start a new stroke, starting from previous point */ - /* XXX Must manually reset inittime... */ - /* XXX We only need to reuse previous point if overflow! */ - if (ok == GP_STROKEADD_OVERFLOW) { - p->inittime = p->ocurtime; - gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime, is_fake); - } - else { - p->inittime = p->curtime; - } - gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake); - } - else if (ok == GP_STROKEADD_INVALID) { - /* the painting operation cannot continue... */ - BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); - p->status = GP_STATUS_ERROR; - - if (G.debug & G_DEBUG) { - printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); - } - return; - } - - /* store used values */ - copy_v2_v2(p->mvalo, p->mval); - p->opressure = p->pressure; - p->ocurtime = p->curtime; - - pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; - if (p->paintmode != GP_PAINTMODE_ERASER) { - ED_gpencil_toggle_brush_cursor(C, true, &pt->x); - } - } - else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && - (gpd->runtime.sbuffer_used > 0)) { - pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; - if (p->paintmode != GP_PAINTMODE_ERASER) { - ED_gpencil_toggle_brush_cursor(C, true, &pt->x); - } - } -} - /* Helper to rotate point around origin */ static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], @@ -2956,66 +2506,151 @@ static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide) } /* apply speed guide */ -static void gpencil_speed_guide(tGPsdata *p, GP_Sculpt_Guide *guide) +static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guide, float point[2]) { switch (guide->type) { default: case GP_GUIDE_CIRCULAR: { - dist_ensure_v2_v2fl(p->mval, p->guide.origin, p->guide.origin_distance); + dist_ensure_v2_v2fl(point, p->guide.origin, p->guide.origin_distance); break; } case GP_GUIDE_RADIAL: { if (guide->use_snapping && (guide->angle_snap > 0.0f)) { - closest_to_line_v2(p->mval, p->mval, p->guide.rot_point, p->guide.origin); + closest_to_line_v2(point, point, p->guide.rot_point, p->guide.origin); } else { - closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.origin); + closest_to_line_v2(point, point, p->mvali, p->guide.origin); } break; } case GP_GUIDE_PARALLEL: { - closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point); + closest_to_line_v2(point, point, p->mvali, p->guide.rot_point); if (guide->use_snapping && (guide->spacing > 0.0f)) { - gp_snap_to_rotated_grid_fl(p->mval, p->guide.origin, p->guide.spacing, guide->angle); + gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, guide->angle); } break; } case GP_GUIDE_ISO: { - closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point); + closest_to_line_v2(point, point, p->mvali, p->guide.rot_point); if (guide->use_snapping && (guide->spacing > 0.0f)) { - gp_snap_to_rotated_grid_fl(p->mval, p->guide.origin, p->guide.spacing, p->guide.rot_angle); + gp_snap_to_rotated_grid_fl(point, p->guide.origin, p->guide.spacing, p->guide.rot_angle); } break; } case GP_GUIDE_GRID: { if (guide->use_snapping && (guide->spacing > 0.0f)) { - closest_to_line_v2(p->mval, p->mval, p->mvali, p->guide.rot_point); + closest_to_line_v2(point, point, p->mvali, p->guide.rot_point); if (p->straight == STROKE_HORIZONTAL) { - p->mval[1] = gp_snap_to_grid_fl(p->mval[1], p->guide.origin[1], p->guide.spacing); + point[1] = gp_snap_to_grid_fl(point[1], p->guide.origin[1], p->guide.spacing); } else { - p->mval[0] = gp_snap_to_grid_fl(p->mval[0], p->guide.origin[0], p->guide.spacing); + point[0] = gp_snap_to_grid_fl(point[0], p->guide.origin[0], p->guide.spacing); } } else if (p->straight == STROKE_HORIZONTAL) { - p->mval[1] = p->mvali[1]; /* replace y */ + point[1] = p->mvali[1]; /* replace y */ } else { - p->mval[0] = p->mvali[0]; /* replace x */ + point[0] = p->mvali[0]; /* replace x */ } break; } } } +/* create a new stroke point at the point indicated by the painting context */ +static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +{ + bGPdata *gpd = p->gpd; + tGPspoint *pt = NULL; + + /* handle drawing/erasing -> test for erasing first */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* do 'live' erasing now */ + gp_stroke_doeraser(p); + + /* store used values */ + copy_v2_v2(p->mvalo, p->mval); + p->opressure = p->pressure; + } + /* Only add current point to buffer if mouse moved + * (even though we got an event, it might be just noise). */ + else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { + + /* if lazy mouse, interpolate the last and current mouse positions */ + if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { + float now_mouse[2]; + float last_mouse[2]; + copy_v2_v2(now_mouse, p->mval); + copy_v2_v2(last_mouse, p->mvalo); + interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); + copy_v2_v2(p->mval, now_mouse); + + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; + bool is_speed_guide = ((guide->use_guide) && + (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); + if (is_speed_guide) { + gpencil_snap_to_guide(p, guide, p->mval); + } + } + + /* try to add point */ + short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + + /* handle errors while adding point */ + if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { + /* finish off old stroke */ + gp_paint_strokeend(p); + /* And start a new one!!! Else, projection errors! */ + gp_paint_initstroke(p, p->paintmode, depsgraph); + + /* start a new stroke, starting from previous point */ + /* XXX Must manually reset inittime... */ + /* XXX We only need to reuse previous point if overflow! */ + if (ok == GP_STROKEADD_OVERFLOW) { + p->inittime = p->ocurtime; + gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); + } + else { + p->inittime = p->curtime; + } + gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + } + else if (ok == GP_STROKEADD_INVALID) { + /* the painting operation cannot continue... */ + BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); + p->status = GP_STATUS_ERROR; + + if (G.debug & G_DEBUG) { + printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); + } + return; + } + + /* store used values */ + copy_v2_v2(p->mvalo, p->mval); + p->opressure = p->pressure; + p->ocurtime = p->curtime; + + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } + } + else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && + (gpd->runtime.sbuffer_used > 0)) { + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } + } +} + /* handle draw event */ static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, - Depsgraph *depsgraph, - float x, - float y, - const bool is_fake) + Depsgraph *depsgraph) { tGPsdata *p = op->customdata; GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; @@ -3025,13 +2660,12 @@ static void gpencil_draw_apply_event(bContext *C, (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); /* convert from window-space to area-space mouse coordinates - * add any x,y override position for fake events + * add any x,y override position */ - p->mval[0] = (float)event->mval[0] - x; - p->mval[1] = (float)event->mval[1] - y; + copy_v2fl_v2i(p->mval, event->mval); p->shift = event->shift; - /* verify direction for straight lines */ + /* verify direction for straight lines and guides */ if ((is_speed_guide) || ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { if (p->straight == 0) { @@ -3099,12 +2733,12 @@ static void gpencil_draw_apply_event(bContext *C, p->flags &= ~GP_PAINTFLAG_FIRSTRUN; /* set values */ - copy_v2_v2(p->mvalo, p->mval); p->opressure = p->pressure; p->inittime = p->ocurtime = p->curtime; p->straight = 0; /* save initial mouse */ + copy_v2_v2(p->mvalo, p->mval); copy_v2_v2(p->mvali, p->mval); if (is_speed_guide && !ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) && @@ -3120,7 +2754,7 @@ static void gpencil_draw_apply_event(bContext *C, } /* wait for vector then add initial point */ - if (is_speed_guide && p->flags & GP_PAINTFLAG_REQ_VECTOR) { + if (is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR)) { if (p->straight == 0) { return; } @@ -3154,23 +2788,13 @@ static void gpencil_draw_apply_event(bContext *C, p->mvali, (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f); } - - /* create fake events */ - float tmp[2]; - copy_v2_v2(tmp, p->mval); - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false); - if (len_v2v2(p->mval, p->mvalo)) { - sub_v2_v2v2(pt, p->mval, p->mvalo); - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false); - } - copy_v2_v2(p->mval, tmp); } /* check if stroke is straight or guided */ if ((p->paintmode != GP_PAINTMODE_ERASER) && ((p->straight) || (is_speed_guide))) { /* guided stroke */ if (is_speed_guide) { - gpencil_speed_guide(p, guide); + gpencil_snap_to_guide(p, guide, p->mval); } else if (p->straight == STROKE_HORIZONTAL) { p->mval[1] = p->mvali[1]; /* replace y */ @@ -3192,7 +2816,7 @@ static void gpencil_draw_apply_event(bContext *C, RNA_float_set(&itemptr, "time", p->curtime - p->inittime); /* apply the current latest drawing point */ - gpencil_draw_apply(C, op, p, depsgraph, is_fake); + gpencil_draw_apply(C, op, p, depsgraph); /* force refresh */ /* just active area for now, since doing whole screen is too slow */ @@ -3207,28 +2831,21 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) tGPsdata *p = NULL; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - /* printf("GPencil - Starting Re-Drawing\n"); */ - /* try to initialize context data needed while drawing */ if (!gpencil_draw_init(C, op, NULL)) { MEM_SAFE_FREE(op->customdata); - /* printf("\tGP - no valid data\n"); */ return OPERATOR_CANCELLED; } else { p = op->customdata; } - /* printf("\tGP - Start redrawing stroke\n"); */ - /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), * setting the relevant values in context at each step, then applying */ RNA_BEGIN (op->ptr, itemptr, "stroke") { float mousef[2]; - /* printf("\t\tGP - stroke elem\n"); */ - /* get relevant data for this point from stroke */ RNA_float_get_array(&itemptr, "mouse", mousef); p->mval[0] = mousef[0]; @@ -3258,12 +2875,10 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) } /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(C, op, p, depsgraph, false); + gpencil_draw_apply(C, op, p, depsgraph); } RNA_END; - /* printf("\tGP - done\n"); */ - /* cleanup */ gpencil_draw_exit(C, op); @@ -3400,9 +3015,9 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event else { /* don't erase empty frames */ bool has_layer_to_erase = false; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Skip if layer not editable */ - if (gpencil_layer_is_editable(gpl)) { + if (BKE_gpencil_layer_is_editable(gpl)) { if (gpl->actframe && gpl->actframe->strokes.first) { has_layer_to_erase = true; break; @@ -3430,35 +3045,25 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event } /* TODO: set any additional settings that we can take from the events? - * TODO? if tablet is erasing, force eraser to be on? */ - - /* if eraser is on, draw radial aid */ + * if eraser is on, draw radial aid */ if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); } else { ED_gpencil_toggle_brush_cursor(C, true, NULL); } - /* set cursor - * NOTE: This may change later (i.e. intentionally via brush toggle, - * or unintentionally if the user scrolls outside the area)... - */ - gpencil_draw_cursor_set(p); /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ - /* printf("\tGP - set first spot\n"); */ p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event( - C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f, false); + gpencil_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C)); op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { /* toolbar invoked - don't start drawing yet... */ - /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ op->flag |= OP_IS_MODAL_CURSOR_REGION; } @@ -3510,12 +3115,7 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) p->status = GP_STATUS_ERROR; } - /* printf("\t\tGP - start stroke\n"); */ - /* we may need to set up paint env again if we're resuming */ - /* XXX: watch it with the paintmode! in future, - * it'd be nice to allow changing paint-mode when in sketching-sessions */ - if (gp_session_initdata(C, op, p)) { gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph_pointer(C)); } @@ -3528,134 +3128,222 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) return op->customdata; } -static void gpencil_stroke_end(wmOperator *op) +/* Add arc points between two mouse events using the previous segment to determine the vertice of + * the arc. + * /+ CTL + * / | + * / | + * PtA +...|...+ PtB + * / + * / + * + PtA - 1 + * / + * CTL is the vertice of the triangle created between PtA and PtB */ +static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) { - tGPsdata *p = op->customdata; + bGPdata *gpd = p->gpd; + if (gpd->runtime.sbuffer_used < 3) { + return; + } + + int idx_prev = gpd->runtime.sbuffer_used; + + /* Add space for new arc points. */ + gpd->runtime.sbuffer_used += segments - 1; + + /* Check if still room in buffer or add more. */ + gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure( + gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false); + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + tGPspoint *pt = NULL; + tGPspoint *pt_before = &points[idx_prev - 1]; /* current - 2 */ + tGPspoint *pt_prev = &points[idx_prev - 2]; /* previous */ + + /* Create two vectors, previous and half way of the actual to get the vertex of the triangle + * for arc curve. + */ + float v_prev[2], v_cur[2], v_half[2]; + sub_v2_v2v2(v_cur, mval, &pt_prev->x); + + sub_v2_v2v2(v_prev, &pt_prev->x, &pt_before->x); + interp_v2_v2v2(v_half, &pt_prev->x, mval, 0.5f); + sub_v2_v2(v_half, &pt_prev->x); + + /* If angle is too sharp undo all changes and return. */ + const float min_angle = DEG2RADF(120.0f); + float angle = angle_v2v2(v_prev, v_half); + if (angle < min_angle) { + gpd->runtime.sbuffer_used -= segments - 1; + return; + } + + /* Project the half vector to the previous vector and calculate the mid projected point. */ + float dot = dot_v2v2(v_prev, v_half); + float l = len_squared_v2(v_prev); + if (l > 0.0f) { + mul_v2_fl(v_prev, dot / l); + } - gp_paint_cleanup(p); + /* Calc the position of the control point. */ + float ctl[2]; + add_v2_v2v2(ctl, &pt_prev->x, v_prev); - gpencil_undo_push(p->gpd); + float step = M_PI_2 / (float)(segments + 1); + float a = step; - gp_session_cleanup(p); + float midpoint[2], start[2], end[2], cp1[2], corner[2]; + mid_v2_v2v2(midpoint, &pt_prev->x, mval); + copy_v2_v2(start, &pt_prev->x); + copy_v2_v2(end, mval); + copy_v2_v2(cp1, ctl); - p->status = GP_STATUS_IDLING; - op->flag |= OP_IS_MODAL_CURSOR_REGION; + corner[0] = midpoint[0] - (cp1[0] - midpoint[0]); + corner[1] = midpoint[1] - (cp1[1] - midpoint[1]); - p->gpd = NULL; - p->gpl = NULL; - p->gpf = NULL; + for (int i = 0; i < segments; i++) { + pt = &points[idx_prev + i - 1]; + pt->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a); + pt->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a); + + /* Set pressure and strength equals to previous. It will be smoothed later. */ + pt->pressure = pt_prev->pressure; + pt->strength = pt_prev->strength; + + a += step; + } } -/* Move last stroke in the listbase to the head - * to be drawn below all previous strokes in the layer. */ -static void gpencil_move_last_stroke_to_back(bContext *C) +static void gpencil_add_guide_points(const tGPsdata *p, + const GP_Sculpt_Guide *guide, + const float start[2], + const float end[2], + int segments) { - /* Move last stroke (the polygon) to head of the listbase stroke - * to draw on back of all previous strokes. */ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + bGPdata *gpd = p->gpd; + if ((gpd->runtime.sbuffer_used == 0)) { return; } - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps = gpf->strokes.last; - if (ELEM(NULL, gps)) { - return; + int idx_prev = gpd->runtime.sbuffer_used; + + /* Add space for new points. */ + gpd->runtime.sbuffer_used += segments - 1; + + /* Check if still room in buffer or add more. */ + gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure( + gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false); + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + tGPspoint *pt = NULL; + tGPspoint *pt_before = &points[idx_prev - 1]; + + /* Use arc sampling for circular guide */ + if (guide->type == GP_GUIDE_CIRCULAR) { + float cw = cross_tri_v2(start, p->guide.origin, end); + float angle = angle_v2v2v2(start, p->guide.origin, end); + + float step = angle / (float)(segments + 1); + if (cw < 0.0f) { + step = -step; + } + + float a = step; + + for (int i = 0; i < segments; i++) { + pt = &points[idx_prev + i - 1]; + + gp_rotate_v2_v2v2fl(&pt->x, start, p->guide.origin, -a); + gpencil_snap_to_guide(p, guide, &pt->x); + a += step; + + /* Set pressure and strength equals to previous. It will be smoothed later. */ + pt->pressure = pt_before->pressure; + pt->strength = pt_before->strength; + } } + else { + float step = 1.0f / (float)(segments + 1); + float a = step; + + for (int i = 0; i < segments; i++) { + pt = &points[idx_prev + i - 1]; + + interp_v2_v2v2(&pt->x, start, end, a); + gpencil_snap_to_guide(p, guide, &pt->x); + a += step; - BLI_remlink(&gpf->strokes, gps); - BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); + /* Set pressure and strength equals to previous. It will be smoothed later. */ + pt->pressure = pt_before->pressure; + pt->strength = pt_before->strength; + } + } } -/* Add fake events for missing mouse movements when the artist draw very fast */ -static bool gpencil_add_fake_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +/* Add fake points for missing mouse movements when the artist draw very fast creating an arc + * with the vertice in the midle of the segment and using the angle of the previous segment. */ +static void gpencil_add_fake_points(const wmEvent *event, tGPsdata *p) { Brush *brush = p->brush; + /* Lazy mode do not use fake events. */ + if (GPENCIL_LAZY_MODE(brush, p->shift)) { + return; + } + GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); int input_samples = brush->gpencil_settings->input_samples; - bool added_events = false; - /* ensure sampling when using circular guide */ - if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) { + bool is_speed_guide = ((guide->use_guide) && + (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); + + /* TODO: ensure sampling enough points when using circular guide, + but the arc must be around the center. (see if above to check other guides only) + */ + if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) { input_samples = GP_MAX_INPUT_SAMPLES; } if (input_samples == 0) { - return added_events; + return; } - RegionView3D *rv3d = p->region->regiondata; - float defaultpixsize = rv3d->pixsize * 1000.0f; - int samples = (GP_MAX_INPUT_SAMPLES - input_samples + 1); - float thickness = (float)brush->size; + int samples = GP_MAX_INPUT_SAMPLES - input_samples + 1; - float pt[2], a[2], b[2]; - float vec[3]; - float scale = 1.0f; + float mouse_prv[2], mouse_cur[2]; + float min_dist = 4.0f * samples; - /* get pixel scale */ - gp_get_3d_reference(p, vec); - mul_m4_v3(rv3d->persmat, vec); - if (rv3d->is_persp) { - scale = vec[2] * defaultpixsize; - } - else { - scale = defaultpixsize; - } + copy_v2_v2(mouse_prv, p->mvalo); + copy_v2fl_v2i(mouse_cur, event->mval); - /* The thickness of the brush is reduced of thickness to get overlap dots */ - float dot_factor = 0.50f; - if (samples < 2) { - dot_factor = 0.05f; - } - else if (samples < 4) { - dot_factor = 0.10f; - } - else if (samples < 7) { - dot_factor = 0.3f; - } - else if (samples < 10) { - dot_factor = 0.4f; + /* get distance in pixels */ + float dist = len_v2v2(mouse_prv, mouse_cur); + + /* get distance for circular guide */ + if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) { + float middle[2]; + gpencil_snap_to_guide(p, guide, mouse_prv); + gpencil_snap_to_guide(p, guide, mouse_cur); + mid_v2_v2v2(middle, mouse_cur, mouse_prv); + gpencil_snap_to_guide(p, guide, middle); + dist = len_v2v2(mouse_prv, middle) + len_v2v2(middle, mouse_cur); } - float factor = ((thickness * dot_factor) / scale) * samples; - copy_v2_v2(a, p->mvalo); - b[0] = (float)event->mval[0] + 1.0f; - b[1] = (float)event->mval[1] + 1.0f; + if ((dist > 3.0f) && (dist > min_dist)) { + int slices = (dist / min_dist) + 1; - /* get distance in pixels */ - float dist = len_v2v2(a, b); - - /* for very small distances, add a half way point */ - if (dist <= 2.0f) { - interp_v2_v2v2(pt, a, b, 0.5f); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true); - added_events = true; - } - else if (dist >= factor) { - int slices = 2 + (int)((dist - 1.0) / factor); - float n = 1.0f / slices; - for (int i = 1; i < slices; i++) { - interp_v2_v2v2(pt, a, b, n * i); - sub_v2_v2v2(pt, b, pt); - /* create fake event */ - gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], true); - added_events = true; - } - } - return added_events; + if (is_speed_guide) { + gpencil_add_guide_points(p, guide, mouse_prv, mouse_cur, slices); + } + else { + gpencil_add_arc_points(p, mouse_cur, slices); + } + } } /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p = op->customdata; - ToolSettings *ts = CTX_data_tool_settings(C); + // ToolSettings *ts = CTX_data_tool_settings(C); GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; /* default exit state - pass through to support MMB view nav, etc. */ @@ -3754,98 +3442,26 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } - // printf("\tGP - handle modal event...\n"); - /* Exit painting mode (and/or end current stroke). * - * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) - * as that would break polyline T32647. */ - /* if polyline and release shift must cancel */ - if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || - ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) { - /* exit() ends the current stroke before cleaning up */ - /* printf("\t\tGP - end of paint op + end of stroke\n"); */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && - (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } /* toggle painting mode upon mouse-button movement - * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox - * only) - * - RIGHTMOUSE = polyline (hotkey) / eraser (all) + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) + * - RIGHTMOUSE = eraser (all) * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) * also making sure we have a valid event value, to not exit too early */ if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { /* if painting, end stroke */ if (p->status == GP_STATUS_PAINTING) { - int sketch = 0; - - /* basically, this should be mouse-button up = end stroke - * BUT, polyline drawing is an exception -- all knots should be added during one session - */ - sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); - - if (sketch) { - /* end stroke only, and then wait to resume painting soon */ - /* printf("\t\tGP - end stroke only\n"); */ - gpencil_stroke_end(op); - - /* If eraser mode is on, turn it off after the stroke finishes - * NOTE: This just makes it nicer to work with drawing sessions - */ - if (p->paintmode == GP_PAINTMODE_ERASER) { - p->paintmode = RNA_enum_get(op->ptr, "mode"); - - /* if the original mode was *still* eraser, - * we'll let it say for now, since this gives - * users an opportunity to have visual feedback - * when adjusting eraser size - */ - if (p->paintmode != GP_PAINTMODE_ERASER) { - /* turn off cursor... - * NOTE: this should be enough for now - * Just hiding this makes it seem like - * you can paint again... - */ - gpencil_draw_toggle_eraser_cursor(C, p, false); - } - } - - /* we've just entered idling state, so this event was processed (but no others yet) */ - estate = OPERATOR_RUNNING_MODAL; - - /* stroke could be smoothed, send notifier to refresh screen */ - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - } - else { - /* printf("\t\tGP - end of stroke + op\n"); */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && - (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } - /* drawing batch cache is dirty now */ - gp_update_cache(p->gpd); - - p->status = GP_STATUS_DONE; - estate = OPERATOR_FINISHED; - } + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; } else if (event->val == KM_PRESS) { bool in_bounds = false; @@ -3929,15 +3545,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * NOTE: Don't enter this case if an error occurred while finding the * region (as above) */ - /* if drawing polygon and enable on back, must move stroke */ - if (ts) { - if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && - (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { - if (p->flags & GP_PAINTFLAG_STROKEADDED) { - gpencil_move_last_stroke_to_back(C); - } - } - } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -3954,25 +3561,24 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* handle painting mouse-movements? */ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { /* handle drawing event */ - /* printf("\t\tGP - add point\n"); */ + bool is_speed_guide = ((guide->use_guide) && + (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW))); int size_before = p->gpd->runtime.sbuffer_used; - bool added_events = false; - if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER)) { - added_events = gpencil_add_fake_events(C, op, event, p); + if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER) && + !(is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR))) { + gpencil_add_fake_points(event, p); } - gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C), 0.0f, 0.0f, false); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph_pointer(C)); int size_after = p->gpd->runtime.sbuffer_used; - /* Last point of the event is always real (not fake). */ - tGPspoint *points = (tGPspoint *)p->gpd->runtime.sbuffer; - tGPspoint *pt = &points[size_after - 1]; - pt->tflag &= ~GP_TPOINT_FAKE; - - /* Smooth the fake events to get smoother strokes, specially at ends. */ - if (added_events) { - gp_smooth_fake_events(p, size_before, size_after); + /* Smooth segments if some fake points were added (need loop to get cumulative smooth). + * the 0.15 value gets a good result in Windows and Linux. */ + if (!is_speed_guide && (size_after - size_before > 1)) { + for (int r = 0; r < 5; r++) { + gp_smooth_segment(p->gpd, 0.15f, size_before - 1, size_after - 1); + } } /* finish painting operation if anything went wrong just now */ @@ -3982,17 +3588,13 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else { /* event handled, so just tag as running modal */ - /* printf("\t\t\t\tGP - add point handled!\n"); */ estate = OPERATOR_RUNNING_MODAL; } } /* eraser size */ else if ((p->paintmode == GP_PAINTMODE_ERASER) && ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) { - /* just resize the brush (local version) - * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys - */ - /* printf("\t\tGP - resize eraser\n"); */ + /* Just resize the brush (local version). */ switch (event->type) { case WHEELDOWNMOUSE: /* larger */ case PADPLUSKEY: @@ -4032,7 +3634,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) else { /* update status indicators - cursor, header, etc. */ gpencil_draw_status_indicators(C, p); - gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ } /* process last operations before exiting */ @@ -4056,12 +3657,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: /* event doesn't need to be handled */ -#if 0 - printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", - event->type, - event->type == MIDDLEMOUSE, - event->type == MOUSEMOVE); -#endif break; } @@ -4078,11 +3673,6 @@ static const EnumPropertyItem prop_gpencil_drawmodes[] = { 0, "Draw Straight Lines", "Draw straight line segment(s)"}, - {GP_PAINTMODE_DRAW_POLY, - "DRAW_POLY", - 0, - "Draw Poly Line", - "Click to place endpoints of straight line segments (connected)"}, {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Grease Pencil strokes"}, {0, NULL, 0, NULL, NULL}, }; @@ -4094,7 +3684,7 @@ void GPENCIL_OT_draw(wmOperatorType *ot) /* identifiers */ ot->name = "Grease Pencil Draw"; ot->idname = "GPENCIL_OT_draw"; - ot->description = "Draw a new stroke in the active Grease Pencil Object"; + ot->description = "Draw mouse_prv new stroke in the active Grease Pencil Object"; /* api callbacks */ ot->exec = gpencil_draw_exec; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index ad54382dde7..96522d1750c 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -122,6 +122,10 @@ static void gp_session_validatebuffer(tGPDprimitive *p) gpd->runtime.sbuffer_sflag = 0; gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + /* Set vertex colors for buffer. */ + ED_gpencil_sbuffer_vertex_color_set( + p->depsgraph, p->ob, p->scene->toolsettings, p->brush, p->material); + if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC; } @@ -132,34 +136,11 @@ static void gp_init_colors(tGPDprimitive *p) bGPdata *gpd = p->gpd; Brush *brush = p->brush; - MaterialGPencilStyle *gp_style = NULL; - /* use brush material */ - p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); - - /* assign color information to temp data */ - gp_style = p->mat->gp_style; - if (gp_style) { - - /* set colors */ - if (gp_style->flag & GP_STYLE_STROKE_SHOW) { - copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); - } - else { - /* if no stroke, use fill */ - copy_v4_v4(gpd->runtime.scolor, gp_style->fill_rgba); - } - - copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba); - /* add some alpha to make easy the filling without hide strokes */ - if (gpd->runtime.sfill[3] > 0.8f) { - gpd->runtime.sfill[3] = 0.8f; - } + p->material = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush); - gpd->runtime.mode = (short)gp_style->mode; - gpd->runtime.bstroke_style = gp_style->stroke_style; - gpd->runtime.bfill_style = gp_style->fill_style; - } + gpd->runtime.matid = BKE_object_material_slot_find_index(p->ob, p->material); + gpd->runtime.sbuffer_brush = brush; } /* Helper to square a primitive */ @@ -254,18 +235,6 @@ static void gp_primitive_update_cps(tGPDprimitive *tgpi) } } -/* Helper to reflect point */ -static void UNUSED_FUNCTION(gp_reflect_point_v2_v2v2v2)(float va[2], - const float p[2], - const float a[2], - const float b[2]) -{ - float point[2]; - closest_to_line_v2(point, p, a, b); - va[0] = point[0] - (p[0] - point[0]); - va[1] = point[1] - (p[1] - point[1]); -} - /* Poll callback for primitive operators */ static bool gpencil_primitive_add_poll(bContext *C) { @@ -294,7 +263,7 @@ static bool gpencil_primitive_add_poll(bContext *C) /* don't allow operator to function if the active layer is locked/hidden * (BUT, if there isn't an active layer, we are free to add new layer when the time comes) */ - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) { CTX_wm_operator_poll_msg_set(C, "Primitives cannot be added as active layer is locked or hidden"); @@ -322,6 +291,8 @@ static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi) static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) { Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + Brush *brush = tgpi->brush; int cfra = CFRA; bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); @@ -339,13 +310,15 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* create new temp stroke */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke"); gps->thickness = 2.0f; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; + gps->fill_opacity_fac = 1.0f; + gps->hardeness = 1.0f; + copy_v2_fl(gps->aspect_ratio, 1.0f); + gps->uv_scale = 1.0f; gps->inittime = 0.0f; - /* enable recalculation flag by default */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + /* Apply the vertex color to fill. */ + ED_gpencil_fill_vertex_color_set(ts, brush, gps); + gps->flag &= ~GP_STROKE_SELECT; /* the polygon must be closed, so enabled cyclic */ if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) { @@ -367,10 +340,11 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* allocate memory for storage points, but keep empty */ gps->totpoints = 0; gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points"); + gps->dvert = NULL; + /* initialize triangle memory to dummy data */ gps->tot_triangles = 0; gps->triangles = NULL; - gps->flag |= GP_STROKE_RECALC_GEOMETRY; /* add to strokes */ BLI_addtail(&tgpi->gpf->strokes, gps); @@ -381,6 +355,8 @@ static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* Random generator, only init once. */ uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); tgpi->rng = BLI_rng_new(rng_seed); + + DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE); } /* add new segment to curve */ @@ -898,7 +874,6 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) { jitter = BKE_curvemapping_evaluateF( brush->gpencil_settings->curve_jitter, 0, curve_pressure); - jitter *= brush->gpencil_settings->draw_sensitivity; } else { jitter = brush->gpencil_settings->draw_jitter; @@ -934,10 +909,10 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->draw_random_press > 0.0f)) { if (p2d->rnd[0] > 0.5f) { - pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[1]; + pressure -= (brush->gpencil_settings->draw_random_press * 2.0f) * p2d->rnd[1]; } else { - pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[2]; + pressure += (brush->gpencil_settings->draw_random_press * 2.0f) * p2d->rnd[2]; } } @@ -945,7 +920,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { float curvef = BKE_curvemapping_evaluateF( brush->gpencil_settings->curve_strength, 0, curve_pressure); - strength *= curvef * brush->gpencil_settings->draw_sensitivity; + strength *= curvef; strength *= brush->gpencil_settings->draw_strength; } @@ -973,14 +948,13 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* point uv */ if (gpd->runtime.sbuffer_used > 0) { - MaterialGPencilStyle *gp_style = tgpi->mat->gp_style; - const float pixsize = gp_style->texture_pixsize / 1000000.0f; tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; bGPDspoint spt, spt2; /* get origin to reproject point */ float origin[3]; - ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); + ED_gpencil_drawing_reference_get( + tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); /* reproject current */ ED_gpencil_tpoint_to_point(tgpi->region, origin, tpt, &spt); ED_gp_project_point_to_plane( @@ -990,11 +964,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) ED_gpencil_tpoint_to_point(tgpi->region, origin, tptb, &spt2); ED_gp_project_point_to_plane( tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2); - tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x); tpt->uv_fac = tgpi->totpixlen; - if ((gp_style) && (gp_style->sima)) { - tpt->uv_fac /= gp_style->sima->gen_x; - } } else { tgpi->totpixlen = 0.0f; @@ -1028,6 +999,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) pt->time = 0.0f; pt->flag = 0; pt->uv_fac = tpt->uv_fac; + /* Apply the vertex color to point. */ + ED_gpencil_point_vertex_color_set(ts, brush, pt); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -1052,7 +1025,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* reproject to plane */ if (!is_depth) { float origin[3]; - ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); + ED_gpencil_drawing_reference_get( + tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin); ED_gp_project_stroke_to_plane( tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1); } @@ -1060,7 +1034,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) /* if parented change position relative to parent object */ for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; - gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt); + gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpl, pt); } /* if camera view, reproject flat to view to avoid perspective effect */ @@ -1068,8 +1042,11 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) ED_gpencil_project_stroke_to_view(C, tgpi->gpl, gps); } - /* force fill recalc */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + + /* Update evaluated data. */ + ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval); MEM_SAFE_FREE(depth_arr); @@ -1162,13 +1139,14 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* set current scene and window info */ tgpi->bmain = CTX_data_main(C); + tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); tgpi->scene = scene; tgpi->ob = CTX_data_active_object(C); + tgpi->ob_eval = (Object *)DEG_get_evaluated_object(tgpi->depsgraph, tgpi->ob); tgpi->sa = CTX_wm_area(C); tgpi->region = CTX_wm_region(C); tgpi->rv3d = tgpi->region->regiondata; tgpi->v3d = tgpi->sa->spacedata.first; - tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); tgpi->win = CTX_wm_window(C); /* save original type */ @@ -1184,7 +1162,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) /* if brush doesn't exist, create a new set (fix damaged files from old versions) */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { - BKE_brush_gpencil_presets(bmain, ts); + BKE_brush_gpencil_paint_presets(bmain, ts); } /* Set Draw brush. */ @@ -1199,7 +1177,7 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) tgpi->gpd->runtime.tot_cp_points = 0; /* getcolor info */ - tgpi->mat = BKE_gpencil_object_material_ensure_from_active_input_toolsettings( + tgpi->material = BKE_gpencil_object_material_ensure_from_active_input_toolsettings( bmain, tgpi->ob, ts); /* set parameters */ @@ -1309,20 +1287,17 @@ static void gpencil_primitive_interaction_end(bContext *C, add_frame_mode = GP_GETFRAME_ADD_NEW; } - gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode); + gpf = BKE_gpencil_layer_frame_get(tgpi->gpl, tgpi->cframe, add_frame_mode); /* prepare stroke to get transferred */ gps = tgpi->gpf->strokes.first; if (gps) { gps->thickness = brush->size; - gps->gradient_f = brush->gpencil_settings->gradient_f; - copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s); - - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; + gps->hardeness = brush->gpencil_settings->hardeness; + copy_v2_v2(gps->aspect_ratio, brush->gpencil_settings->aspect_ratio); - /* calculate UVs along the stroke */ - ED_gpencil_calc_stroke_uv(tgpi->ob, gps); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* transfer stroke from temporary buffer to the actual frame */ @@ -1348,7 +1323,7 @@ static void gpencil_primitive_interaction_end(bContext *C, /* Close stroke with geometry */ if ((tgpi->type == GP_STROKE_BOX) || (tgpi->type == GP_STROKE_CIRCLE)) { - BKE_gpencil_close_stroke(gps); + BKE_gpencil_stroke_close(gps); } DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c new file mode 100644 index 00000000000..5cf4681c755 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -0,0 +1,2150 @@ +/* + * 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) 2015, Blender Foundation + * This is a new part of Blender + * Brush based operators for editing Grease Pencil strokes + */ + +/** \file + * \ingroup edgpencil + */ + +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object_deform.h" +#include "BKE_report.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +/* ************************************************ */ +/* General Brush Editing Context */ + +/* Context for brush operators */ +typedef struct tGP_BrushEditData { + /* Current editor/region/etc. */ + Depsgraph *depsgraph; + Main *bmain; + Scene *scene; + Object *object; + + ScrArea *sa; + ARegion *region; + + /* Current GPencil datablock */ + bGPdata *gpd; + + /* Brush Settings */ + GP_Sculpt_Settings *settings; + Brush *brush; + Brush *brush_prev; + + eGP_Sculpt_Flag flag; + eGP_Sculpt_SelectMaskFlag mask; + + /* Space Conversion Data */ + GP_SpaceConversion gsc; + + /* Is the brush currently painting? */ + bool is_painting; + bool is_transformed; + + /* Start of new sculpt stroke */ + bool first; + + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + + /* Current frame */ + int cfra; + + /* Brush Runtime Data: */ + /* - position and pressure + * - the *_prev variants are the previous values + */ + float mval[2], mval_prev[2]; + float pressure, pressure_prev; + + /* - effect vector (e.g. 2D/3D translation for grab brush) */ + float dvec[3]; + + /* rotation for evaluated data */ + float rot_eval; + + /* - multiframe falloff factor */ + float mf_falloff; + + /* active vertex group */ + int vrgroup; + + /* brush geometry (bounding box) */ + rcti brush_rect; + + /* Custom data for certain brushes */ + /* - map from bGPDstroke's to structs containing custom data about those strokes */ + GHash *stroke_customdata; + /* - general customdata */ + void *customdata; + + /* Timer for in-place accumulation of brush effect */ + wmTimer *timer; + bool timerTick; /* is this event from a timer */ + + /* Object invert matrix */ + float inv_mat[4][4]; + + RNG *rng; +} tGP_BrushEditData; + +/* Callback for performing some brush operation on a single point */ +typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, + bGPDstroke *gps, + float rotation, + int pt_index, + const int radius, + const int co[2]); + +/* ************************************************ */ +/* Utility Functions */ + +/* apply lock axis reset */ +static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, + bGPDspoint *pt, + const float save_pt[3]) +{ + const ToolSettings *ts = gso->scene->toolsettings; + const View3DCursor *cursor = &gso->scene->cursor; + const int axis = ts->gp_sculpt.lock_axis; + + /* lock axis control */ + switch (axis) { + case GP_LOCKAXIS_X: { + pt->x = save_pt[0]; + break; + } + case GP_LOCKAXIS_Y: { + pt->y = save_pt[1]; + break; + } + case GP_LOCKAXIS_Z: { + pt->z = save_pt[2]; + break; + } + case GP_LOCKAXIS_CURSOR: { + /* Compute a plane with cursor normal and position of the point before do the sculpt. */ + const float scale[3] = {1.0f, 1.0f, 1.0f}; + float plane_normal[3] = {0.0f, 0.0f, 1.0f}; + float plane[4]; + float mat[4][4]; + float r_close[3]; + + loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale); + + mul_mat3_m4_v3(mat, plane_normal); + plane_from_point_normal_v3(plane, save_pt, plane_normal); + + /* find closest point to the plane with the new position */ + closest_to_plane_v3(r_close, plane, &pt->x); + copy_v3_v3(&pt->x, r_close); + break; + } + default: { + break; + } + } +} + +/* Context ---------------------------------------- */ + +/* Get the sculpting settings */ +static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene) +{ + return &scene->toolsettings->gp_sculpt; +} + +/* Brush Operations ------------------------------- */ + +/* Invert behavior of brush? */ +static bool gp_brush_invert_check(tGP_BrushEditData *gso) +{ + /* The basic setting is the brush's setting (from the panel) */ + bool invert = ((gso->brush->gpencil_settings->sculpt_flag & GP_SCULPT_FLAG_INVERT) != 0); + + /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ + if (gso->flag & GP_SCULPT_FLAG_INVERT) { + invert ^= true; + } + + /* set temporary status */ + if (invert) { + gso->brush->gpencil_settings->sculpt_flag |= GP_SCULPT_FLAG_TMP_INVERT; + } + else { + gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + } + + return invert; +} + +/* Compute strength of effect */ +static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2]) +{ + Brush *brush = gso->brush; + + /* basic strength factor from brush settings */ + float influence = brush->alpha; + + /* use pressure? */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + influence *= gso->pressure; + } + + /* distance fading */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, co); + + /* Apply Brush curve. */ + float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius); + influence *= brush_fallof; + + /* apply multiframe falloff */ + influence *= gso->mf_falloff; + + /* return influence */ + return influence; +} + +/* Tag stroke to be recalculated. */ +static void gpencil_recalc_geometry_tag(bGPDstroke *gps) +{ + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + gps_active->flag |= GP_STROKE_TAG; +} + +/* Recalc any stroke tagged. */ +static void gpencil_update_geometry(bGPdata *gpd) +{ + if (gpd == NULL) { + return; + } + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + if ((gpl->actframe != gpf) && ((gpf->flag & GP_FRAME_SELECT) == 0)) { + continue; + } + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_TAG) { + BKE_gpencil_stroke_geometry_update(gps); + gps->flag &= ~GP_STROKE_TAG; + } + } + } + } + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +/* ************************************************ */ +/* Brush Callbacks */ +/* This section defines the callbacks used by each brush to perform their magic. + * These are called on each point within the brush's radius. + */ + +/* ----------------------------------------------- */ +/* Smooth Brush */ + +/* A simple (but slower + inaccurate) + * smooth-brush implementation to test the algorithm for stroke smoothing. */ +static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + float inf = gp_brush_influence_calc(gso, radius, co); + + /* perform smoothing */ + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) { + BKE_gpencil_stroke_smooth(gps, pt_index, inf); + } + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) { + BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf); + } + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) { + BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf); + } + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) { + BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf); + } + + return true; +} + +/* ----------------------------------------------- */ +/* Line Thickness Brush */ + +/* Make lines thicker or thinner by the specified amounts */ +static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* apply */ + /* XXX: this is much too strong, + * and it should probably do some smoothing with the surrounding stuff. */ + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->pressure -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->pressure += inf; + } + + /* Pressure should stay within [0.0, 1.0] + * However, it is nice for volumetric strokes to be able to exceed + * the upper end of this range. Therefore, we don't actually clamp + * down on the upper end. + */ + if (pt->pressure < 0.0f) { + pt->pressure = 0.0f; + } + + return true; +} + +/* ----------------------------------------------- */ +/* Color Strength Brush */ + +/* Make color more or less transparent by the specified amounts */ +static bool gp_brush_strength_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float inf; + + /* Compute strength of effect + * - We divide the strength, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; + CLAMP_MIN(inf, 0.01f); + + /* apply */ + if (gp_brush_invert_check(gso)) { + /* make line more transparent - reduce alpha factor */ + pt->strength -= inf; + } + else { + /* make line more opaque - increase stroke strength */ + pt->strength += inf; + } + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + /* smooth the strength */ + BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf); + + return true; +} + +/* ----------------------------------------------- */ +/* Grab Brush */ + +/* Custom data per stroke for the Grab Brush + * + * This basically defines the strength of the effect for each + * affected stroke point that was within the initial range of + * the brush region. + */ +typedef struct tGPSB_Grab_StrokeData { + /* array of indices to corresponding points in the stroke */ + int *points; + /* array of influence weights for each of the included points */ + float *weights; + /* angles to calc transformation */ + float *rot_eval; + + /* capacity of the arrays */ + int capacity; + /* actual number of items currently stored */ + int size; +} tGPSB_Grab_StrokeData; + +/* initialise custom data for handling this stroke */ +static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) +{ + tGPSB_Grab_StrokeData *data = NULL; + + BLI_assert(gps->totpoints > 0); + + /* Check if there are buffers already (from a prior run) */ + if (BLI_ghash_haskey(gso->stroke_customdata, gps)) { + /* Ensure that the caches are empty + * - Since we reuse these between different strokes, we don't + * want the previous invocation's data polluting the arrays + */ + data = BLI_ghash_lookup(gso->stroke_customdata, gps); + BLI_assert(data != NULL); + + data->size = 0; /* minimum requirement - so that we can repopulate again */ + + memset(data->points, 0, sizeof(int) * data->capacity); + memset(data->weights, 0, sizeof(float) * data->capacity); + memset(data->rot_eval, 0, sizeof(float) * data->capacity); + } + else { + /* Create new instance */ + data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data"); + + data->capacity = gps->totpoints; + data->size = 0; + + data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices"); + data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights"); + data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Rotations"); + + /* hook up to the cache */ + BLI_ghash_insert(gso->stroke_customdata, gps, data); + } +} + +/* store references to stroke points in the initial stage */ +static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, + bGPDstroke *gps, + float rot_eval, + int pt_index, + const int radius, + const int co[2]) +{ + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + float inf = gp_brush_influence_calc(gso, radius, co); + + BLI_assert(data != NULL); + BLI_assert(data->size < data->capacity); + + /* insert this point into the set of affected points */ + data->points[data->size] = pt_index; + data->weights[data->size] = inf; + data->rot_eval[data->size] = rot_eval; + data->size++; + + /* done */ + return true; +} + +/* Compute effect vector for grab brush */ +static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) +{ + /* Convert mouse-movements to movement vector */ + RegionView3D *rv3d = gso->region->regiondata; + float *rvec = gso->object->loc; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + + /* convert from 2D screenspace to 3D... */ + mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* apply evaluated data transformation */ + if (gso->rot_eval != 0.0f) { + const float cval = cos(gso->rot_eval); + const float sval = sin(gso->rot_eval); + float r[2]; + r[0] = (mval_f[0] * cval) - (mval_f[1] * sval); + r[1] = (mval_f[0] * sval) + (mval_f[1] * cval); + copy_v2_v2(mval_f, r); + } + + ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac); +} + +/* Apply grab transform to all relevant points of the affected strokes */ +static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, + bGPDstroke *gps, + const float diff_mat[4][4]) +{ + tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); + /* If a new frame is created, could be impossible find the stroke. */ + if (data == NULL) { + return; + } + + int i; + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + + /* Apply dvec to all of the stored points */ + for (i = 0; i < data->size; i++) { + bGPDspoint *pt = &gps->points[data->points[i]]; + float delta[3] = {0.0f}; + + /* get evaluated transformation */ + gso->rot_eval = data->rot_eval[i]; + gp_brush_grab_calc_dvec(gso); + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + + float fpt[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3v3(&pt->x, fpt, delta); + /* undo transformation to the init parent position */ + mul_m4_v3(inverse_diff_mat, &pt->x); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } +} + +/* free customdata used for handling this stroke */ +static void gp_brush_grab_stroke_free(void *ptr) +{ + tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr; + + /* free arrays */ + MEM_SAFE_FREE(data->points); + MEM_SAFE_FREE(data->weights); + MEM_SAFE_FREE(data->rot_eval); + + /* ... and this item itself, since it was also allocated */ + MEM_freeN(data); +} + +/* ----------------------------------------------- */ +/* Push Brush */ +/* NOTE: Depends on gp_brush_grab_calc_dvec() */ +static bool gp_brush_push_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + float inf = gp_brush_influence_calc(gso, radius, co); + float delta[3] = {0.0f}; + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, inf); + + /* apply */ + mul_mat3_m4_v3(gso->inv_mat, delta); /* only rotation component */ + add_v3_v3(&pt->x, delta); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + + /* done */ + return true; +} + +/* ----------------------------------------------- */ +/* Pinch Brush */ +/* Compute reference midpoint for the brush - this is what we'll be moving towards */ +static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) +{ + /* Convert mouse position to 3D space + * See: gpencil_paint.c :: gp_stroke_convertcoords() + */ + RegionView3D *rv3d = gso->region->regiondata; + const float *rvec = gso->object->loc; + float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + + float mval_f[2]; + copy_v2_v2(mval_f, gso->mval); + float mval_prj[2]; + float dvec[3]; + + if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac); + sub_v3_v3v3(gso->dvec, rvec, dvec); + } + else { + zero_v3(gso->dvec); + } +} + +/* Shrink distance between midpoint and this point... */ +static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float fac, inf; + float vec[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Scale down standard influence value to get it more manageable... + * - No damping = Unmanageable at > 0.5 strength + * - Div 10 = Not enough effect + * - Div 5 = Happy medium... (by trial and error) + */ + inf = gp_brush_influence_calc(gso, radius, co) / 5.0f; + + /* 1) Make this point relative to the cursor/midpoint (dvec) */ + float fpt[3]; + mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); + sub_v3_v3v3(vec, fpt, gso->dvec); + + /* 2) Shrink the distance by pulling the point towards the midpoint + * (0.0 = at midpoint, 1 = at edge of brush region) + * OR + * Increase the distance (if inverting the brush action!) + */ + if (gp_brush_invert_check(gso)) { + /* Inflate (inverse) */ + fac = 1.0f + (inf * inf); /* squared to temper the effect... */ + } + else { + /* Shrink (default) */ + fac = 1.0f - (inf * inf); /* squared to temper the effect... */ + } + mul_v3_fl(vec, fac); + + /* 3) Translate back to original space, with the shrinkage applied */ + add_v3_v3v3(fpt, gso->dvec, vec); + mul_v3_m4v3(&pt->x, gso->object->imat, fpt); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + + /* done */ + return true; +} + +/* ----------------------------------------------- */ +/* Twist Brush - Rotate Around midpoint */ +/* Take the screenspace coordinates of the point, rotate this around the brush midpoint, + * convert the rotated point and convert it into "data" space + */ + +static bool gp_brush_twist_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float angle, inf; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Angle to rotate by */ + inf = gp_brush_influence_calc(gso, radius, co); + angle = DEG2RADF(1.0f) * inf; + + if (gp_brush_invert_check(gso)) { + /* invert angle that we rotate by */ + angle *= -1; + } + + /* Rotate in 2D or 3D space? */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* Perform rotation in 3D space... */ + RegionView3D *rv3d = gso->region->regiondata; + float rmat[3][3]; + float axis[3]; + float vec[3]; + + /* Compute rotation matrix - rotate around view vector by angle */ + negate_v3_v3(axis, rv3d->persinv[2]); + normalize_v3(axis); + + axis_angle_normalized_to_mat3(rmat, axis, angle); + + /* Rotate point */ + float fpt[3]; + mul_v3_m4v3(fpt, gso->object->obmat, &pt->x); + sub_v3_v3v3(vec, fpt, gso->dvec); /* make relative to center + * (center is stored in dvec) */ + mul_m3_v3(rmat, vec); + add_v3_v3v3(fpt, vec, gso->dvec); /* restore */ + mul_v3_m4v3(&pt->x, gso->object->imat, fpt); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + else { + const float axis[3] = {0.0f, 0.0f, 1.0f}; + float vec[3] = {0.0f}; + float rmat[3][3]; + + /* Express position of point relative to cursor, ready to rotate */ + // XXX: There is still some offset here, but it's close to working as expected... + vec[0] = (float)(co[0] - gso->mval[0]); + vec[1] = (float)(co[1] - gso->mval[1]); + + /* rotate point */ + axis_angle_normalized_to_mat3(rmat, axis, angle); + mul_m3_v3(rmat, vec); + + /* Convert back to screen-coordinates */ + vec[0] += (float)gso->mval[0]; + vec[1] += (float)gso->mval[1]; + + /* Map from screen-coordinates to final coordinate space */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d = gso->gsc.v2d; + UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y); + } + else { + // XXX + copy_v2_v2(&pt->x, vec); + } + } + + /* done */ + return true; +} + +/* ----------------------------------------------- */ +/* Randomize Brush */ +/* Apply some random jitter to the point */ +static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, + bGPDstroke *gps, + float UNUSED(rot_eval), + int pt_index, + const int radius, + const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + + /* Amount of jitter to apply depends on the distance of the point to the cursor, + * as well as the strength of the brush + */ + const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; + const float fac = BLI_rng_get_float(gso->rng) * inf; + + /* apply random to position */ + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * view-space as needed. + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_rng_get_float(gso->rng) > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + bool flip; + RegionView3D *rv3d = gso->region->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + } + } + } + /* apply random to strength */ + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); + } + /* apply random to thickness (use pressure) */ + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* apply random to UV (use pressure) */ + if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->uv_rot += fac; + } + else { + pt->uv_rot -= fac; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } + + /* done */ + return true; +} + +/* ************************************************ */ +/* Non Callback-Based Brushes */ +/* Clone Brush ------------------------------------- */ +/* How this brush currently works: + * - If this is start of the brush stroke, paste immediately under the cursor + * by placing the midpoint of the buffer strokes under the cursor now + * + * - Otherwise, in: + * "Stamp Mode" - Move the newly pasted strokes so that their center follows the cursor + * "Continuous" - Repeatedly just paste new copies for where the brush is now + */ + +/* Custom state data for clone brush */ +typedef struct tGPSB_CloneBrushData { + /* midpoint of the strokes on the clipboard */ + float buffer_midpoint[3]; + + /* number of strokes in the paste buffer (and/or to be created each time) */ + size_t totitems; + + /* for "stamp" mode, the currently pasted brushes */ + bGPDstroke **new_strokes; + + /** Mapping from colors referenced per stroke, to the new colors in the "pasted" strokes. */ + GHash *new_colors; +} tGPSB_CloneBrushData; + +/* Initialise "clone" brush data */ +static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) +{ + tGPSB_CloneBrushData *data; + bGPDstroke *gps; + + /* init custom data */ + gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData"); + + /* compute midpoint of strokes on clipboard */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + const float dfac = 1.0f / ((float)gps->totpoints); + float mid[3] = {0.0f}; + + bGPDspoint *pt; + int i; + + /* compute midpoint of this stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float co[3]; + + mul_v3_v3fl(co, &pt->x, dfac); + add_v3_v3(mid, co); + } + + /* combine this stroke's data with the main data */ + add_v3_v3(data->buffer_midpoint, mid); + data->totitems++; + } + } + + /* Divide the midpoint by the number of strokes, to finish averaging it */ + if (data->totitems > 1) { + mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems); + } + + /* Create a buffer for storing the current strokes */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, + "cloned strokes ptr array"); + } + + /* Init colormap for mapping between the pasted stroke's source color (names) + * and the final colors that will be used here instead. + */ + data->new_colors = gp_copybuf_validate_colormap(C); +} + +/* Free custom data used for "clone" brush */ +static void gp_brush_clone_free(tGP_BrushEditData *gso) +{ + tGPSB_CloneBrushData *data = gso->customdata; + + /* free strokes array */ + if (data->new_strokes) { + MEM_freeN(data->new_strokes); + data->new_strokes = NULL; + } + + /* free copybuf colormap */ + if (data->new_colors) { + BLI_ghash_free(data->new_colors, NULL, NULL); + data->new_colors = NULL; + } + + /* free the customdata itself */ + MEM_freeN(data); + gso->customdata = NULL; +} + +/* Create new copies of the strokes on the clipboard */ +static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) +{ + tGPSB_CloneBrushData *data = gso->customdata; + + Object *ob = gso->object; + bGPdata *gpd = (bGPdata *)ob->data; + Scene *scene = gso->scene; + bGPDstroke *gps; + + float delta[3]; + size_t strokes_added = 0; + + /* Compute amount to offset the points by */ + /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */ + + gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */ + sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint); + + /* Copy each stroke into the layer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + bGPDstroke *new_stroke; + bGPDspoint *pt; + int i; + + bGPDlayer *gpl = NULL; + /* Try to use original layer. */ + if (gps->runtime.tmp_layerinfo != NULL) { + gpl = BKE_gpencil_layer_named_get(gpd, gps->runtime.tmp_layerinfo); + } + + /* if not available, use active layer. */ + if (gpl == NULL) { + gpl = CTX_data_active_gpencil_layer(C); + } + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); + + /* Make a new stroke */ + new_stroke = BKE_gpencil_stroke_duplicate(gps, true); + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + + /* Fix color references */ + Material *ma = BLI_ghash_lookup(data->new_colors, POINTER_FROM_INT(new_stroke->mat_nr)); + new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma); + if (!ma || new_stroke->mat_nr < 0) { + new_stroke->mat_nr = 0; + } + /* Adjust all the stroke's points, so that the strokes + * get pasted relative to where the cursor is now + */ + for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) { + /* Rotate around center new position */ + mul_mat3_m4_v3(gso->object->obmat, &pt->x); /* only rotation component */ + + /* assume that the delta can just be applied, and then everything works */ + add_v3_v3(&pt->x, delta); + mul_m4_v3(gso->object->imat, &pt->x); + } + + /* Store ref for later */ + if ((data->new_strokes) && (strokes_added < data->totitems)) { + data->new_strokes[strokes_added] = new_stroke; + strokes_added++; + } + } + } +} + +/* Move newly-added strokes around - "Stamp" mode of the Clone brush */ +static void gp_brush_clone_adjust(tGP_BrushEditData *gso) +{ + tGPSB_CloneBrushData *data = gso->customdata; + size_t snum; + + /* Compute the amount of movement to apply (overwrites dvec) */ + gso->rot_eval = 0.0f; + gp_brush_grab_calc_dvec(gso); + + /* For each of the stored strokes, apply the offset to each point */ + /* NOTE: Again this assumes that in the 3D view, + * we only have 3d space and not screenspace strokes... */ + for (snum = 0; snum < data->totitems; snum++) { + bGPDstroke *gps = data->new_strokes[snum]; + bGPDspoint *pt; + int i; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + /* "Smudge" Effect falloff */ + float delta[3] = {0.0f}; + int sco[2] = {0}; + float influence; + + /* compute influence on point */ + gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]); + influence = gp_brush_influence_calc(gso, gso->brush->size, sco); + + /* adjust the amount of displacement to apply */ + mul_v3_v3fl(delta, gso->dvec, influence); + + /* apply */ + add_v3_v3(&pt->x, delta); + } + } +} + +/* Entrypoint for applying "clone" brush */ +static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) +{ + /* Which "mode" are we operating in? */ + if (gso->first) { + /* Create initial clones */ + gp_brush_clone_add(C, gso); + } + else { + /* Stamp or Continuous Mode */ + if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { + /* Stamp - Proceed to translate the newly added strokes */ + gp_brush_clone_adjust(gso); + } + else { + /* Continuous - Just keep pasting everytime we move */ + /* TODO: The spacing of repeat should be controlled using a + * "stepsize" or similar property? */ + gp_brush_clone_add(C, gso); + } + } + + return true; +} + +/* ************************************************ */ +/* Header Info for GPencil Sculpt */ + +static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) +{ + Brush *brush = gso->brush; + char str[UI_MAX_DRAW_STR] = ""; + + BLI_snprintf(str, + sizeof(str), + TIP_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit" + " | Ctrl to Invert Action | Wheel Up/Down for Size " + " | Shift-Wheel Up/Down for Strength"), + brush->id.name + 2); + + ED_workspace_status_text(C, str); +} + +/* ************************************************ */ +/* Grease Pencil Sculpting Operator */ + +/* Init/Exit ----------------------------------------------- */ + +static bool gpsculpt_brush_init(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + Object *ob = CTX_data_active_object(C); + + /* set the brush using the tool */ + tGP_BrushEditData *gso; + + /* setup operator data */ + gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); + op->customdata = gso; + + gso->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + gso->bmain = CTX_data_main(C); + /* store state */ + gso->settings = gpsculpt_get_settings(scene); + + /* Random generator, only init once. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= POINTER_AS_UINT(gso); + gso->rng = BLI_rng_new(rng_seed); + + gso->is_painting = false; + gso->first = true; + + gso->gpd = ED_gpencil_data_get_active(C); + gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ + + gso->scene = scene; + gso->object = ob; + if (ob) { + invert_m4_m4(gso->inv_mat, ob->obmat); + gso->vrgroup = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = -1; + } + /* Check if some modifier can transform the stroke. */ + gso->is_transformed = BKE_gpencil_has_transform_modifiers(ob); + } + else { + unit_m4(gso->inv_mat); + gso->vrgroup = -1; + gso->is_transformed = false; + } + + gso->sa = CTX_wm_area(C); + gso->region = CTX_wm_region(C); + + Paint *paint = &ts->gp_sculptpaint->paint; + gso->brush = paint->brush; + BKE_curvemapping_initialize(gso->brush->curve); + + /* save mask */ + gso->mask = ts->gpencil_selectmode_sculpt; + + /* multiframe settings */ + gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + + /* Init multi-edit falloff curve data before doing anything, + * so we won't have to do it again later. */ + if (gso->is_multiframe) { + BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + + /* initialise custom data for brushes */ + char tool = gso->brush->gpencil_sculpt_tool; + switch (tool) { + case GPSCULPT_TOOL_CLONE: { + bGPDstroke *gps; + bool found = false; + + /* check that there are some usable strokes in the buffer */ + for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + found = true; + break; + } + } + + if (found == false) { + /* STOP HERE! Nothing to paste! */ + BKE_report(op->reports, + RPT_ERROR, + "Copy some strokes to the clipboard before using the Clone brush to paste " + "copies of them"); + + MEM_freeN(gso); + op->customdata = NULL; + return false; + } + else { + /* initialise customdata */ + gp_brush_clone_init(C, gso); + } + break; + } + + case GPSCULPT_TOOL_GRAB: { + /* initialise the cache needed for this brush */ + gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash"); + break; + } + + /* Others - No customdata needed */ + default: + break; + } + + /* setup space conversions */ + gp_point_conversion_init(C, &gso->gsc); + + /* update header */ + gpsculpt_brush_header_set(C, gso); + + return true; +} + +static void gpsculpt_brush_exit(bContext *C, wmOperator *op) +{ + tGP_BrushEditData *gso = op->customdata; + wmWindow *win = CTX_wm_window(C); + char tool = gso->brush->gpencil_sculpt_tool; + + /* free brush-specific data */ + switch (tool) { + case GPSCULPT_TOOL_GRAB: { + /* Free per-stroke customdata + * - Keys don't need to be freed, as those are the strokes + * - Values assigned to those keys do, as they are custom structs + */ + BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free); + break; + } + + case GPSCULPT_TOOL_CLONE: { + /* Free customdata */ + gp_brush_clone_free(gso); + break; + } + + default: + break; + } + + /* unregister timer (only used for realtime) */ + if (gso->timer) { + WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer); + } + + if (gso->rng != NULL) { + BLI_rng_free(gso->rng); + } + + /* Disable headerprints. */ + ED_workspace_status_text(C, NULL); + + /* disable temp invert flag */ + gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT; + + /* Update geometry data for tagged strokes. */ + gpencil_update_geometry(gso->gpd); + + /* free operator data */ + MEM_freeN(gso); + op->customdata = NULL; +} + +/* poll callback for stroke sculpting operator(s) */ +static bool gpsculpt_brush_poll(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return false; + } + + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; +} + +/* Init Sculpt Stroke ---------------------------------- */ + +static void gpsculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso) +{ + bGPdata *gpd = gso->gpd; + + Scene *scene = gso->scene; + int cfra = CFRA; + + /* only try to add a new frame if this is the first stroke, or the frame has changed */ + if ((gpd == NULL) || (cfra == gso->cfra)) { + return; + } + + /* go through each layer, and ensure that we've got a valid frame to use */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* only editable and visible layers are considered */ + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + bGPDframe *gpf = gpl->actframe; + + /* Make a new frame to work on if the layer's frame + * and the current scene frame don't match up: + * - This is useful when animating as it saves that "uh-oh" moment when you realize you've + * spent too much time editing the wrong frame. + */ + if (gpf->framenum != cfra) { + BKE_gpencil_frame_addcopy(gpl, cfra); + /* Need tag to recalculate evaluated data to avoid crashes. */ + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + } + } + + /* save off new current frame, so that next update works fine */ + gso->cfra = cfra; +} + +/* Apply ----------------------------------------------- */ + +/* Get angle of the segment relative to the original segment before any transformation + * For strokes with one point only this is impossible to calculate because there isn't a + * valid reference point. + */ +static float gpsculpt_rotation_eval_get(tGP_BrushEditData *gso, + bGPDstroke *gps_eval, + bGPDspoint *pt_eval, + int idx_eval) +{ + /* If multiframe or no modifiers, return 0. */ + if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) { + return 0.0f; + } + + GP_SpaceConversion *gsc = &gso->gsc; + bGPDstroke *gps_orig = gps_eval->runtime.gps_orig; + bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig]; + bGPDspoint *pt_prev_eval = NULL; + bGPDspoint *pt_orig_prev = NULL; + if (idx_eval != 0) { + pt_prev_eval = &gps_eval->points[idx_eval - 1]; + } + else { + if (gps_eval->totpoints > 1) { + pt_prev_eval = &gps_eval->points[idx_eval + 1]; + } + else { + return 0.0f; + } + } + + if (pt_eval->runtime.idx_orig != 0) { + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1]; + } + else { + if (gps_orig->totpoints > 1) { + pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1]; + } + else { + return 0.0f; + } + } + + /* create 2D vectors of the stroke segments */ + float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2]; + + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b); + sub_v2_v2(v_orig_a, v_orig_b); + + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a); + gp_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b); + sub_v2_v2(v_eval_a, v_eval_b); + + return angle_v2v2(v_orig_a, v_eval_a); +} + +/* Apply brush operation to points in this stroke */ +static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, + bGPDstroke *gps, + const float diff_mat[4][4], + GP_BrushApplyCb apply) +{ + GP_SpaceConversion *gsc = &gso->gsc; + rcti *rect = &gso->brush_rect; + Brush *brush = gso->brush; + const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size; + + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + bGPDspoint *pt_active = NULL; + + bGPDspoint *pt1, *pt2; + bGPDspoint *pt = NULL; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + int index; + bool include_last = false; + bool changed = false; + float rot_eval = 0.0f; + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { + return false; + } + + if (gps->totpoints == 1) { + bGPDspoint pt_temp; + pt = &gps->points[0]; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* apply operation to this point */ + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, 0); + changed = apply(gso, gps_active, rot_eval, 0, radius, pc1); + } + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Skip if neither one is selected + * (and we are only allowed to edit/consider selected points) */ + if (GPENCIL_ANY_SCULPT_MASK(gso->mask)) { + if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { + include_last = false; + continue; + } + } + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the selection stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * brush region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle( + gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + /* Apply operation to these points */ + bool ok = false; + + /* To each point individually... */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); + ok = apply(gso, gps_active, rot_eval, index, radius, pc1); + } + + /* Only do the second point if this is the last segment, + * and it is unlikely that the point will get handled + * otherwise. + * + * NOTE: There is a small risk here that the second point wasn't really + * actually in-range. In that case, it only got in because + * the line linking the points was! + */ + if (i + 1 == gps->totpoints - 1) { + pt = &gps->points[i + 1]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i + 1); + ok |= apply(gso, gps_active, rot_eval, index, radius, pc2); + include_last = false; + } + } + else { + include_last = true; + } + + changed |= ok; + } + else if (include_last) { + /* This case is for cases where for whatever reason the second vert (1st here) + * doesn't get included because the whole edge isn't in bounds, + * but it would've qualified since it did with the previous step + * (but wasn't added then, to avoid double-ups). + */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + rot_eval = gpsculpt_rotation_eval_get(gso, gps, pt, i); + changed |= apply(gso, gps_active, rot_eval, index, radius, pc1); + include_last = false; + } + } + } + } + } + + return changed; +} + +/* Apply sculpt brushes to strokes in the given frame */ +static bool gpsculpt_brush_do_frame(bContext *C, + tGP_BrushEditData *gso, + bGPDlayer *gpl, + bGPDframe *gpf, + const float diff_mat[4][4]) +{ + bool changed = false; + bool redo_geom = false; + Object *ob = gso->object; + char tool = gso->brush->gpencil_sculpt_tool; + + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + switch (tool) { + case GPSCULPT_TOOL_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); + redo_geom |= changed; + break; + } + + case GPSCULPT_TOOL_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); + break; + } + + case GPSCULPT_TOOL_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); + break; + } + + case GPSCULPT_TOOL_GRAB: /* Grab points */ + { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if (gps_active != NULL) { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps_active); + changed |= gpsculpt_brush_do_stroke( + gso, gps_active, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps_active, diff_mat); + changed |= true; + } + } + redo_geom |= changed; + break; + } + + case GPSCULPT_TOOL_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); + redo_geom |= changed; + break; + } + + case GPSCULPT_TOOL_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); + redo_geom |= changed; + break; + } + + case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); + redo_geom |= changed; + break; + } + + case GPSCULPT_TOOL_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); + redo_geom |= changed; + break; + } + + default: + printf("ERROR: Unknown type of GPencil Sculpt brush \n"); + break; + } + + /* Triangulation must be calculated. */ + if (redo_geom) { + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + if (gpl->actframe == gpf) { + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + /* Update active frame now, only if material has fill. */ + if (gp_style->flag & GP_MATERIAL_FILL_SHOW) { + BKE_gpencil_stroke_geometry_update(gps_active); + } + else { + gpencil_recalc_geometry_tag(gps_active); + } + } + else { + /* Delay a full recalculation for other frames. */ + gpencil_recalc_geometry_tag(gps_active); + } + } + } + + return changed; +} + +/* Perform two-pass brushes which modify the existing strokes */ +static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) +{ + ToolSettings *ts = gso->scene->toolsettings; + Depsgraph *depsgraph = gso->depsgraph; + Object *obact = gso->object; + bool changed = false; + + Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id); + bGPdata *gpd = (bGPdata *)ob_eval->data; + + /* Calculate brush-specific data which applies equally to all points */ + char tool = gso->brush->gpencil_sculpt_tool; + switch (tool) { + case GPSCULPT_TOOL_GRAB: /* Grab points */ + case GPSCULPT_TOOL_PUSH: /* Push points */ + { + /* calculate amount of displacement to apply */ + gso->rot_eval = 0.0f; + gp_brush_grab_calc_dvec(gso); + break; + } + + case GPSCULPT_TOOL_PINCH: /* Pinch points */ + case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */ + { + /* calculate midpoint of the brush (in data space) */ + gp_brush_calc_midpoint(gso); + break; + } + + case GPSCULPT_TOOL_RANDOMIZE: /* Random jitter */ + { + /* compute the displacement vector for the cursor (in data space) */ + gso->rot_eval = 0.0f; + gp_brush_grab_calc_dvec(gso); + break; + } + + default: + break; + } + + /* Find visible strokes, and perform operations on those if hit */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* If no active frame, don't do anything... */ + if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + continue; + } + + /* calculate difference matrix */ + float diff_mat[4][4]; + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (gso->use_multiframe_falloff) { + BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end); + } + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + if (gso->use_multiframe_falloff) { + /* Faloff depends on distance to active frame (relative to the overall frame range) */ + gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + else { + /* No falloff */ + gso->mf_falloff = 1.0f; + } + + /* affect strokes in this frame */ + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); + } + } + } + else { + if (gpl->actframe != NULL) { + /* Apply to active frame's strokes */ + gso->mf_falloff = 1.0f; + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } + } + } + + return changed; +} + +/* Calculate settings for applying brush */ +static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) +{ + tGP_BrushEditData *gso = op->customdata; + Brush *brush = gso->brush; + const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size; + float mousef[2]; + int mouse[2]; + bool changed = false; + + /* Get latest mouse coordinates */ + RNA_float_get_array(itemptr, "mouse", mousef); + gso->mval[0] = mouse[0] = (int)(mousef[0]); + gso->mval[1] = mouse[1] = (int)(mousef[1]); + + gso->pressure = RNA_float_get(itemptr, "pressure"); + + if (RNA_boolean_get(itemptr, "pen_flip")) { + gso->flag |= GP_SCULPT_FLAG_INVERT; + } + else { + gso->flag &= ~GP_SCULPT_FLAG_INVERT; + } + + /* Store coordinates as reference, if operator just started running */ + if (gso->first) { + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + } + + /* Update brush_rect, so that it represents the bounding rectangle of brush */ + gso->brush_rect.xmin = mouse[0] - radius; + gso->brush_rect.ymin = mouse[1] - radius; + gso->brush_rect.xmax = mouse[0] + radius; + gso->brush_rect.ymax = mouse[1] + radius; + + /* Apply brush */ + char tool = gso->brush->gpencil_sculpt_tool; + if (tool == GPSCULPT_TOOL_CLONE) { + changed = gpsculpt_brush_apply_clone(C, gso); + } + else { + changed = gpsculpt_brush_apply_standard(C, gso); + } + + /* Updates */ + if (changed) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Store values for next step */ + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + gso->first = false; +} + +/* Running --------------------------------------------- */ +static Brush *gpsculpt_get_smooth_brush(tGP_BrushEditData *gso) +{ + Main *bmain = gso->bmain; + Brush *brush = BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2); + + return brush; +} + +/* helper - a record stroke, and apply paint event */ +static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushEditData *gso = op->customdata; + PointerRNA itemptr; + float mouse[2]; + + mouse[0] = event->mval[0] + 1; + mouse[1] = event->mval[1] + 1; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "is_start", gso->first); + + /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ + float pressure = event->tablet.pressure; + /* special exception here for too high pressure values on first touch in + * windows for some tablets: clamp the values to be sane */ + if (pressure >= 0.99f) { + pressure = 1.0f; + } + RNA_float_set(&itemptr, "pressure", pressure); + + if (event->shift) { + gso->brush_prev = gso->brush; + + gso->brush = gpsculpt_get_smooth_brush(gso); + if (gso->brush == NULL) { + gso->brush = gso->brush_prev; + } + } + else { + if (gso->brush_prev != NULL) { + gso->brush = gso->brush_prev; + } + } + + /* apply */ + gpsculpt_brush_apply(C, op, &itemptr); +} + +/* reapply */ +static int gpsculpt_brush_exec(bContext *C, wmOperator *op) +{ + if (!gpsculpt_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + RNA_BEGIN (op->ptr, itemptr, "stroke") { + gpsculpt_brush_apply(C, op, &itemptr); + } + RNA_END; + + gpsculpt_brush_exit(C, op); + + return OPERATOR_FINISHED; +} + +/* start modal painting */ +static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushEditData *gso = NULL; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; + bool needs_timer = false; + float brush_rate = 0.0f; + + /* the operator cannot work while play animation */ + if (is_playing) { + BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation"); + + return OPERATOR_CANCELLED; + } + + /* init painting data */ + if (!gpsculpt_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + gso = op->customdata; + + /* initialise type-specific data (used for the entire session) */ + char tool = gso->brush->gpencil_sculpt_tool; + switch (tool) { + /* Brushes requiring timer... */ + case GPSCULPT_TOOL_THICKNESS: + brush_rate = 0.01f; + needs_timer = true; + break; + + case GPSCULPT_TOOL_STRENGTH: + brush_rate = 0.01f; + needs_timer = true; + break; + + case GPSCULPT_TOOL_PINCH: + brush_rate = 0.001f; + needs_timer = true; + break; + + case GPSCULPT_TOOL_TWIST: + brush_rate = 0.01f; + needs_timer = true; + break; + + default: + break; + } + + /* register timer for increasing influence by hovering over an area */ + if (needs_timer) { + gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate); + } + + /* register modal handler */ + WM_event_add_modal_handler(C, op); + + /* start drawing immediately? */ + if (is_modal == false) { + ARegion *region = CTX_wm_region(C); + + /* ensure that we'll have a new frame to draw on */ + gpsculpt_brush_init_stroke(C, gso); + + /* apply first dab... */ + gso->is_painting = true; + gpsculpt_brush_apply_event(C, op, event); + + /* redraw view with feedback */ + ED_region_tag_redraw(region); + } + + return OPERATOR_RUNNING_MODAL; +} + +/* painting - handle events */ +static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushEditData *gso = op->customdata; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + bool redraw_region = false; + bool redraw_toolsettings = false; + + /* The operator can be in 2 states: Painting and Idling */ + if (gso->is_painting) { + /* Painting */ + switch (event->type) { + /* Mouse Move = Apply somewhere else */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + /* apply brush effect at new position */ + gpsculpt_brush_apply_event(C, op, event); + + /* force redraw, so that the cursor will at least be valid */ + redraw_region = true; + break; + + /* Timer Tick - Only if this was our own timer */ + case TIMER: + if (event->customdata == gso->timer) { + gso->timerTick = true; + gpsculpt_brush_apply_event(C, op, event); + gso->timerTick = false; + } + break; + + /* Painting mbut release = Stop painting (back to idle) */ + case LEFTMOUSE: + // BLI_assert(event->val == KM_RELEASE); + if (is_modal) { + /* go back to idling... */ + gso->is_painting = false; + } + else { + /* end sculpt session, since we're not modal */ + gso->is_painting = false; + + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + break; + + /* Abort painting if any of the usual things are tried */ + case MIDDLEMOUSE: + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + } + } + else { + /* Idling */ + BLI_assert(is_modal == true); + + switch (event->type) { + /* Painting mbut press = Start painting (switch to painting state) */ + case LEFTMOUSE: + /* do initial "click" apply */ + gso->is_painting = true; + gso->first = true; + + gpsculpt_brush_init_stroke(C, gso); + gpsculpt_brush_apply_event(C, op, event); + break; + + /* Exit modal operator, based on the "standard" ops */ + case RIGHTMOUSE: + case ESCKEY: + gpsculpt_brush_exit(C, op); + return OPERATOR_FINISHED; + + /* MMB is often used for view manipulations */ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Mouse movements should update the brush cursor - Just redraw the active region */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + redraw_region = true; + break; + + /* Change Frame - Allowed */ + case LEFTARROWKEY: + case RIGHTARROWKEY: + case UPARROWKEY: + case DOWNARROWKEY: + return OPERATOR_PASS_THROUGH; + + /* Camera/View Gizmo's - Allowed */ + /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + return OPERATOR_PASS_THROUGH; + + /* Unhandled event */ + default: + break; + } + } + + /* Redraw region? */ + if (redraw_region) { + ARegion *region = CTX_wm_region(C); + ED_region_tag_redraw(region); + } + + /* Redraw toolsettings (brush settings)? */ + if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + + return OPERATOR_RUNNING_MODAL; +} + +/* Also used for weight paint. */ +void GPENCIL_OT_sculpt_paint(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stroke Sculpt"; + ot->idname = "GPENCIL_OT_sculpt_paint"; + ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX + + /* api callbacks */ + ot->exec = gpsculpt_brush_exec; + ot->invoke = gpsculpt_brush_invoke; + ot->modal = gpsculpt_brush_modal; + ot->cancel = gpsculpt_brush_exit; + ot->poll = gpsculpt_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean( + ot->srna, + "wait_for_input", + true, + "Wait for Input", + "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index be265ed4bd5..26b68707d55 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -84,17 +84,40 @@ static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode) } } +/* Convert vertex mask mode to Select mode */ +static int gpencil_select_mode_from_vertex(eGP_Sculpt_SelectMaskFlag mode) +{ + if (mode & GP_VERTEX_MASK_SELECTMODE_POINT) { + return GP_SELECTMODE_POINT; + } + else if (mode & GP_VERTEX_MASK_SELECTMODE_STROKE) { + return GP_SELECTMODE_STROKE; + } + else if (mode & GP_VERTEX_MASK_SELECTMODE_SEGMENT) { + return GP_SELECTMODE_SEGMENT; + } + else { + return GP_SELECTMODE_POINT; + } +} + static bool gpencil_select_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); if (GPENCIL_SCULPT_MODE(gpd)) { - ToolSettings *ts = CTX_data_tool_settings(C); if (!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) { return false; } } + if (GPENCIL_VERTEX_MODE(gpd)) { + if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + return false; + } + } + /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ if (GPENCIL_ANY_MODE(gpd)) { /* TODO: include a check for visible strokes? */ @@ -350,7 +373,7 @@ static void gp_select_same_layer(bContext *C) Scene *scene = CTX_data_scene(C); CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); bGPDstroke *gps; bool found = false; @@ -832,7 +855,7 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) * from gpencil_paint.c #gp_stroke_eraser_dostroke(). * It would be great to de-duplicate the logic here sometime, but that can wait. */ -static bool gp_stroke_do_circle_sel(bGPdata *gpd, +static bool gp_stroke_do_circle_sel(bGPdata *UNUSED(gpd), bGPDlayer *gpl, bGPDstroke *gps, GP_SpaceConversion *gsc, @@ -845,13 +868,12 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd, const int selectmode, const float scale) { - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bGPDspoint *pt1 = NULL; bGPDspoint *pt2 = NULL; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; int i; bool changed = false; - bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; bGPDspoint *pt_active = NULL; if (gps->totpoints == 1) { @@ -910,22 +932,22 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd, */ hit = true; if (select) { - pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + pt_active = pt1->runtime.pt_orig; if (pt_active != NULL) { pt_active->flag |= GP_SPOINT_SELECT; } - pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2; + pt_active = pt2->runtime.pt_orig; if (pt_active != NULL) { pt_active->flag |= GP_SPOINT_SELECT; } changed = true; } else { - pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + pt_active = pt1->runtime.pt_orig; if (pt_active != NULL) { pt_active->flag &= ~GP_SPOINT_SELECT; } - pt_active = (!is_multiedit) ? pt2->runtime.pt_orig : pt2; + pt_active = pt2->runtime.pt_orig; if (pt_active != NULL) { pt_active->flag &= ~GP_SPOINT_SELECT; } @@ -942,7 +964,7 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd, /* if stroke mode expand selection */ if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) { for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) { - pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1; if (pt_active != NULL) { if (select) { pt_active->flag |= GP_SPOINT_SELECT; @@ -955,7 +977,7 @@ static bool gp_stroke_do_circle_sel(bGPdata *gpd, } /* expand selection to segment */ - pt_active = (!is_multiedit) ? pt1->runtime.pt_orig : pt1; + pt_active = (pt1->runtime.pt_orig) ? pt1->runtime.pt_orig : pt1; if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) { float r_hita[3], r_hitb[3]; bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT); @@ -976,9 +998,17 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); - const int selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? - gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : - ts->gpencil_selectmode_edit; + int selectmode; + if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) { + selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt); + } + else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) { + selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex); + } + else { + selectmode = ts->gpencil_selectmode_edit; + } + const float scale = ts->gp_sculpt.isect_threshold; /* if not edit/sculpt mode, the event is catched but not processed */ @@ -1100,9 +1130,16 @@ static int gpencil_generic_select_exec(bContext *C, ToolSettings *ts = CTX_data_tool_settings(C); ScrArea *sa = CTX_wm_area(C); - const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? - gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : - ts->gpencil_selectmode_edit; + int selectmode; + if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) { + selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt); + } + else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) { + selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex); + } + else { + selectmode = ts->gpencil_selectmode_edit; + } const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)); @@ -1145,16 +1182,16 @@ static int gpencil_generic_select_exec(bContext *C, /* select/deselect points */ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { - bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; bGPDspoint *pt; int i; bool hit = false; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { + if (pt->runtime.pt_orig == NULL) { continue; } - bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + bGPDspoint *pt_active = pt->runtime.pt_orig; /* convert point coords to screenspace */ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data); @@ -1192,7 +1229,7 @@ static int gpencil_generic_select_exec(bContext *C, if ((!is_multiedit) && (pt->runtime.pt_orig == NULL)) { continue; } - bGPDspoint *pt_active = (!is_multiedit) ? pt->runtime.pt_orig : pt; + bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; if (sel_op_result) { pt_active->flag |= GP_SPOINT_SELECT; @@ -1419,6 +1456,10 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) whole = (bool)(gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) == GP_SELECTMODE_STROKE); } + else if ((ob) && (ob->mode == OB_MODE_VERTEX_GPENCIL)) { + whole = (bool)(gpencil_select_mode_from_vertex(ts->gpencil_selectmode_sculpt) == + GP_SELECTMODE_STROKE); + } else { whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE); } @@ -1433,7 +1474,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* XXX: maybe we should go from the top of the stack down instead... */ GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) { - bGPDstroke *gps_active = (!is_multiedit) ? gps->runtime.gps_orig : gps; + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; bGPDspoint *pt; int i; @@ -1526,9 +1567,16 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) hit_stroke->flag |= GP_STROKE_SELECT; /* expand selection to segment */ - const short selectmode = (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) ? - gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt) : - ts->gpencil_selectmode_edit; + int selectmode; + if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) { + selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt); + } + else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) { + selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex); + } + else { + selectmode = ts->gpencil_selectmode_edit; + } if (selectmode == GP_SELECTMODE_SEGMENT) { float r_hita[3], r_hitb[3]; @@ -1606,4 +1654,108 @@ void GPENCIL_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } +/* Select by Vertex Color. */ +static bool gpencil_select_color_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + + if (GPENCIL_VERTEX_MODE(gpd)) { + if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + return false; + } + + /* Any data to use. */ + if (gpd->layers.first) { + return true; + } + } + + return false; +} + +static int gpencil_select_color_exec(bContext *C, wmOperator *op) +{ + const float threshold = RNA_float_get(op->ptr, "threshold"); + + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + if (!GPENCIL_VERTEX_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + Paint *paint = &ts->gp_vertexpaint->paint; + Brush *brush = paint->brush; + bool done = false; + + float hsv_brush[3], hsv_stroke[3]; + rgb_to_hsv_compat_v(brush->rgb, hsv_brush); + + /* Select any visible stroke that uses this color */ + CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) { + bGPDspoint *pt; + int i; + bool gps_selected = false; + /* Check all stroke points. */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->vert_color[3] < 0.03f) { + continue; + } + + rgb_to_hsv_compat_v(pt->vert_color, hsv_stroke); + /* Only check Hue to get full value and saturation ranges. */ + if (compare_ff(hsv_stroke[0], hsv_brush[0], threshold)) { + pt->flag |= GP_SPOINT_SELECT; + gps_selected = true; + } + } + + if (gps_selected) { + gps->flag |= GP_STROKE_SELECT; + done = true; + } + } + CTX_DATA_END; + + if (done) { + /* updates */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_color(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Color"; + ot->idname = "GPENCIL_OT_select_color"; + ot->description = "Select all strokes with same color"; + + /* callbacks */ + ot->exec = gpencil_select_color_exec; + ot->poll = gpencil_select_color_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_float(ot->srna, "threshold", 0.01f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + /** \} */ diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 7b57dacd3e4..f9403fd94b0 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -90,14 +90,14 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) if (gpd_ptr) { if (*gpd_ptr) { bGPdata *gpd = *gpd_ptr; - bGPDlayer *gpl, *gpld; + bGPDlayer *gpld; BKE_gpencil_free_layers(&gpd->layers); /* copy layers */ BLI_listbase_clear(&gpd->layers); - for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* make a copy of source layer and its data */ gpld = BKE_gpencil_layer_duplicate(gpl); BLI_addtail(&gpd->layers, gpld); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 9c2e0f100cc..a975af1c19a 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -328,7 +328,7 @@ bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr) bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) { if (ob && ob->data && (ob->type == OB_GPENCIL)) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(ob->data); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -336,7 +336,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) } else { /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ + /* return BKE_gpencil_layer_frame_find(gpl, cfra); */ } } } @@ -367,7 +367,7 @@ bool gp_active_layer_poll(bContext *C) return false; } bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); return (gpl != NULL); } @@ -553,10 +553,10 @@ bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstr MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); if (gp_style != NULL) { - if (gp_style->flag & GP_STYLE_COLOR_HIDE) { + if (gp_style->flag & GP_MATERIAL_HIDE) { return false; } - if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_MATERIAL_LOCKED)) { return false; } } @@ -630,8 +630,7 @@ void gp_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], /** * Change position relative to parent object */ -void gp_apply_parent( - Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) +void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -641,7 +640,7 @@ void gp_apply_parent( float inverse_diff_mat[4][4]; float fpt[3]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); for (i = 0; i < gps->totpoints; i++) { @@ -654,15 +653,14 @@ void gp_apply_parent( /** * Change point position relative to parent object */ -void gp_apply_parent_point( - Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt) +void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDspoint *pt) { /* undo matrix */ float diff_mat[4][4]; float inverse_diff_mat[4][4]; float fpt[3]; - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); @@ -870,7 +868,7 @@ bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, const RegionView3D *rv3d = gsc->region->regiondata; float rvec[3]; - ED_gp_get_drawing_reference( + ED_gpencil_drawing_reference_get( scene, gsc->ob, gsc->gpl, scene->toolsettings->gpencil_v3d_align, rvec); float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); @@ -930,7 +928,7 @@ void gp_stroke_convertcoords_tpoint(Scene *scene, /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. */ - ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, rvec); + ED_gpencil_drawing_reference_get(scene, ob, gpl, ts->gpencil_v3d_align, rvec); zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL); if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == @@ -949,7 +947,7 @@ void gp_stroke_convertcoords_tpoint(Scene *scene, * Get drawing reference point for conversion or projection of the stroke * \param[out] r_vec : Reference point found */ -void ED_gp_get_drawing_reference( +void ED_gpencil_drawing_reference_get( const Scene *scene, const Object *ob, bGPDlayer *UNUSED(gpl), char align_flag, float r_vec[3]) { const float *fp = scene->cursor.location; @@ -979,7 +977,6 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = CTX_data_active_object(C); - bGPdata *gpd = (bGPdata *)ob->data; GP_SpaceConversion gsc = {NULL}; bGPDspoint *pt; @@ -990,7 +987,7 @@ void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke * /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); /* Adjust each point */ @@ -1182,7 +1179,6 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) if (gps->dvert != NULL) { gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; /* move points from last to first to new place */ i2 = gps->totpoints - 1; @@ -1197,6 +1193,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) pt_final->flag = pt->flag; pt_final->uv_fac = pt->uv_fac; pt_final->uv_rot = pt->uv_rot; + copy_v4_v4(pt_final->vert_color, pt->vert_color); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -1223,6 +1220,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); + interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); if (gps->dvert != NULL) { MDeformVert *dvert_final = &gps->dvert[i2]; @@ -1251,116 +1249,11 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) /* free temp memory */ MEM_SAFE_FREE(temp_points); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } -/** - * Add randomness to stroke - * \param gps: Stroke data - * \param brush: Brush data - */ -void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, RNG *rng) -{ - bGPDspoint *pt1, *pt2, *pt3; - float v1[3]; - float v2[3]; - if (gps->totpoints < 3) { - return; - } - - /* get two vectors using 3 points */ - pt1 = &gps->points[0]; - pt2 = &gps->points[1]; - pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; - - sub_v3_v3v3(v1, &pt2->x, &pt1->x); - sub_v3_v3v3(v2, &pt3->x, &pt2->x); - normalize_v3(v1); - normalize_v3(v2); - - /* get normal vector to plane created by two vectors */ - float normal[3]; - cross_v3_v3v3(normal, v1, v2); - normalize_v3(normal); - - /* get orthogonal vector to plane to rotate random effect */ - float ortho[3]; - cross_v3_v3v3(ortho, v1, normal); - normalize_v3(ortho); - - /* Read all points and apply shift vector (first and last point not modified) */ - for (int i = 1; i < gps->totpoints - 1; i++) { - bGPDspoint *pt = &gps->points[i]; - /* get vector with shift (apply a division because random is too sensitive */ - const float fac = BLI_rng_get_float(rng) * (brush->gpencil_settings->draw_random_sub / 10.0f); - float svec[3]; - copy_v3_v3(svec, ortho); - if (BLI_rng_get_float(rng) > 0.5f) { - mul_v3_fl(svec, -fac); - } - else { - mul_v3_fl(svec, fac); - } - - /* apply shift */ - add_v3_v3(&pt->x, svec); - } -} - -/* ******************************************************** */ -/* Layer Parenting - Compute Parent Transforms */ - -/* calculate difference matrix */ -void ED_gpencil_parent_location(const Depsgraph *depsgraph, - Object *obact, - bGPdata *UNUSED(gpd), - bGPDlayer *gpl, - float diff_mat[4][4]) -{ - Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; - Object *obparent = gpl->parent; - Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : - obparent; - - /* if not layer parented, try with object parented */ - if (obparent_eval == NULL) { - if (ob_eval != NULL) { - if (ob_eval->type == OB_GPENCIL) { - copy_m4_m4(diff_mat, ob_eval->obmat); - return; - } - } - /* not gpencil object */ - unit_m4(diff_mat); - return; - } - else { - if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { - mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); - add_v3_v3(diff_mat[3], ob_eval->obmat[3]); - return; - } - else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); - if (pchan) { - float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat); - mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); - add_v3_v3(diff_mat[3], ob_eval->obmat[3]); - } - else { - /* if bone not found use object (armature) */ - mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); - add_v3_v3(diff_mat[3], ob_eval->obmat[3]); - } - return; - } - else { - unit_m4(diff_mat); /* not defined type */ - } - } -} - -/* reset parent matrix for all layers */ +/* Reset parent matrix for all layers. */ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd) { bGPDspoint *pt; @@ -1370,7 +1263,7 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata float gpl_loc[3]; zero_v3(gpl_loc); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->parent != NULL) { /* calculate new matrix */ if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { @@ -1390,12 +1283,12 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata /* only redo if any change */ if (!equals_m4m4(gpl->inverse, cur_mat)) { /* first apply current transformation to all strokes */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); /* undo local object */ sub_v3_v3(diff_mat[3], gpl_loc); - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { mul_m4_v3(diff_mat, &pt->x); } @@ -1411,10 +1304,7 @@ void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata /* GP Object Stuff */ /* Helper function to create new OB_GPENCIL Object */ -Object *ED_gpencil_add_object(bContext *C, - Scene *UNUSED(scene), - const float loc[3], - ushort local_view_bits) +Object *ED_gpencil_add_object(bContext *C, const float loc[3], ushort local_view_bits) { float rot[3] = {0.0f}; @@ -1437,7 +1327,7 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob) /* if not exist, create a new one */ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) { /* create new brushes */ - BKE_brush_gpencil_presets(bmain, ts); + BKE_brush_gpencil_paint_presets(bmain, ts); } /* ensure a color exists and is assigned to object */ @@ -1676,8 +1566,11 @@ static bool gp_check_cursor_region(bContext *C, int mval_i[2]) ScrArea *sa = CTX_wm_area(C); Object *ob = CTX_data_active_object(C); - if ((ob == NULL) || - (!ELEM(ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) { + if ((ob == NULL) || (!ELEM(ob->mode, + OB_MODE_PAINT_GPENCIL, + OB_MODE_SCULPT_GPENCIL, + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL))) { return false; } @@ -1753,22 +1646,14 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata) Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); ARegion *region = CTX_wm_region(C); + Paint *paint = BKE_paint_get_active_from_context(C); - GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt; bGPdata *gpd = ED_gpencil_data_get_active(C); - GP_Sculpt_Data *gp_brush = NULL; Brush *brush = NULL; Material *ma = NULL; MaterialGPencilStyle *gp_style = NULL; float *last_mouse_position = customdata; - if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { - gp_brush = &gset->brush[gset->weighttype]; - } - else { - gp_brush = &gset->brush[gset->brushtype]; - } - /* default radius and color */ float color[3] = {1.0f, 1.0f, 1.0f}; float darkcolor[3]; @@ -1794,7 +1679,7 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata) return; } - if ((brush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { + if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { return; } @@ -1805,7 +1690,7 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata) } /* get current drawing color */ - ma = BKE_gpencil_object_material_get_from_brush(ob, brush); + ma = BKE_gpencil_object_material_from_brush_get(ob, brush); if (ma) { gp_style = ma->gp_style; @@ -1822,29 +1707,73 @@ static void gp_brush_cursor_draw(bContext *C, int x, int y, void *customdata) copy_v3_v3(color, gp_style->stroke_rgba); } else { - radius = 5.0f; - copy_v3_v3(color, brush->add_col); + /* Only Tint tool must show big cursor. */ + if (brush->gpencil_tool == GPAINT_TOOL_TINT) { + radius = brush->size; + copy_v3_v3(color, brush->rgb); + } + else { + radius = 5.0f; + copy_v3_v3(color, brush->add_col); + } } } } - /* for sculpt use sculpt brush size */ - if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { - if (gp_brush) { - if ((gp_brush->flag & GP_SCULPT_FLAG_ENABLE_CURSOR) == 0) { - return; - } + /* Sculpt use sculpt brush size */ + if (GPENCIL_SCULPT_MODE(gpd)) { + brush = scene->toolsettings->gp_sculptpaint->paint.brush; + if ((brush == NULL) || (brush->gpencil_settings == NULL)) { + return; + } + if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { + return; + } - radius = gp_brush->size; - if (gp_brush->flag & (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) { - copy_v3_v3(color, gp_brush->curcolor_sub); - } - else { - copy_v3_v3(color, gp_brush->curcolor_add); - } + radius = brush->size; + if (brush->gpencil_settings->sculpt_flag & + (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) { + copy_v3_v3(color, brush->sub_col); + } + else { + copy_v3_v3(color, brush->add_col); + } + } + + /* Weight Paint */ + if (GPENCIL_WEIGHT_MODE(gpd)) { + brush = scene->toolsettings->gp_weightpaint->paint.brush; + if ((brush == NULL) || (brush->gpencil_settings == NULL)) { + return; + } + if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { + return; + } + + radius = brush->size; + if (brush->gpencil_settings->sculpt_flag & + (GP_SCULPT_FLAG_INVERT | GP_SCULPT_FLAG_TMP_INVERT)) { + copy_v3_v3(color, brush->sub_col); + } + else { + copy_v3_v3(color, brush->add_col); } } + /* For Vertex Paint use brush size. */ + if (GPENCIL_VERTEX_MODE(gpd)) { + brush = scene->toolsettings->gp_vertexpaint->paint.brush; + if ((brush == NULL) || (brush->gpencil_settings == NULL)) { + return; + } + if ((paint->flags & PAINT_SHOW_BRUSH) == 0) { + return; + } + + radius = brush->size; + copy_v3_v3(color, brush->rgb); + } + /* draw icon */ GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -1924,30 +1853,6 @@ void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata) } } -/* verify if is using the right brush */ -static void gpencil_verify_brush_type(bContext *C, int newmode) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - GP_Sculpt_Settings *gset = &ts->gp_sculpt; - - switch (newmode) { - case OB_MODE_SCULPT_GPENCIL: - gset->flag &= ~GP_SCULPT_SETT_FLAG_WEIGHT_MODE; - if ((gset->brushtype < 0) || (gset->brushtype >= GP_SCULPT_TYPE_WEIGHT)) { - gset->brushtype = GP_SCULPT_TYPE_PUSH; - } - break; - case OB_MODE_WEIGHT_GPENCIL: - gset->flag |= GP_SCULPT_SETT_FLAG_WEIGHT_MODE; - if ((gset->weighttype < GP_SCULPT_TYPE_WEIGHT) || (gset->weighttype >= GP_SCULPT_TYPE_MAX)) { - gset->weighttype = GP_SCULPT_TYPE_WEIGHT; - } - break; - default: - break; - } -} - /* set object modes */ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) { @@ -1961,6 +1866,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; ED_gpencil_toggle_brush_cursor(C, false, NULL); break; case OB_MODE_PAINT_GPENCIL: @@ -1968,6 +1874,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) gpd->flag |= GP_DATA_STROKE_PAINTMODE; gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; ED_gpencil_toggle_brush_cursor(C, true, NULL); break; case OB_MODE_SCULPT_GPENCIL: @@ -1975,7 +1882,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; gpd->flag |= GP_DATA_STROKE_SCULPTMODE; gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; - gpencil_verify_brush_type(C, OB_MODE_SCULPT_GPENCIL); + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; ED_gpencil_toggle_brush_cursor(C, true, NULL); break; case OB_MODE_WEIGHT_GPENCIL: @@ -1983,7 +1890,15 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; - gpencil_verify_brush_type(C, OB_MODE_WEIGHT_GPENCIL); + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_VERTEX_GPENCIL: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpd->flag |= GP_DATA_STROKE_VERTEXMODE; ED_gpencil_toggle_brush_cursor(C, true, NULL); break; default: @@ -1991,6 +1906,7 @@ void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpd->flag &= ~GP_DATA_STROKE_VERTEXMODE; ED_gpencil_toggle_brush_cursor(C, false, NULL); break; } @@ -2032,6 +1948,7 @@ void ED_gpencil_tpoint_to_point(ARegion *region, /* conversion to 3d format */ gpencil_stroke_convertcoords(region, tpt, origin, p3d); copy_v3_v3(&pt->x, p3d); + zero_v4(pt->vert_color); pt->pressure = tpt->pressure; pt->strength = tpt->strength; @@ -2039,63 +1956,6 @@ void ED_gpencil_tpoint_to_point(ARegion *region, pt->uv_rot = tpt->uv_rot; } -/* texture coordinate utilities */ -void ED_gpencil_calc_stroke_uv(Object *ob, bGPDstroke *gps) -{ - if (gps == NULL) { - return; - } - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - float pixsize; - if (gp_style) { - pixsize = gp_style->texture_pixsize / 1000000.0f; - } - else { - /* use this value by default */ - pixsize = 0.0001f; - } - pixsize = MAX2(pixsize, 0.0000001f); - - bGPDspoint *pt = NULL; - bGPDspoint *ptb = NULL; - int i; - float totlen = 0.0f; - - /* first read all points and calc distance */ - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - /* first point */ - if (i == 0) { - pt->uv_fac = 0.0f; - continue; - } - - ptb = &gps->points[i - 1]; - totlen += len_v3v3(&pt->x, &ptb->x) / pixsize; - pt->uv_fac = totlen; - } - - /* normalize the distance using a factor */ - float factor; - - /* if image, use texture width */ - if ((gp_style) && (gp_style->stroke_style == GP_STYLE_STROKE_STYLE_TEXTURE) && - (gp_style->sima)) { - factor = gp_style->sima->gen_x; - } - else if (totlen == 0) { - return; - } - else { - factor = totlen; - } - - for (i = 0; i < gps->totpoints; i++) { - pt = &gps->points[i]; - pt->uv_fac /= factor; - } -} - /* recalc uv for any stroke using the material */ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) { @@ -2108,11 +1968,11 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) continue; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl)) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (BKE_gpencil_layer_is_editable(gpl)) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { /* check if it is editable */ if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; @@ -2120,7 +1980,7 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat) gps_ma = BKE_gpencil_material(ob, gps->mat_nr + 1); /* update */ if ((gps_ma) && (gps_ma == mat)) { - ED_gpencil_calc_stroke_uv(ob, gps); + BKE_gpencil_stroke_uv_update(gps); } } } @@ -2197,6 +2057,7 @@ static void gp_copy_points(bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final pt_final->flag = pt->flag; pt_final->uv_fac = pt->uv_fac; pt_final->uv_rot = pt->uv_rot; + copy_v4_v4(pt_final->vert_color, pt->vert_color); if (gps->dvert != NULL) { MDeformVert *dvert = &gps->dvert[i]; @@ -2251,7 +2112,6 @@ static void gp_insert_point( if (gps->dvert != NULL) { gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } - gps->flag |= GP_STROKE_RECALC_GEOMETRY; /* copy all points */ int i2 = 0; @@ -2275,6 +2135,8 @@ static void gp_insert_point( i2++; } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); MEM_SAFE_FREE(temp_points); } @@ -2518,10 +2380,9 @@ void ED_gpencil_select_toggle_all(bContext *C, int action) * nothing should be able to touch it */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf; /* deselect all strokes on all frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { @@ -2618,6 +2479,18 @@ tGPspoint *ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, return buffer_array; } +void ED_gpencil_sbuffer_update_eval(bGPdata *gpd, Object *ob_eval) +{ + bGPdata *gpd_eval = (bGPdata *)ob_eval->data; + + gpd_eval->runtime.sbuffer = gpd->runtime.sbuffer; + gpd_eval->runtime.sbuffer_sflag = gpd->runtime.sbuffer_sflag; + gpd_eval->runtime.sbuffer_used = gpd->runtime.sbuffer_used; + gpd_eval->runtime.sbuffer_size = gpd->runtime.sbuffer_size; + gpd_eval->runtime.tot_cp_points = gpd->runtime.tot_cp_points; + gpd_eval->runtime.cp_points = gpd->runtime.cp_points; +} + /* Tag all scene grease pencil object to update. */ void ED_gpencil_tag_scene_gpencil(Scene *scene) { @@ -2638,3 +2511,107 @@ void ED_gpencil_tag_scene_gpencil(Scene *scene) WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } + +void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps) +{ + if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + copy_v3_v3(gps->vert_color_fill, brush->rgb); + gps->vert_color_fill[3] = brush->gpencil_settings->vertex_factor; + srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); + } + else { + zero_v4(gps->vert_color_fill); + } +} + +void ED_gpencil_point_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDspoint *pt) +{ + if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + copy_v3_v3(pt->vert_color, brush->rgb); + pt->vert_color[3] = brush->gpencil_settings->vertex_factor; + srgb_to_linearrgb_v4(pt->vert_color, pt->vert_color); + } + else { + zero_v4(pt->vert_color); + } +} + +void ED_gpencil_sbuffer_vertex_color_set( + Depsgraph *depsgraph, Object *ob, ToolSettings *ts, Brush *brush, Material *material) +{ + bGPdata *gpd = (bGPdata *)ob->data; + Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &ob->id); + bGPdata *gpd_eval = (bGPdata *)ob_eval->data; + MaterialGPencilStyle *gp_style = material->gp_style; + + float vertex_color[4]; + copy_v3_v3(vertex_color, brush->rgb); + vertex_color[3] = brush->gpencil_settings->vertex_factor; + srgb_to_linearrgb_v4(vertex_color, vertex_color); + + /* Copy fill vertex color. */ + if (GPENCIL_USE_VERTEX_COLOR_FILL(ts, brush)) { + copy_v4_v4(gpd->runtime.vert_color_fill, vertex_color); + } + else { + copy_v4_v4(gpd->runtime.vert_color_fill, gp_style->fill_rgba); + } + /* Copy stroke vertex color. */ + if (GPENCIL_USE_VERTEX_COLOR_STROKE(ts, brush)) { + copy_v4_v4(gpd->runtime.vert_color, vertex_color); + } + else { + copy_v4_v4(gpd->runtime.vert_color, gp_style->stroke_rgba); + } + + /* Copy to eval data because paint operators don't tag refresh until end for speedup painting. */ + if (gpd_eval != NULL) { + copy_v4_v4(gpd_eval->runtime.vert_color, gpd->runtime.vert_color); + copy_v4_v4(gpd_eval->runtime.vert_color_fill, gpd->runtime.vert_color_fill); + gpd_eval->runtime.matid = gpd->runtime.matid; + } +} + +/* Check if the stroke collides with brush. */ +bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc, + bGPDstroke *gps, + float mouse[2], + const int radius, + const float diff_mat[4][4]) +{ + const int offset = (int)ceil(sqrt((radius * radius) * 2)); + bGPDspoint pt_dummy, pt_dummy_ps; + float boundbox_min[2] = {0.0f}; + float boundbox_max[2] = {0.0f}; + float zerov3[3]; + + /* Check we have something to use (only for old files). */ + if (equals_v3v3(zerov3, gps->boundbox_min)) { + BKE_gpencil_stroke_boundingbox_calc(gps); + } + + /* Convert bound box to 2d */ + copy_v3_v3(&pt_dummy.x, gps->boundbox_min); + gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps); + gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_min[0], &boundbox_min[1]); + + copy_v3_v3(&pt_dummy.x, gps->boundbox_max); + gp_point_to_parent_space(&pt_dummy, diff_mat, &pt_dummy_ps); + gp_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &boundbox_max[0], &boundbox_max[1]); + + /* Ensure the bounding box is oriented to axis. */ + if (boundbox_max[0] < boundbox_min[0]) { + SWAP(float, boundbox_min[0], boundbox_max[0]); + } + if (boundbox_max[1] < boundbox_min[1]) { + SWAP(float, boundbox_min[1], boundbox_max[1]); + } + + rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]}; + + /* For mouse, add a small offet to avoid false negative in corners. */ + rcti rect_mouse = {mouse[0] - offset, mouse[0] + offset, mouse[1] - offset, mouse[1] + offset}; + + /* Check collision between both rectangles. */ + return BLI_rcti_isect(&rect_stroke, &rect_mouse, NULL); +} diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c new file mode 100644 index 00000000000..5e397374437 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_uv.c @@ -0,0 +1,587 @@ +/* + * 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. + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" + +#include "BLI_blenlib.h" +#include "BLI_string.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_unit.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" + +#include "ED_gpencil.h" +#include "ED_numinput.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +typedef struct GpUvData { + Object *ob; + bGPdata *gpd; + GP_SpaceConversion gsc; + float ob_scale; + + float initial_length; + float pixel_size; /* use when mouse input is interpreted as spatial distance */ + bool is_modal; + + /* Arrays of original loc/rot/scale by stroke. */ + float (*array_loc)[2]; + float *array_rot; + float *array_scale; + + /* modal only */ + float mcenter[2]; + float mouse[2]; + + /** Vector with the original orientation. */ + float vinit_rotation[2]; + + void *draw_handle_pixel; +} GpUvData; + +enum { + GP_UV_ROTATE = 0, + GP_UV_TRANSLATE = 1, + GP_UV_SCALE = 2, + GP_UV_ALL = 3, +}; + +#define SMOOTH_FACTOR 0.3f + +static void gpencil_uv_transform_update_header(wmOperator *op, bContext *C) +{ + const int mode = RNA_enum_get(op->ptr, "mode"); + const char *str = TIP_("Confirm: Enter/LClick, Cancel: (Esc/RClick) %s"); + + char msg[UI_MAX_DRAW_STR]; + ScrArea *sa = CTX_wm_area(C); + + if (sa) { + char flts_str[NUM_STR_REP_LEN * 2]; + switch (mode) { + case GP_UV_TRANSLATE: { + float location[2]; + RNA_float_get_array(op->ptr, "location", location); + BLI_snprintf( + flts_str, NUM_STR_REP_LEN, ", Translation: (%f, %f)", location[0], location[1]); + break; + } + case GP_UV_ROTATE: { + BLI_snprintf(flts_str, + NUM_STR_REP_LEN, + ", Rotation: %f", + RAD2DEG(RNA_float_get(op->ptr, "rotation"))); + break; + } + case GP_UV_SCALE: { + BLI_snprintf( + flts_str, NUM_STR_REP_LEN, ", Scale: %f", RAD2DEG(RNA_float_get(op->ptr, "scale"))); + break; + } + default: + break; + } + BLI_snprintf(msg, sizeof(msg), str, flts_str, flts_str + NUM_STR_REP_LEN); + ED_area_status_text(sa, msg); + } +} + +/* Helper: Get stroke center. */ +static void gpencil_stroke_center(bGPDstroke *gps, float r_center[3]) +{ + bGPDspoint *pt; + int i; + + zero_v3(r_center); + if (gps->totpoints > 0) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(r_center, &pt->x); + } + + mul_v3_fl(r_center, 1.0f / gps->totpoints); + } +} + +static bool gpencil_uv_transform_init(bContext *C, wmOperator *op, const bool is_modal) +{ + GpUvData *opdata; + if (is_modal) { + float zero[2] = {0.0f}; + RNA_float_set_array(op->ptr, "location", zero); + RNA_float_set(op->ptr, "rotation", 0.0f); + RNA_float_set(op->ptr, "scale", 1.0f); + } + + op->customdata = opdata = MEM_mallocN(sizeof(GpUvData), __func__); + + opdata->is_modal = is_modal; + opdata->ob = CTX_data_active_object(C); + opdata->gpd = (bGPdata *)opdata->ob->data; + gp_point_conversion_init(C, &opdata->gsc); + opdata->array_loc = NULL; + opdata->array_rot = NULL; + opdata->array_scale = NULL; + opdata->ob_scale = mat4_to_scale(opdata->ob->obmat); + + opdata->vinit_rotation[0] = 1.0f; + opdata->vinit_rotation[1] = 0.0f; + + if (is_modal) { + ARegion *region = CTX_wm_region(C); + + opdata->draw_handle_pixel = ED_region_draw_cb_activate( + region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); + } + + /* Calc selected strokes center. */ + zero_v2(opdata->mcenter); + float center[3] = {0.0f}; + int i = 0; + /* Need use evaluated to get the viewport final position. */ + GP_EVALUATED_STROKES_BEGIN(gpstroke_iter, C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + float r_center[3]; + gpencil_stroke_center(gps, r_center); + /* Add object location. */ + add_v3_v3(r_center, opdata->ob->obmat[3]); + add_v3_v3(center, r_center); + i++; + } + } + GP_EVALUATED_STROKES_END(gpstroke_iter); + + if (i > 0) { + mul_v3_fl(center, 1.0f / i); + /* Create arrays to save all transformations. */ + opdata->array_loc = MEM_calloc_arrayN(i, 2 * sizeof(float), __func__); + opdata->array_rot = MEM_calloc_arrayN(i, sizeof(float), __func__); + opdata->array_scale = MEM_calloc_arrayN(i, sizeof(float), __func__); + i = 0; + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + copy_v2_v2(opdata->array_loc[i], gps->uv_translation); + opdata->array_rot[i] = gps->uv_rotation; + opdata->array_scale[i] = gps->uv_scale; + i++; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + } + /* convert to 2D */ + gp_point_3d_to_xy(&opdata->gsc, GP_STROKE_3DSPACE, center, opdata->mcenter); + + return true; +} + +static void gpencil_uv_transform_exit(bContext *C, wmOperator *op) +{ + GpUvData *opdata; + ScrArea *sa = CTX_wm_area(C); + + opdata = op->customdata; + + if (opdata->is_modal) { + ARegion *region = CTX_wm_region(C); + + ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel); + } + + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT); + + if (sa) { + ED_area_status_text(sa, NULL); + } + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + + MEM_SAFE_FREE(opdata->array_loc); + MEM_SAFE_FREE(opdata->array_rot); + MEM_SAFE_FREE(opdata->array_scale); + MEM_SAFE_FREE(op->customdata); +} + +static void gpencil_transform_fill_cancel(bContext *C, wmOperator *op) +{ + GpUvData *opdata = op->customdata; + UNUSED_VARS(opdata); + + gpencil_uv_transform_exit(C, op); + + /* need to force redisplay or we may still view the modified result */ + ED_region_tag_redraw(CTX_wm_region(C)); +} + +static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op) +{ + const int mode = RNA_enum_get(op->ptr, "mode"); + GpUvData *opdata = op->customdata; + bGPdata *gpd = opdata->gpd; + bool changed = false; + /* Get actual vector. */ + float vr[2]; + sub_v2_v2v2(vr, opdata->mouse, opdata->mcenter); + normalize_v2(vr); + + float location[2]; + RNA_float_get_array(op->ptr, "location", location); + + float uv_rotation = (opdata->is_modal) ? angle_signed_v2v2(opdata->vinit_rotation, vr) : + RNA_float_get(op->ptr, "rotation"); + uv_rotation *= SMOOTH_FACTOR; + + if (opdata->is_modal) { + RNA_float_set(op->ptr, "rotation", uv_rotation); + } + + int i = 0; + + /* Apply transformations to all strokes. */ + if ((mode == GP_UV_TRANSLATE) || (!opdata->is_modal)) { + float mdiff[2]; + mdiff[0] = opdata->mcenter[0] - opdata->mouse[0]; + mdiff[1] = opdata->mcenter[1] - opdata->mouse[1]; + + /* Apply a big amount of smooth always for translate to get smooth result. */ + mul_v2_fl(mdiff, 0.006f); + + /* Apply angle in translation. */ + mdiff[0] *= cos(uv_rotation); + mdiff[1] *= sin(uv_rotation); + if (opdata->is_modal) { + RNA_float_set_array(op->ptr, "location", mdiff); + } + + changed = (bool)((mdiff[0] != 0.0f) || (mdiff[1] != 0.0f)); + if (changed) { + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + if (opdata->is_modal) { + add_v2_v2v2(gps->uv_translation, opdata->array_loc[i], mdiff); + } + else { + copy_v2_v2(gps->uv_translation, location); + } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + i++; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + } + } + + if ((mode == GP_UV_ROTATE) || (!opdata->is_modal)) { + changed = (bool)(uv_rotation != 0.0f); + if (changed) { + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + gps->uv_rotation = (opdata->is_modal) ? opdata->array_rot[i] + uv_rotation : uv_rotation; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + i++; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + } + } + + if ((mode == GP_UV_SCALE) || (!opdata->is_modal)) { + float mdiff[2]; + mdiff[0] = opdata->mcenter[0] - opdata->mouse[0]; + mdiff[1] = opdata->mcenter[1] - opdata->mouse[1]; + float scale = (opdata->is_modal) ? + ((len_v2(mdiff) - opdata->initial_length) * opdata->pixel_size) / + opdata->ob_scale : + RNA_float_get(op->ptr, "scale"); + scale *= SMOOTH_FACTOR; + + if (opdata->is_modal) { + RNA_float_set(op->ptr, "scale", scale); + } + + changed = (bool)(scale != 0.0f); + if (changed) { + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + gps->uv_scale = (opdata->is_modal) ? opdata->array_scale[i] + scale : scale; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + i++; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + } + } + + if ((!opdata->is_modal) || (changed)) { + /* Update cursor line. */ + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return changed; +} + +static int gpencil_transform_fill_exec(bContext *C, wmOperator *op) +{ + if (!gpencil_uv_transform_init(C, op, false)) { + return OPERATOR_CANCELLED; + } + + if (!gpencil_uv_transform_calc(C, op)) { + gpencil_uv_transform_exit(C, op); + return OPERATOR_CANCELLED; + } + + gpencil_uv_transform_exit(C, op); + return OPERATOR_FINISHED; +} + +static bool gpencil_transform_fill_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + bGPdata *gpd = (bGPdata *)ob->data; + if (gpd == NULL) { + return false; + } + + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + + if ((gpl == NULL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) { + return false; + } + + return true; +} + +static int gpencil_transform_fill_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + float mlen[2]; + float center_3d[3]; + + if (!gpencil_uv_transform_init(C, op, true)) { + return OPERATOR_CANCELLED; + } + + GpUvData *opdata = op->customdata; + /* initialize mouse values */ + opdata->mouse[0] = event->mval[0]; + opdata->mouse[1] = event->mval[1]; + + copy_v3_v3(center_3d, opdata->ob->loc); + mlen[0] = opdata->mcenter[0] - event->mval[0]; + mlen[1] = opdata->mcenter[1] - event->mval[1]; + opdata->initial_length = len_v2(mlen); + + opdata->pixel_size = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f; + + /* Calc init rotation vector. */ + float mouse[2] = {event->mval[0], event->mval[1]}; + sub_v2_v2v2(opdata->vinit_rotation, mouse, opdata->mcenter); + normalize_v2(opdata->vinit_rotation); + + gpencil_uv_transform_calc(C, op); + + gpencil_uv_transform_update_header(op, C); + WM_cursor_set(CTX_wm_window(C), WM_CURSOR_EW_ARROW); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int gpencil_transform_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + GpUvData *opdata = op->customdata; + + switch (event->type) { + case ESCKEY: + case RIGHTMOUSE: { + gpencil_transform_fill_cancel(C, op); + return OPERATOR_CANCELLED; + } + case MOUSEMOVE: { + opdata->mouse[0] = event->mval[0]; + opdata->mouse[1] = event->mval[1]; + + if (gpencil_uv_transform_calc(C, op)) { + gpencil_uv_transform_update_header(op, C); + } + else { + gpencil_transform_fill_cancel(C, op); + return OPERATOR_CANCELLED; + } + break; + } + case LEFTMOUSE: + case PADENTER: + case RETKEY: { + if ((event->val == KM_PRESS) || + ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm"))) { + gpencil_uv_transform_calc(C, op); + gpencil_uv_transform_exit(C, op); + return OPERATOR_FINISHED; + } + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void GPENCIL_OT_transform_fill(wmOperatorType *ot) +{ + static const EnumPropertyItem uv_mode[] = { + {GP_UV_TRANSLATE, "TRANSLATE", 0, "Translate", ""}, + {GP_UV_ROTATE, "ROTATE", 0, "Rotate", ""}, + {GP_UV_SCALE, "SCALE", 0, "Scale", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Transform Stroke Fill"; + ot->idname = "GPENCIL_OT_transform_fill"; + ot->description = "Transform Grease Pencil Stroke Fill"; + + /* api callbacks */ + ot->invoke = gpencil_transform_fill_invoke; + ot->modal = gpencil_transform_fill_modal; + ot->exec = gpencil_transform_fill_exec; + ot->cancel = gpencil_transform_fill_cancel; + ot->poll = gpencil_transform_fill_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR_XY | OPTYPE_BLOCKING; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", uv_mode, GP_UV_ROTATE, "Mode", ""); + + prop = RNA_def_float_vector( + ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_float_rotation(ot->srna, + "rotation", + 0, + NULL, + DEG2RADF(-360.0f), + DEG2RADF(360.0f), + "Rotation", + "", + DEG2RADF(-360.0f), + DEG2RADF(360.0f)); + RNA_def_property_float_default(prop, DEG2RADF(0.0f)); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_float(ot->srna, "scale", 1.0f, 0.001f, 100.0f, "Scale", "", 0.001f, 100.0f); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/* Clear UV transformations. */ +static int gpencil_reset_transform_fill_exec(bContext *C, wmOperator *op) +{ + const int mode = RNA_enum_get(op->ptr, "mode"); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + bool changed = false; + + /* Loop all selected strokes and reset. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + if ((mode == GP_UV_TRANSLATE) || (mode == GP_UV_ALL)) { + zero_v2(gps->uv_translation); + } + if ((mode == GP_UV_ROTATE) || (mode == GP_UV_ALL)) { + gps->uv_rotation = 0.0f; + } + if ((mode == GP_UV_SCALE) || (mode == GP_UV_ALL)) { + gps->uv_scale = 1.0f; + } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + changed = true; + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reset_transform_fill(wmOperatorType *ot) +{ + static const EnumPropertyItem uv_clear_mode[] = { + {GP_UV_ALL, "ALL", 0, "All", ""}, + {GP_UV_TRANSLATE, "TRANSLATE", 0, "Translate", ""}, + {GP_UV_ROTATE, "ROTATE", 0, "Rotate", ""}, + {GP_UV_SCALE, "SCALE", 0, "Scale", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Reset Fill Transformations"; + ot->idname = "GPENCIL_OT_reset_transform_fill"; + ot->description = "Reset any UV transformation and back to default values"; + + /* callbacks */ + ot->exec = gpencil_reset_transform_fill_exec; + ot->poll = gpencil_transform_fill_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", uv_clear_mode, GP_UV_ALL, "Mode", ""); +} diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c new file mode 100644 index 00000000000..6a3eebf1baf --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -0,0 +1,899 @@ +/* + * 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) 2015, Blender Foundation + * This is a new part of Blender + * Brush based operators for editing Grease Pencil strokes + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +enum { + GP_PAINT_VERTEX_STROKE = 0, + GP_PAINT_VERTEX_FILL = 1, + GP_PAINT_VERTEX_BOTH = 2, +}; + +static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = { + {GP_PAINT_VERTEX_STROKE, "STROKE", 0, "Stroke", ""}, + {GP_PAINT_VERTEX_FILL, "FILL", 0, "Fill", ""}, + {GP_PAINT_VERTEX_BOTH, "BOTH", 0, "Both", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Poll callback for stroke vertex paint operator. */ +static bool gp_vertexpaint_mode_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + bGPdata *gpd = (bGPdata *)ob->data; + if (GPENCIL_VERTEX_MODE(gpd)) { + if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + return false; + } + + /* Any data to use. */ + if (gpd->layers.first) { + return true; + } + } + + return false; +} + +static int gp_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + bool changed = false; + int i; + bGPDspoint *pt; + const int mode = RNA_enum_get(op->ptr, "mode"); + + float gain, offset; + { + float brightness = RNA_float_get(op->ptr, "brightness"); + float contrast = RNA_float_get(op->ptr, "contrast"); + brightness /= 100.0f; + float delta = contrast / 200.0f; + /* + * The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + if (contrast > 0) { + gain = 1.0f - delta * 2.0f; + gain = 1.0f / max_ff(gain, FLT_EPSILON); + offset = gain * (brightness - delta); + } + else { + delta *= -1; + gain = max_ff(1.0f - delta * 2.0f, 0.0f); + offset = gain * brightness + delta; + } + } + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = gain * gps->vert_color_fill[i2] + offset; + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i2 < 3; i2++) { + pt->vert_color[i2] = gain * pt->vert_color[i2] + offset; + } + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_color_brightness_contrast(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Vertex Paint Bright/Contrast"; + ot->idname = "GPENCIL_OT_vertex_color_brightness_contrast"; + ot->description = "Adjust vertex color brightness/contrast"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_brightness_contrast_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + const float min = -100, max = +100; + prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); + prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); + RNA_def_property_ui_range(prop, min, max, 1, 1); +} + +static int gp_vertexpaint_hsv_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + float hsv[3]; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float hue = RNA_float_get(op->ptr, "h"); + float sat = RNA_float_get(op->ptr, "s"); + float val = RNA_float_get(op->ptr, "v"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + + /* Fill color. */ + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + + rgb_to_hsv_v(gps->vert_color_fill, hsv); + + hsv[0] += (hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= sat; + hsv[2] *= val; + + hsv_to_rgb_v(hsv, gps->vert_color_fill); + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + rgb_to_hsv_v(pt->vert_color, hsv); + + hsv[0] += (hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= sat; + hsv[2] *= val; + + hsv_to_rgb_v(hsv, pt->vert_color); + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_color_hsv(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Hue Saturation Value"; + ot->idname = "GPENCIL_OT_vertex_color_hsv"; + ot->description = "Adjust vertex color HSV values"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_hsv_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); + RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); +} + +static int gp_vertexpaint_invert_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = 1.0f - gps->vert_color_fill[i2]; + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i2 < 3; i2++) { + pt->vert_color[i2] = 1.0f - pt->vert_color[i2]; + } + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_color_invert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Invert"; + ot->idname = "GPENCIL_OT_vertex_color_invert"; + ot->description = "Invert RGB values"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_invert_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); +} + +static int gp_vertexpaint_levels_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float gain = RNA_float_get(op->ptr, "gain"); + float offset = RNA_float_get(op->ptr, "offset"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = gain * (gps->vert_color_fill[i2] + offset); + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i < 3; i2++) { + pt->vert_color[i2] = gain * (pt->vert_color[i2] + offset); + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_color_levels(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Vertex Paint Levels"; + ot->idname = "GPENCIL_OT_vertex_color_levels"; + ot->description = "Adjust levels of vertex colors"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_levels_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + + RNA_def_float( + ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); + RNA_def_float( + ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); +} + +static int gp_vertexpaint_set_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + Paint *paint = &ts->gp_vertexpaint->paint; + Brush *brush = brush = paint->brush; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float factor = RNA_float_get(op->ptr, "factor"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + copy_v3_v3(gps->vert_color_fill, brush->rgb); + gps->vert_color_fill[3] = factor; + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(pt->vert_color, brush->rgb); + pt->vert_color[3] = factor; + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_color_set(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Vertex Paint Set Color"; + ot->idname = "GPENCIL_OT_vertex_color_set"; + ot->description = "Set active color to all selected vertex"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_set_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + RNA_def_float(ot->srna, "factor", 1.0f, 0.001f, 1.0f, "Factor", "Mix Factor", 0.001f, 1.0f); +} + +/* Helper to extract color from vertex color to create a palette. */ +static bool gp_extract_palette_from_vertex(bContext *C, const bool selected, const int threshold) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + bool done = false; + const float range = pow(10.0f, threshold); + float col[3]; + + GHash *color_table = BLI_ghash_int_new(__func__); + + /* Extract all colors. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + if (gp_style == NULL) { + continue; + } + + if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + + bool use_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW); + bool use_fill = (gp_style->flag & GP_MATERIAL_FILL_SHOW); + + /* Material is disabled. */ + if ((!use_fill) && (!use_stroke)) { + continue; + } + + /* Only solid strokes or stencil. */ + if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) { + continue; + } + + /* Only solid fill. */ + if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) { + continue; + } + + /* Fill color. */ + if (gps->vert_color_fill[3] > 0.0f) { + col[0] = truncf(gps->vert_color_fill[0] * range) / range; + col[1] = truncf(gps->vert_color_fill[1] * range) / range; + col[2] = truncf(gps->vert_color_fill[2] * range) / range; + + uint key = rgb_to_cpack(col[0], col[1], col[2]); + + if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) { + BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + + /* Read all points to get all colors. */ + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + col[0] = truncf(pt->vert_color[0] * range) / range; + col[1] = truncf(pt->vert_color[1] * range) / range; + col[2] = truncf(pt->vert_color[2] * range) / range; + + uint key = rgb_to_cpack(col[0], col[1], col[2]); + if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) { + BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } + CTX_DATA_END; + + /* Create the Palette. */ + done = BKE_palette_from_hash(bmain, color_table, ob->id.name + 2, true); + + /* Free memory. */ + BLI_ghash_free(color_table, NULL, NULL); + + return done; +} + +/* Convert Materials to Vertex Color. */ +typedef struct GPMatArray { + uint key; + Material *ma; + int index; +} GPMatArray; + +static uint get_material_type(MaterialGPencilStyle *gp_style, + bool use_stroke, + bool use_fill, + char *name) +{ + uint r_i = 0; + if ((use_stroke) && (use_fill)) { + switch (gp_style->mode) { + case GP_MATERIAL_MODE_LINE: { + r_i = 1; + strcpy(name, "Line Stroke-Fill"); + break; + } + case GP_MATERIAL_MODE_DOT: { + r_i = 2; + strcpy(name, "Dots Stroke-Fill"); + break; + } + case GP_MATERIAL_MODE_SQUARE: { + r_i = 3; + strcpy(name, "Squares Stroke-Fill"); + break; + } + default: + break; + } + } + else if (use_stroke) { + switch (gp_style->mode) { + case GP_MATERIAL_MODE_LINE: { + r_i = 4; + strcpy(name, "Line Stroke"); + break; + } + case GP_MATERIAL_MODE_DOT: { + r_i = 5; + strcpy(name, "Dots Stroke"); + break; + } + case GP_MATERIAL_MODE_SQUARE: { + r_i = 6; + strcpy(name, "Squares Stroke"); + break; + } + default: + break; + } + } + else { + r_i = 7; + strcpy(name, "Solid Fill"); + } + + /* Create key TSSSSFFFF (T: Type S: Stroke Alpha F: Fill Alpha) */ + r_i *= 1e8; + if (use_stroke) { + r_i += gp_style->stroke_rgba[3] * 1e7; + } + if (use_fill) { + r_i += gp_style->fill_rgba[3] * 1e3; + } + + return r_i; +} + +static bool gp_material_to_vertex_poll(bContext *C) +{ + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + return true; +} + +static int gp_material_to_vertex_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const bool remove = RNA_boolean_get(op->ptr, "remove"); + const bool palette = RNA_boolean_get(op->ptr, "palette"); + const bool selected = RNA_boolean_get(op->ptr, "selected"); + + char name[32] = ""; + Material *ma = NULL; + GPMatArray *mat_elm = NULL; + int i; + + bool changed = false; + + short *totcol = BKE_object_material_len_p(ob); + if (totcol == 0) { + return OPERATOR_CANCELLED; + } + + /* These arrays hold all materials and index in the material slots for all combinations. */ + int totmat = *totcol; + GPMatArray *mat_table = MEM_calloc_arrayN(totmat, sizeof(GPMatArray), __func__); + + /* Update stroke material index. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + if (gp_style == NULL) { + continue; + } + + bool use_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) && + (gp_style->stroke_rgba[3] > 0.0f)); + bool use_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) && + (gp_style->fill_rgba[3] > 0.0f)); + bool is_stencil = ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + (gp_style->flag & GP_MATERIAL_STROKE_PATTERN)); + /* Material is disabled. */ + if ((!use_fill) && (!use_stroke)) { + continue; + } + + /* Only solid strokes or stencil. */ + if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) { + continue; + } + + /* Only solid fill. */ + if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) { + continue; + } + + /* Only for no Stencil materials. */ + if (!is_stencil) { + /* Create material type unique key by type and alpha. */ + uint key = get_material_type(gp_style, use_stroke, use_fill, name); + + /* Check if material exist. */ + bool found = false; + for (i = 0; i < totmat; i++) { + mat_elm = &mat_table[i]; + if (mat_elm->ma == NULL) { + break; + } + if (key == mat_elm->key) { + found = true; + break; + } + } + + /* If not found create a new material. */ + if (!found) { + ma = BKE_gpencil_material_add(bmain, name); + if (use_stroke) { + ma->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + } + else { + ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + } + + if (use_fill) { + ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW; + } + else { + ma->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; + } + + ma->gp_style->stroke_rgba[3] = gp_style->stroke_rgba[3]; + ma->gp_style->fill_rgba[3] = gp_style->fill_rgba[3]; + + BKE_object_material_slot_add(bmain, ob); + BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); + + mat_elm->key = key; + mat_elm->ma = ma; + mat_elm->index = ob->totcol - 1; + } + else { + mat_elm = &mat_table[i]; + } + + /* Update stroke */ + gps->mat_nr = mat_elm->index; + } + + changed = true; + copy_v3_v3(gps->vert_color_fill, gp_style->fill_rgba); + gps->vert_color_fill[3] = 1.0f; + + /* Update all points. */ + bGPDspoint *pt; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + copy_v3_v3(pt->vert_color, gp_style->stroke_rgba); + pt->vert_color[3] = 1.0f; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Free memory. */ + MEM_SAFE_FREE(mat_table); + + /* Generate a Palette. */ + if (palette) { + gp_extract_palette_from_vertex(C, selected, 1); + } + + /* Clean unused materials. */ + if (remove) { + WM_operator_name_call( + C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Convert Stroke Materials to Vertex Color"; + ot->idname = "GPENCIL_OT_material_to_vertex_color"; + ot->description = "Replace materials in strokes with Vertex Color"; + + /* api callbacks */ + ot->exec = gp_material_to_vertex_exec; + ot->poll = gp_material_to_vertex_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, + "remove", + true, + "Remove Unused Materiales", + "Remove any unused material after the conversion"); + RNA_def_boolean(ot->srna, "palette", true, "Create Palette", "Create a new palette with colors"); + RNA_def_boolean(ot->srna, "selected", false, "Only Selected", "Convert only selected strokes"); + RNA_def_int(ot->srna, "threshold", 3, 1, 4, "Threshold", "", 1, 4); +} + +/* Extract Palette from Vertex Color. */ +static bool gp_extract_palette_vertex_poll(bContext *C) +{ + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + return true; +} + +static int gp_extract_palette_vertex_exec(bContext *C, wmOperator *op) +{ + const bool selected = RNA_boolean_get(op->ptr, "selected"); + const int threshold = RNA_int_get(op->ptr, "threshold"); + + if (gp_extract_palette_from_vertex(C, selected, threshold)) { + BKE_reportf(op->reports, RPT_INFO, "Palette created"); + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Unable to find Vertex Information to create palette"); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extract Palette from Vertex Color"; + ot->idname = "GPENCIL_OT_extract_palette_vertex"; + ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette"; + + /* api callbacks */ + ot->exec = gp_extract_palette_vertex_exec; + ot->poll = gp_extract_palette_vertex_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean( + ot->srna, "selected", false, "Only Selected", "Convert only selected strokes"); + RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4); +} diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c new file mode 100644 index 00000000000..5b5a306aa25 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -0,0 +1,1414 @@ +/* + * 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) 2015, Blender Foundation + * This is a new part of Blender + * Brush based operators for editing Grease Pencil strokes + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_brush.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +/* ************************************************ */ +/* General Brush Editing Context */ +#define GP_SELECT_BUFFER_CHUNK 256 +#define GP_GRID_PIXEL_SIZE 10.0f + +/* Temp Flags while Painting. */ +typedef enum eGPDvertex_brush_Flag { + /* invert the effect of the brush */ + GP_VERTEX_FLAG_INVERT = (1 << 0), + /* temporary invert action */ + GP_VERTEX_FLAG_TMP_INVERT = (1 << 1), +} eGPDvertex_brush_Flag; + +/* Grid of Colors for Smear. */ +typedef struct tGP_Grid { + /** Lower right corner of rectangle of grid cell. */ + float bottom[2]; + /** Upper left corner of rectangle of grid cell. */ + float top[2]; + /** Average Color */ + float color[4]; + /** Total points included. */ + int totcol; + +} tGP_Grid; + +/* List of points affected by brush. */ +typedef struct tGP_Selected { + /** Referenced stroke. */ + bGPDstroke *gps; + /** Point index in points array. */ + int pt_index; + /** Position */ + int pc[2]; + /** Color */ + float color[4]; +} tGP_Selected; + +/* Context for brush operators */ +typedef struct tGP_BrushVertexpaintData { + Scene *scene; + Object *object; + + ARegion *region; + + /* Current GPencil datablock */ + bGPdata *gpd; + + Brush *brush; + eGPDvertex_brush_Flag flag; + eGP_Vertex_SelectMaskFlag mask; + + /* Space Conversion Data */ + GP_SpaceConversion gsc; + + /* Is the brush currently painting? */ + bool is_painting; + + /* Start of new paint */ + bool first; + + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + + /* Brush Runtime Data: */ + /* - position and pressure + * - the *_prev variants are the previous values + */ + float mval[2], mval_prev[2]; + float pressure, pressure_prev; + + /* - Effect 2D vector */ + float dvec[2]; + + /* - multiframe falloff factor */ + float mf_falloff; + + /* brush geometry (bounding box) */ + rcti brush_rect; + + /* Temp data to save selected points */ + /** Stroke buffer. */ + tGP_Selected *pbuffer; + /** Number of elements currently used in cache. */ + int pbuffer_used; + /** Number of total elements available in cache. */ + int pbuffer_size; + + /** Grid of average colors */ + tGP_Grid *grid; + /** Total number of rows/cols. */ + int grid_size; + /** Total number of cells elments in the grid array. */ + int grid_len; + /** Grid sample position (used to determine distance of falloff) */ + int grid_sample[2]; + /** Grid is ready to use */ + bool grid_ready; + +} tGP_BrushVertexpaintData; + +/* Ensure the buffer to hold temp selected point size is enough to save all points selected. */ +static tGP_Selected *gpencil_select_buffer_ensure(tGP_Selected *buffer_array, + int *buffer_size, + int *buffer_used, + const bool clear) +{ + tGP_Selected *p = NULL; + + /* By default a buffer is created with one block with a predefined number of free slots, + * if the size is not enough, the cache is reallocated adding a new block of free slots. + * This is done in order to keep cache small and improve speed. */ + if (*buffer_used + 1 > *buffer_size) { + if ((*buffer_size == 0) || (buffer_array == NULL)) { + p = MEM_callocN(sizeof(struct tGP_Selected) * GP_SELECT_BUFFER_CHUNK, __func__); + *buffer_size = GP_SELECT_BUFFER_CHUNK; + } + else { + *buffer_size += GP_SELECT_BUFFER_CHUNK; + p = MEM_recallocN(buffer_array, sizeof(struct tGP_Selected) * *buffer_size); + } + + if (p == NULL) { + *buffer_size = *buffer_used = 0; + } + + buffer_array = p; + } + + /* clear old data */ + if (clear) { + *buffer_used = 0; + if (buffer_array != NULL) { + memset(buffer_array, 0, sizeof(tGP_Selected) * *buffer_size); + } + } + + return buffer_array; +} + +/* Brush Operations ------------------------------- */ + +/* Invert behavior of brush? */ +static bool brush_invert_check(tGP_BrushVertexpaintData *gso) +{ + /* The basic setting is no inverted */ + bool invert = false; + + /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */ + if (gso->flag & GP_VERTEX_FLAG_INVERT) { + invert ^= true; + } + + return invert; +} + +/* Compute strength of effect. */ +static float brush_influence_calc(tGP_BrushVertexpaintData *gso, const int radius, const int co[2]) +{ + Brush *brush = gso->brush; + float influence = brush->size; + + /* use pressure? */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + influence *= gso->pressure; + } + + /* distance fading */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, co); + + /* Apply Brush curve. */ + float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius); + influence *= brush_fallof; + + /* apply multiframe falloff */ + influence *= gso->mf_falloff; + + /* return influence */ + return influence; +} + +/* Compute effect vector for directional brushes. */ +static void brush_calc_dvec_2d(tGP_BrushVertexpaintData *gso) +{ + gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + normalize_v2(gso->dvec); +} + +/* Init a grid of cells around mouse position. + * + * For each Cell. + * + * *--------* Top + * | | + * | | + * Bottom *--------* + * + * The number of cells is calculated using the brush size and a predefined + * number of pixels (see: GP_GRID_PIXEL_SIZE) + */ + +static void gp_grid_cells_init(tGP_BrushVertexpaintData *gso) +{ + tGP_Grid *grid; + float bottom[2]; + float top[2]; + int grid_index = 0; + + /* The grid center is (0,0). */ + bottom[0] = gso->brush_rect.xmin - gso->mval[0]; + bottom[1] = gso->brush_rect.ymax - GP_GRID_PIXEL_SIZE - gso->mval[1]; + + /* Calc all cell of the grid from top/left. */ + for (int y = gso->grid_size - 1; y >= 0; y--) { + top[1] = bottom[1] + GP_GRID_PIXEL_SIZE; + + for (int x = 0; x < gso->grid_size; x++) { + top[0] = bottom[0] + GP_GRID_PIXEL_SIZE; + + grid = &gso->grid[grid_index]; + + copy_v2_v2(grid->bottom, bottom); + copy_v2_v2(grid->top, top); + + bottom[0] += GP_GRID_PIXEL_SIZE; + + grid_index++; + } + + /* Reset for new row. */ + bottom[0] = gso->brush_rect.xmin - gso->mval[0]; + bottom[1] -= GP_GRID_PIXEL_SIZE; + } +} + +/* Get the index used in the grid base on dvec. */ +static void gp_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gso, int r_idx[2]) +{ + /* Lower direction. */ + if (gso->dvec[1] < 0.0f) { + if ((gso->dvec[0] >= -1.0f) && (gso->dvec[0] < -0.8f)) { + r_idx[0] = 0; + r_idx[1] = -1; + } + else if ((gso->dvec[0] >= -0.8f) && (gso->dvec[0] < -0.6f)) { + r_idx[0] = -1; + r_idx[1] = -1; + } + else if ((gso->dvec[0] >= -0.6f) && (gso->dvec[0] < 0.6f)) { + r_idx[0] = -1; + r_idx[1] = 0; + } + else if ((gso->dvec[0] >= 0.6f) && (gso->dvec[0] < 0.8f)) { + r_idx[0] = -1; + r_idx[1] = 1; + } + else if (gso->dvec[0] >= 0.8f) { + r_idx[0] = 0; + r_idx[1] = 1; + } + } + /* Upper direction. */ + else { + if ((gso->dvec[0] >= -1.0f) && (gso->dvec[0] < -0.8f)) { + r_idx[0] = 0; + r_idx[1] = -1; + } + else if ((gso->dvec[0] >= -0.8f) && (gso->dvec[0] < -0.6f)) { + r_idx[0] = 1; + r_idx[1] = -1; + } + else if ((gso->dvec[0] >= -0.6f) && (gso->dvec[0] < 0.6f)) { + r_idx[0] = 1; + r_idx[1] = 0; + } + else if ((gso->dvec[0] >= 0.6f) && (gso->dvec[0] < 0.8f)) { + r_idx[0] = 1; + r_idx[1] = 1; + } + else if (gso->dvec[0] >= 0.8f) { + r_idx[0] = 0; + r_idx[1] = 1; + } + } +} + +static int gp_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2]) +{ + float bottom[2], top[2]; + + for (int i = 0; i < gso->grid_len; i++) { + tGP_Grid *grid = &gso->grid[i]; + add_v2_v2v2(bottom, grid->bottom, gso->mval); + add_v2_v2v2(top, grid->top, gso->mval); + + if (pc[0] >= bottom[0] && pc[0] <= top[0] && pc[1] >= bottom[1] && pc[1] <= top[1]) { + return i; + } + } + + return -1; +} + +/* Fill the grid with the color in each cell and assign point cell index. */ +static void gp_grid_colors_calc(tGP_BrushVertexpaintData *gso) +{ + tGP_Selected *selected = NULL; + bGPDstroke *gps_selected = NULL; + bGPDspoint *pt = NULL; + tGP_Grid *grid = NULL; + + /* Don't calculate again. */ + if (gso->grid_ready) { + return; + } + + /* Extract colors by cell. */ + for (int i = 0; i < gso->pbuffer_used; i++) { + selected = &gso->pbuffer[i]; + gps_selected = selected->gps; + pt = &gps_selected->points[selected->pt_index]; + int grid_index = gp_grid_cell_index_get(gso, selected->pc); + + if (grid_index > -1) { + grid = &gso->grid[grid_index]; + /* Add stroke mix color (only if used). */ + if (pt->vert_color[3] > 0.0f) { + add_v3_v3(grid->color, selected->color); + grid->color[3] = 1.0f; + grid->totcol++; + } + } + } + + /* Average colors. */ + for (int i = 0; i < gso->grid_len; i++) { + grid = &gso->grid[i]; + if (grid->totcol > 0) { + mul_v3_fl(grid->color, (1.0f / (float)grid->totcol)); + } + } + + /* Save sample position. */ + round_v2i_v2fl(gso->grid_sample, gso->mval); + + gso->grid_ready = true; + + return; +} + +/* ************************************************ */ +/* Brush Callbacks + * This section defines the callbacks used by each brush to perform their magic. + * These are called on each point within the brush's radius. */ + +/* Tint Brush */ +static bool brush_tint_apply(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int pt_index, + const int radius, + const int co[2]) +{ + Brush *brush = gso->brush; + + /* Attenuate factor to get a smoother tinting. */ + float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) / + 100.0f; + float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f; + + CLAMP(inf, 0.0f, 1.0f); + CLAMP(inf_fill, 0.0f, 1.0f); + + bGPDspoint *pt = &gps->points[pt_index]; + + /* Apply color to Stroke point. */ + if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) { + if (brush_invert_check(gso)) { + pt->vert_color[3] -= inf; + CLAMP_MIN(pt->vert_color[3], 0.0f); + } + else { + /* Premult. */ + mul_v3_fl(pt->vert_color, pt->vert_color[3]); + /* "Alpha over" blending. */ + interp_v3_v3v3(pt->vert_color, pt->vert_color, brush->rgb, inf); + pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf; + /* Un-premult. */ + if (pt->vert_color[3] > 0.0f) { + mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]); + } + } + } + + /* Apply color to Fill area (all with same color and factor). */ + if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) { + if (brush_invert_check(gso)) { + gps->vert_color_fill[3] -= inf_fill; + CLAMP_MIN(gps->vert_color_fill[3], 0.0f); + } + else { + /* Premult. */ + mul_v3_fl(gps->vert_color_fill, gps->vert_color_fill[3]); + /* "Alpha over" blending. */ + interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, brush->rgb, inf_fill); + gps->vert_color_fill[3] = gps->vert_color_fill[3] * (1.0 - inf_fill) + inf_fill; + /* Un-premult. */ + if (gps->vert_color_fill[3] > 0.0f) { + mul_v3_fl(gps->vert_color_fill, 1.0f / gps->vert_color_fill[3]); + } + } + } + + return true; +} + +/* Replace Brush (Don't use pressure or invert). */ +static bool brush_replace_apply(tGP_BrushVertexpaintData *gso, bGPDstroke *gps, int pt_index) +{ + Brush *brush = gso->brush; + bGPDspoint *pt = &gps->points[pt_index]; + + /* Apply color to Stroke point. */ + if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) { + copy_v3_v3(pt->vert_color, brush->rgb); + /* If not mix color, full replace. */ + if (pt->vert_color[3] == 0.0f) { + pt->vert_color[3] = 1.0f; + } + } + + /* Apply color to Fill area (all with same color and factor). */ + if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) { + copy_v3_v3(gps->vert_color_fill, brush->rgb); + /* If not mix color, full replace. */ + if (gps->vert_color_fill[3] == 0.0f) { + gps->vert_color_fill[3] = 1.0f; + } + } + + return true; +} + +/* Get surrounding color. */ +static bool get_surrounding_color(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int pt_index, + float r_color[3]) +{ + tGP_Selected *selected = NULL; + bGPDstroke *gps_selected = NULL; + bGPDspoint *pt = NULL; + + int totcol = 0; + zero_v3(r_color); + + /* Average the surrounding points except current one. */ + for (int i = 0; i < gso->pbuffer_used; i++) { + selected = &gso->pbuffer[i]; + gps_selected = selected->gps; + /* current point is not evaluated. */ + if ((gps_selected == gps) && (selected->pt_index == pt_index)) { + continue; + } + + pt = &gps_selected->points[selected->pt_index]; + + /* Add stroke mix color (only if used). */ + if (pt->vert_color[3] > 0.0f) { + add_v3_v3(r_color, selected->color); + totcol++; + } + } + if (totcol > 0) { + mul_v3_fl(r_color, (1.0f / (float)totcol)); + return true; + } + + return false; +} + +/* Blur Brush */ +static bool brush_blur_apply(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int pt_index, + const int radius, + const int co[2]) +{ + Brush *brush = gso->brush; + + /* Attenuate factor to get a smoother tinting. */ + float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) / + 100.0f; + float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f; + + bGPDspoint *pt = &gps->points[pt_index]; + + /* Get surrounding color. */ + float blur_color[3]; + if (get_surrounding_color(gso, gps, pt_index, blur_color)) { + /* Apply color to Stroke point. */ + if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) { + interp_v3_v3v3(pt->vert_color, pt->vert_color, blur_color, inf); + } + + /* Apply color to Fill area (all with same color and factor). */ + if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) { + interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, blur_color, inf_fill); + } + return true; + } + + return false; +} + +/* Average Brush */ +static bool brush_average_apply(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int pt_index, + const int radius, + const int co[2], + float average_color[3]) +{ + Brush *brush = gso->brush; + + /* Attenuate factor to get a smoother tinting. */ + float inf = (brush_influence_calc(gso, radius, co) * brush->gpencil_settings->draw_strength) / + 100.0f; + float inf_fill = (gso->pressure * brush->gpencil_settings->draw_strength) / 1000.0f; + + bGPDspoint *pt = &gps->points[pt_index]; + + float alpha = pt->vert_color[3]; + float alpha_fill = gps->vert_color_fill[3]; + + if (brush_invert_check(gso)) { + alpha -= inf; + alpha_fill -= inf_fill; + } + else { + alpha += inf; + alpha_fill += inf_fill; + } + + /* Apply color to Stroke point. */ + if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) { + CLAMP(alpha, 0.0f, 1.0f); + interp_v3_v3v3(pt->vert_color, pt->vert_color, average_color, inf); + pt->vert_color[3] = alpha; + } + + /* Apply color to Fill area (all with same color and factor). */ + if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) { + CLAMP(alpha_fill, 0.0f, 1.0f); + copy_v3_v3(gps->vert_color_fill, average_color); + gps->vert_color_fill[3] = alpha_fill; + } + + return true; +} + +/* Smear Brush */ +static bool brush_smear_apply(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int pt_index, + tGP_Selected *selected) +{ + Brush *brush = gso->brush; + tGP_Grid *grid = NULL; + int average_idx[2]; + ARRAY_SET_ITEMS(average_idx, 0, 0); + + bool changed = false; + + /* Need some movement, so first input is not done. */ + if (gso->first) { + return false; + } + + bGPDspoint *pt = &gps->points[pt_index]; + + /* Need get average colors in the grid. */ + if ((!gso->grid_ready) && (gso->pbuffer_used > 0)) { + gp_grid_colors_calc(gso); + } + + /* The influence is equal to strength and no decay around brush radius. */ + float inf = brush->gpencil_settings->draw_strength; + if (brush->flag & GP_BRUSH_USE_PRESSURE) { + inf *= gso->pressure; + } + + /* Calc distance from initial sample location and add a fallof effect. */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, gso->grid_sample); + float fac = 1.0f - (distance / (float)(brush->size * 2)); + CLAMP(fac, 0.0f, 1.0f); + inf *= fac; + + /* Retry row and col for average color. */ + gp_grid_cell_average_color_idx_get(gso, average_idx); + + /* Retry average color cell. */ + int grid_index = gp_grid_cell_index_get(gso, selected->pc); + if (grid_index > -1) { + int row = grid_index / gso->grid_size; + int col = grid_index - (gso->grid_size * row); + row += average_idx[0]; + col += average_idx[1]; + CLAMP(row, 0, gso->grid_size); + CLAMP(col, 0, gso->grid_size); + + int new_index = (row * gso->grid_size) + col; + CLAMP(new_index, 0, gso->grid_len - 1); + grid = &gso->grid[new_index]; + } + + /* Apply color to Stroke point. */ + if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush)) { + if (grid_index > -1) { + if (grid->color[3] > 0.0f) { + // copy_v3_v3(pt->vert_color, grid->color); + interp_v3_v3v3(pt->vert_color, pt->vert_color, grid->color, inf); + changed = true; + } + } + } + + /* Apply color to Fill area (all with same color and factor). */ + if (GPENCIL_TINT_VERTEX_COLOR_FILL(brush)) { + if (grid_index > -1) { + if (grid->color[3] > 0.0f) { + interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, grid->color, inf); + changed = true; + } + } + } + + return changed; +} + +/* ************************************************ */ +/* Header Info */ +static void gp_vertexpaint_brush_header_set(bContext *C) +{ + ED_workspace_status_text(C, + TIP_("GPencil Vertex Paint: LMB to paint | RMB/Escape to Exit" + " | Ctrl to Invert Action")); +} + +/* ************************************************ */ +/* Grease Pencil Vertex Paint Operator */ + +/* Init/Exit ----------------------------------------------- */ + +static bool gp_vertexpaint_brush_init(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + Paint *paint = ob->mode == OB_MODE_VERTEX_GPENCIL ? &ts->gp_vertexpaint->paint : + &ts->gp_paint->paint; + + /* set the brush using the tool */ + tGP_BrushVertexpaintData *gso; + + /* setup operator data */ + gso = MEM_callocN(sizeof(tGP_BrushVertexpaintData), "tGP_BrushVertexpaintData"); + op->customdata = gso; + + gso->brush = paint->brush; + BKE_curvemapping_initialize(gso->brush->curve); + + gso->is_painting = false; + gso->first = true; + + gso->pbuffer = NULL; + gso->pbuffer_size = 0; + gso->pbuffer_used = 0; + + /* Alloc grid array */ + gso->grid_size = (int)(((gso->brush->size * 2.0f) / GP_GRID_PIXEL_SIZE) + 1.0); + /* Square value. */ + gso->grid_len = gso->grid_size * gso->grid_size; + gso->grid = MEM_callocN(sizeof(tGP_Grid) * gso->grid_len, "tGP_Grid"); + gso->grid_ready = false; + + gso->gpd = ED_gpencil_data_get_active(C); + gso->scene = scene; + gso->object = ob; + + gso->region = CTX_wm_region(C); + + /* Save mask. */ + gso->mask = ts->gpencil_selectmode_vertex; + + /* Multiframe settings. */ + gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + + /* Init multi-edit falloff curve data before doing anything, + * so we won't have to do it again later. */ + if (gso->is_multiframe) { + BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + + /* Setup space conversions. */ + gp_point_conversion_init(C, &gso->gsc); + + /* Update header. */ + gp_vertexpaint_brush_header_set(C); + + return true; +} + +static void gp_vertexpaint_brush_exit(bContext *C, wmOperator *op) +{ + tGP_BrushVertexpaintData *gso = op->customdata; + + /* Disable headerprints. */ + ED_workspace_status_text(C, NULL); + + /* Disable temp invert flag. */ + gso->brush->flag &= ~GP_VERTEX_FLAG_TMP_INVERT; + + /* Free operator data */ + MEM_SAFE_FREE(gso->pbuffer); + MEM_SAFE_FREE(gso->grid); + MEM_SAFE_FREE(gso); + op->customdata = NULL; +} + +/* Poll callback for stroke vertex paint operator. */ +static bool gp_vertexpaint_brush_poll(bContext *C) +{ + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; +} + +/* Helper to save the points selected by the brush. */ +static void gp_save_selected_point(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + int index, + int pc[2]) +{ + tGP_Selected *selected; + bGPDspoint *pt = &gps->points[index]; + + /* Ensure the array to save the list of selected points is big enough. */ + gso->pbuffer = gpencil_select_buffer_ensure( + gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, false); + + selected = &gso->pbuffer[gso->pbuffer_used]; + selected->gps = gps; + selected->pt_index = index; + copy_v2_v2_int(selected->pc, pc); + copy_v4_v4(selected->color, pt->vert_color); + + gso->pbuffer_used++; +} + +/* Select points in this stroke and add to an array to be used later. */ +static void gp_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, + bGPDstroke *gps, + const float diff_mat[4][4]) +{ + GP_SpaceConversion *gsc = &gso->gsc; + rcti *rect = &gso->brush_rect; + Brush *brush = gso->brush; + const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size; + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + bGPDspoint *pt_active = NULL; + + bGPDspoint *pt1, *pt2; + bGPDspoint *pt = NULL; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + int index; + bool include_last = false; + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { + return; + } + + if (gps->totpoints == 1) { + bGPDspoint pt_temp; + pt = &gps->points[0]; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* apply operation to this point */ + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, 0, pc1); + } + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* Skip if neither one is selected + * (and we are only allowed to edit/consider selected points) */ + if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) && (GPENCIL_VERTEX_MODE(gso->gpd))) { + if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) { + include_last = false; + continue; + } + } + + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the selection stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * brush region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle( + gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + + /* To each point individually... */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc1); + } + + /* Only do the second point if this is the last segment, + * and it is unlikely that the point will get handled + * otherwise. + * + * NOTE: There is a small risk here that the second point wasn't really + * actually in-range. In that case, it only got in because + * the line linking the points was! + */ + if (i + 1 == gps->totpoints - 1) { + pt = &gps->points[i + 1]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc2); + include_last = false; + } + } + else { + include_last = true; + } + } + else if (include_last) { + /* This case is for cases where for whatever reason the second vert (1st here) + * doesn't get included because the whole edge isn't in bounds, + * but it would've qualified since it did with the previous step + * (but wasn't added then, to avoid double-ups). + */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc1); + + include_last = false; + } + } + } + } + } +} + +/* Apply vertex paint brushes to strokes in the given frame. */ +static bool gp_vertexpaint_brush_do_frame(bContext *C, + tGP_BrushVertexpaintData *gso, + bGPDlayer *gpl, + bGPDframe *gpf, + const float diff_mat[4][4]) +{ + Object *ob = CTX_data_active_object(C); + char tool = ob->mode == OB_MODE_VERTEX_GPENCIL ? gso->brush->gpencil_vertex_tool : + gso->brush->gpencil_tool; + const int radius = (gso->brush->flag & GP_BRUSH_USE_PRESSURE) ? + gso->brush->size * gso->pressure : + gso->brush->size; + tGP_Selected *selected = NULL; + int i; + + /*--------------------------------------------------------------------- + * First step: select the points affected. This step is required to have + * all selected points before apply the effect, because it could be + * required to average data. + *--------------------------------------------------------------------- */ + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + /* Check points below the brush. */ + gp_vertexpaint_select_stroke(gso, gps, diff_mat); + } + + /* For Average tool, need calculate the average resulting color from all colors + * under the brush. */ + float average_color[3] = {0}; + int totcol = 0; + if ((tool == GPVERTEX_TOOL_AVERAGE) && (gso->pbuffer_used > 0)) { + for (i = 0; i < gso->pbuffer_used; i++) { + selected = &gso->pbuffer[i]; + bGPDstroke *gps = selected->gps; + bGPDspoint *pt = &gps->points[selected->pt_index]; + + /* Add stroke mix color (only if used). */ + if (pt->vert_color[3] > 0.0f) { + add_v3_v3(average_color, pt->vert_color); + totcol++; + } + + /* If Fill color mix, add to average. */ + if (gps->vert_color_fill[3] > 0.0f) { + add_v3_v3(average_color, gps->vert_color_fill); + totcol++; + } + } + + /* Get average. */ + if (totcol > 0) { + mul_v3_fl(average_color, (1.0f / (float)totcol)); + } + } + + /*--------------------------------------------------------------------- + * Second step: Apply effect. + *--------------------------------------------------------------------- */ + bool changed = false; + for (i = 0; i < gso->pbuffer_used; i++) { + changed = true; + selected = &gso->pbuffer[i]; + + switch (tool) { + case GPAINT_TOOL_TINT: + case GPVERTEX_TOOL_DRAW: { + brush_tint_apply(gso, selected->gps, selected->pt_index, radius, selected->pc); + changed |= true; + break; + } + case GPVERTEX_TOOL_BLUR: { + brush_blur_apply(gso, selected->gps, selected->pt_index, radius, selected->pc); + changed |= true; + break; + } + case GPVERTEX_TOOL_AVERAGE: { + brush_average_apply( + gso, selected->gps, selected->pt_index, radius, selected->pc, average_color); + changed |= true; + break; + } + case GPVERTEX_TOOL_SMEAR: { + brush_smear_apply(gso, selected->gps, selected->pt_index, selected); + changed |= true; + break; + } + case GPVERTEX_TOOL_REPLACE: { + brush_replace_apply(gso, selected->gps, selected->pt_index); + changed |= true; + break; + } + + default: + printf("ERROR: Unknown type of GPencil Vertex Paint brush\n"); + break; + } + } + /* Clear the selected array, but keep the memory allocation.*/ + gso->pbuffer = gpencil_select_buffer_ensure( + gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, true); + + return changed; +} + +/* Apply brush effect to all layers. */ +static bool gp_vertexpaint_brush_apply_to_layers(bContext *C, tGP_BrushVertexpaintData *gso) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Object *obact = gso->object; + bool changed = false; + + Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id); + bGPdata *gpd = (bGPdata *)ob_eval->data; + + /* Find visible strokes, and perform operations on those if hit */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* If locked or no active frame, don't do anything. */ + if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + continue; + } + + /* calculate difference matrix */ + float diff_mat[4][4]; + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (gso->use_multiframe_falloff) { + BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end); + } + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + if (gso->use_multiframe_falloff) { + /* Faloff depends on distance to active frame (relative to the overall frame range) + */ + gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + else { + /* No falloff */ + gso->mf_falloff = 1.0f; + } + + /* affect strokes in this frame */ + changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat); + } + } + } + else { + /* Apply to active frame's strokes */ + if (gpl->actframe != NULL) { + gso->mf_falloff = 1.0f; + changed |= gp_vertexpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } + } + } + + return changed; +} + +/* Calculate settings for applying brush */ +static void gp_vertexpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) +{ + tGP_BrushVertexpaintData *gso = op->customdata; + Brush *brush = gso->brush; + const int radius = ((brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size); + float mousef[2]; + int mouse[2]; + bool changed = false; + + /* Get latest mouse coordinates */ + RNA_float_get_array(itemptr, "mouse", mousef); + gso->mval[0] = mouse[0] = (int)(mousef[0]); + gso->mval[1] = mouse[1] = (int)(mousef[1]); + + gso->pressure = RNA_float_get(itemptr, "pressure"); + + if (RNA_boolean_get(itemptr, "pen_flip")) { + gso->flag |= GP_VERTEX_FLAG_INVERT; + } + else { + gso->flag &= ~GP_VERTEX_FLAG_INVERT; + } + + /* Store coordinates as reference, if operator just started running */ + if (gso->first) { + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + } + + /* Update brush_rect, so that it represents the bounding rectangle of brush. */ + gso->brush_rect.xmin = mouse[0] - radius; + gso->brush_rect.ymin = mouse[1] - radius; + gso->brush_rect.xmax = mouse[0] + radius; + gso->brush_rect.ymax = mouse[1] + radius; + + /* Calc 2D direction vector and relative angle. */ + brush_calc_dvec_2d(gso); + + /* Calc grid for smear tool. */ + gp_grid_cells_init(gso); + + changed = gp_vertexpaint_brush_apply_to_layers(C, gso); + + /* Updates */ + if (changed) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Store values for next step */ + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + gso->first = false; +} + +/* Running --------------------------------------------- */ + +/* helper - a record stroke, and apply paint event */ +static void gp_vertexpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushVertexpaintData *gso = op->customdata; + PointerRNA itemptr; + float mouse[2]; + + mouse[0] = event->mval[0] + 1; + mouse[1] = event->mval[1] + 1; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "is_start", gso->first); + + /* Handle pressure sensitivity (which is supplied by tablets). */ + float pressure = event->tablet.pressure; + CLAMP(pressure, 0.0f, 1.0f); + RNA_float_set(&itemptr, "pressure", pressure); + + /* apply */ + gp_vertexpaint_brush_apply(C, op, &itemptr); +} + +/* reapply */ +static int gp_vertexpaint_brush_exec(bContext *C, wmOperator *op) +{ + if (!gp_vertexpaint_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + RNA_BEGIN (op->ptr, itemptr, "stroke") { + gp_vertexpaint_brush_apply(C, op, &itemptr); + } + RNA_END; + + gp_vertexpaint_brush_exit(C, op); + + return OPERATOR_FINISHED; +} + +/* start modal painting */ +static int gp_vertexpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushVertexpaintData *gso = NULL; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; + + /* the operator cannot work while play animation */ + if (is_playing) { + BKE_report(op->reports, RPT_ERROR, "Cannot Paint while play animation"); + + return OPERATOR_CANCELLED; + } + + /* init painting data */ + if (!gp_vertexpaint_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + gso = op->customdata; + + /* register modal handler */ + WM_event_add_modal_handler(C, op); + + /* start drawing immediately? */ + if (is_modal == false) { + ARegion *region = CTX_wm_region(C); + + /* apply first dab... */ + gso->is_painting = true; + gp_vertexpaint_brush_apply_event(C, op, event); + + /* redraw view with feedback */ + ED_region_tag_redraw(region); + } + + return OPERATOR_RUNNING_MODAL; +} + +/* painting - handle events */ +static int gp_vertexpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushVertexpaintData *gso = op->customdata; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + bool redraw_region = false; + bool redraw_toolsettings = false; + + /* The operator can be in 2 states: Painting and Idling */ + if (gso->is_painting) { + /* Painting */ + switch (event->type) { + /* Mouse Move = Apply somewhere else */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + /* apply brush effect at new position */ + gp_vertexpaint_brush_apply_event(C, op, event); + + /* force redraw, so that the cursor will at least be valid */ + redraw_region = true; + break; + + /* Painting mbut release = Stop painting (back to idle) */ + case LEFTMOUSE: + if (is_modal) { + /* go back to idling... */ + gso->is_painting = false; + } + else { + /* end painting, since we're not modal */ + gso->is_painting = false; + + gp_vertexpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + } + break; + + /* Abort painting if any of the usual things are tried */ + case MIDDLEMOUSE: + case RIGHTMOUSE: + case ESCKEY: + gp_vertexpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + } + } + else { + /* Idling */ + BLI_assert(is_modal == true); + + switch (event->type) { + /* Painting mbut press = Start painting (switch to painting state) */ + case LEFTMOUSE: + /* do initial "click" apply */ + gso->is_painting = true; + gso->first = true; + + gp_vertexpaint_brush_apply_event(C, op, event); + break; + + /* Exit modal operator, based on the "standard" ops */ + case RIGHTMOUSE: + case ESCKEY: + gp_vertexpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + + /* MMB is often used for view manipulations */ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Mouse movements should update the brush cursor - Just redraw the active region */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + redraw_region = true; + break; + + /* Change Frame - Allowed */ + case LEFTARROWKEY: + case RIGHTARROWKEY: + case UPARROWKEY: + case DOWNARROWKEY: + return OPERATOR_PASS_THROUGH; + + /* Camera/View Gizmo's - Allowed */ + /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + return OPERATOR_PASS_THROUGH; + + /* Unhandled event */ + default: + break; + } + } + + /* Redraw region? */ + if (redraw_region) { + ED_region_tag_redraw(CTX_wm_region(C)); + } + + /* Redraw toolsettings (brush settings)? */ + if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + + return OPERATOR_RUNNING_MODAL; +} + +void GPENCIL_OT_vertex_paint(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stroke Vertex Paint"; + ot->idname = "GPENCIL_OT_vertex_paint"; + ot->description = "Paint stroke points with a color"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_brush_exec; + ot->invoke = gp_vertexpaint_brush_invoke; + ot->modal = gp_vertexpaint_brush_modal; + ot->cancel = gp_vertexpaint_brush_exit; + ot->poll = gp_vertexpaint_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c new file mode 100644 index 00000000000..6b337afa559 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -0,0 +1,901 @@ +/* + * 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) 2015, Blender Foundation + * This is a new part of Blender + * Brush based operators for editing Grease Pencil strokes + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_brush.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "DNA_meshdata_types.h" +#include "BKE_object_deform.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +/* ************************************************ */ +/* General Brush Editing Context */ +#define GP_SELECT_BUFFER_CHUNK 256 + +/* Grid of Colors for Smear. */ +typedef struct tGP_Grid { + /** Lower right corner of rectangle of grid cell. */ + float bottom[2]; + /** Upper left corner of rectangle of grid cell. */ + float top[2]; + /** Average Color */ + float color[4]; + /** Total points included. */ + int totcol; + +} tGP_Grid; + +/* List of points affected by brush. */ +typedef struct tGP_Selected { + /** Referenced stroke. */ + bGPDstroke *gps; + /** Point index in points array. */ + int pt_index; + /** Position */ + int pc[2]; + /** Color */ + float color[4]; +} tGP_Selected; + +/* Context for brush operators */ +typedef struct tGP_BrushWeightpaintData { + struct Main *bmain; + Scene *scene; + Object *object; + + ARegion *region; + + /* Current GPencil datablock */ + bGPdata *gpd; + + Brush *brush; + + /* Space Conversion Data */ + GP_SpaceConversion gsc; + + /* Is the brush currently painting? */ + bool is_painting; + + /* Start of new paint */ + bool first; + + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + + /* active vertex group */ + int vrgroup; + + /* Brush Runtime Data: */ + /* - position and pressure + * - the *_prev variants are the previous values + */ + float mval[2], mval_prev[2]; + float pressure, pressure_prev; + + /* - Effect 2D vector */ + float dvec[2]; + + /* - multiframe falloff factor */ + float mf_falloff; + + /* brush geometry (bounding box) */ + rcti brush_rect; + + /* Temp data to save selected points */ + /** Stroke buffer. */ + tGP_Selected *pbuffer; + /** Number of elements currently used in cache. */ + int pbuffer_used; + /** Number of total elements available in cache. */ + int pbuffer_size; +} tGP_BrushWeightpaintData; + +/* Ensure the buffer to hold temp selected point size is enough to save all points selected. */ +static tGP_Selected *gpencil_select_buffer_ensure(tGP_Selected *buffer_array, + int *buffer_size, + int *buffer_used, + const bool clear) +{ + tGP_Selected *p = NULL; + + /* By default a buffer is created with one block with a predefined number of free slots, + * if the size is not enough, the cache is reallocated adding a new block of free slots. + * This is done in order to keep cache small and improve speed. */ + if (*buffer_used + 1 > *buffer_size) { + if ((*buffer_size == 0) || (buffer_array == NULL)) { + p = MEM_callocN(sizeof(struct tGP_Selected) * GP_SELECT_BUFFER_CHUNK, __func__); + *buffer_size = GP_SELECT_BUFFER_CHUNK; + } + else { + *buffer_size += GP_SELECT_BUFFER_CHUNK; + p = MEM_recallocN(buffer_array, sizeof(struct tGP_Selected) * *buffer_size); + } + + if (p == NULL) { + *buffer_size = *buffer_used = 0; + } + + buffer_array = p; + } + + /* clear old data */ + if (clear) { + *buffer_used = 0; + if (buffer_array != NULL) { + memset(buffer_array, 0, sizeof(tGP_Selected) * *buffer_size); + } + } + + return buffer_array; +} + +/* Brush Operations ------------------------------- */ + +/* Compute strength of effect. */ +static float brush_influence_calc(tGP_BrushWeightpaintData *gso, const int radius, const int co[2]) +{ + Brush *brush = gso->brush; + + /* basic strength factor from brush settings */ + float influence = brush->alpha; + + /* use pressure? */ + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + influence *= gso->pressure; + } + + /* distance fading */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + float distance = (float)len_v2v2_int(mval_i, co); + influence *= 1.0f - (distance / max_ff(radius, 1e-8)); + + /* Apply Brush curve. */ + float brush_fallof = BKE_brush_curve_strength(brush, distance, (float)radius); + influence *= brush_fallof; + + /* apply multiframe falloff */ + influence *= gso->mf_falloff; + + /* return influence */ + return influence; +} + +/* Compute effect vector for directional brushes. */ +static void brush_calc_dvec_2d(tGP_BrushWeightpaintData *gso) +{ + gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + normalize_v2(gso->dvec); +} + +/* ************************************************ */ +/* Brush Callbacks + * This section defines the callbacks used by each brush to perform their magic. + * These are called on each point within the brush's radius. */ + +/* Draw Brush */ +static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, + bGPDstroke *gps, + int pt_index, + const int radius, + const int co[2]) +{ + /* create dvert */ + BKE_gpencil_dvert_ensure(gps); + + MDeformVert *dvert = gps->dvert + pt_index; + float inf; + + /* Compute strength of effect */ + inf = brush_influence_calc(gso, radius, co); + + /* need a vertex group */ + if (gso->vrgroup == -1) { + if (gso->object) { + BKE_object_defgroup_add(gso->object); + DEG_relations_tag_update(gso->bmain); + gso->vrgroup = 0; + } + } + else { + bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); + if (defgroup->flag & DG_LOCK_WEIGHT) { + return false; + } + } + /* Get current weight and blend. */ + MDeformWeight *dw = BKE_defvert_ensure_index(dvert, gso->vrgroup); + if (dw) { + dw->weight = interpf(gso->brush->weight, dw->weight, inf); + CLAMP(dw->weight, 0.0f, 1.0f); + } + return true; +} + +/* ************************************************ */ +/* Header Info */ +static void gp_weightpaint_brush_header_set(bContext *C) +{ + ED_workspace_status_text(C, TIP_("GPencil Weight Paint: LMB to paint | RMB/Escape to Exit")); +} + +/* ************************************************ */ +/* Grease Pencil Weight Paint Operator */ + +/* Init/Exit ----------------------------------------------- */ + +static bool gp_weightpaint_brush_init(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + Paint *paint = &ts->gp_weightpaint->paint; + + /* set the brush using the tool */ + tGP_BrushWeightpaintData *gso; + + /* setup operator data */ + gso = MEM_callocN(sizeof(tGP_BrushWeightpaintData), "tGP_BrushWeightpaintData"); + op->customdata = gso; + + gso->bmain = CTX_data_main(C); + + gso->brush = paint->brush; + BKE_curvemapping_initialize(gso->brush->curve); + + gso->is_painting = false; + gso->first = true; + + gso->pbuffer = NULL; + gso->pbuffer_size = 0; + gso->pbuffer_used = 0; + + gso->gpd = ED_gpencil_data_get_active(C); + gso->scene = scene; + gso->object = ob; + if (ob) { + gso->vrgroup = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = -1; + } + } + else { + gso->vrgroup = -1; + } + + gso->region = CTX_wm_region(C); + + /* Multiframe settings. */ + gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0; + + /* Init multi-edit falloff curve data before doing anything, + * so we won't have to do it again later. */ + if (gso->is_multiframe) { + BKE_curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + + /* Setup space conversions. */ + gp_point_conversion_init(C, &gso->gsc); + + /* Update header. */ + gp_weightpaint_brush_header_set(C); + + return true; +} + +static void gp_weightpaint_brush_exit(bContext *C, wmOperator *op) +{ + tGP_BrushWeightpaintData *gso = op->customdata; + + /* Disable headerprints. */ + ED_workspace_status_text(C, NULL); + + /* Free operator data */ + MEM_SAFE_FREE(gso->pbuffer); + MEM_SAFE_FREE(gso); + op->customdata = NULL; +} + +/* Poll callback for stroke weight paint operator. */ +static bool gp_weightpaint_brush_poll(bContext *C) +{ + /* NOTE: this is a bit slower, but is the most accurate... */ + return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; +} + +/* Helper to save the points selected by the brush. */ +static void gp_save_selected_point(tGP_BrushWeightpaintData *gso, + bGPDstroke *gps, + int index, + int pc[2]) +{ + tGP_Selected *selected; + bGPDspoint *pt = &gps->points[index]; + + /* Ensure the array to save the list of selected points is big enough. */ + gso->pbuffer = gpencil_select_buffer_ensure( + gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, false); + + selected = &gso->pbuffer[gso->pbuffer_used]; + selected->gps = gps; + selected->pt_index = index; + copy_v2_v2_int(selected->pc, pc); + copy_v4_v4(selected->color, pt->vert_color); + + gso->pbuffer_used++; +} + +/* Select points in this stroke and add to an array to be used later. */ +static void gp_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso, + bGPDstroke *gps, + const float diff_mat[4][4]) +{ + GP_SpaceConversion *gsc = &gso->gsc; + rcti *rect = &gso->brush_rect; + Brush *brush = gso->brush; + const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size; + bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps; + bGPDspoint *pt_active = NULL; + + bGPDspoint *pt1, *pt2; + bGPDspoint *pt = NULL; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + int index; + bool include_last = false; + + /* Check if the stroke collide with brush. */ + if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) { + return; + } + + if (gps->totpoints == 1) { + bGPDspoint pt_temp; + pt = &gps->points[0]; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + /* do boundbox check first */ + if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { + /* only check if point is inside */ + int mval_i[2]; + round_v2i_v2fl(mval_i, gso->mval); + if (len_v2v2_int(mval_i, pc1) <= radius) { + /* apply operation to this point */ + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, 0, pc1); + } + } + } + } + else { + /* Loop over the points in the stroke, checking for intersections + * - an intersection means that we touched the stroke + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* Get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the selection stroke */ + if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || + ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) { + /* Check if point segment of stroke had anything to do with + * brush region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle( + gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + + /* To each point individually... */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc1); + } + + /* Only do the second point if this is the last segment, + * and it is unlikely that the point will get handled + * otherwise. + * + * NOTE: There is a small risk here that the second point wasn't really + * actually in-range. In that case, it only got in because + * the line linking the points was! + */ + if (i + 1 == gps->totpoints - 1) { + pt = &gps->points[i + 1]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc2); + include_last = false; + } + } + else { + include_last = true; + } + } + else if (include_last) { + /* This case is for cases where for whatever reason the second vert (1st here) + * doesn't get included because the whole edge isn't in bounds, + * but it would've qualified since it did with the previous step + * (but wasn't added then, to avoid double-ups). + */ + pt = &gps->points[i]; + pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; + index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; + if (pt_active != NULL) { + gp_save_selected_point(gso, gps_active, index, pc1); + + include_last = false; + } + } + } + } + } +} + +/* Apply weight paint brushes to strokes in the given frame. */ +static bool gp_weightpaint_brush_do_frame(bContext *C, + tGP_BrushWeightpaintData *gso, + bGPDlayer *gpl, + bGPDframe *gpf, + const float diff_mat[4][4]) +{ + Object *ob = CTX_data_active_object(C); + char tool = gso->brush->gpencil_weight_tool; + const int radius = (gso->brush->flag & GP_BRUSH_USE_PRESSURE) ? + gso->brush->size * gso->pressure : + gso->brush->size; + tGP_Selected *selected = NULL; + int i; + + /*--------------------------------------------------------------------- + * First step: select the points affected. This step is required to have + * all selected points before apply the effect, because it could be + * required to do some step. Now is not used, but the operator is ready. + *--------------------------------------------------------------------- */ + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + /* Check points below the brush. */ + gp_weightpaint_select_stroke(gso, gps, diff_mat); + } + + /*--------------------------------------------------------------------- + * Second step: Apply effect. + *--------------------------------------------------------------------- */ + bool changed = false; + for (i = 0; i < gso->pbuffer_used; i++) { + changed = true; + selected = &gso->pbuffer[i]; + + switch (tool) { + case GPWEIGHT_TOOL_DRAW: { + brush_draw_apply(gso, selected->gps, selected->pt_index, radius, selected->pc); + changed |= true; + break; + } + default: + printf("ERROR: Unknown type of GPencil Weight Paint brush\n"); + break; + } + } + /* Clear the selected array, but keep the memory allocation.*/ + gso->pbuffer = gpencil_select_buffer_ensure( + gso->pbuffer, &gso->pbuffer_size, &gso->pbuffer_used, true); + + return changed; +} + +/* Apply brush effect to all layers. */ +static bool gp_weightpaint_brush_apply_to_layers(bContext *C, tGP_BrushWeightpaintData *gso) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Object *obact = gso->object; + bool changed = false; + + Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id); + bGPdata *gpd = (bGPdata *)ob_eval->data; + + /* Find visible strokes, and perform operations on those if hit */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* If locked or no active frame, don't do anything. */ + if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) { + continue; + } + + /* calculate difference matrix */ + float diff_mat[4][4]; + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); + + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (gso->use_multiframe_falloff) { + BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end); + } + + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + if (gso->use_multiframe_falloff) { + /* Faloff depends on distance to active frame (relative to the overall frame range) + */ + gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff); + } + else { + /* No falloff */ + gso->mf_falloff = 1.0f; + } + + /* affect strokes in this frame */ + changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpf, diff_mat); + } + } + } + else { + if (gpl->actframe != NULL) { + /* Apply to active frame's strokes */ + gso->mf_falloff = 1.0f; + changed |= gp_weightpaint_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } + } + } + + return changed; +} + +/* Calculate settings for applying brush */ +static void gp_weightpaint_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) +{ + tGP_BrushWeightpaintData *gso = op->customdata; + Brush *brush = gso->brush; + const int radius = ((brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure : + gso->brush->size); + float mousef[2]; + int mouse[2]; + bool changed = false; + + /* Get latest mouse coordinates */ + RNA_float_get_array(itemptr, "mouse", mousef); + gso->mval[0] = mouse[0] = (int)(mousef[0]); + gso->mval[1] = mouse[1] = (int)(mousef[1]); + + gso->pressure = RNA_float_get(itemptr, "pressure"); + + /* Store coordinates as reference, if operator just started running */ + if (gso->first) { + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + } + + /* Update brush_rect, so that it represents the bounding rectangle of brush. */ + gso->brush_rect.xmin = mouse[0] - radius; + gso->brush_rect.ymin = mouse[1] - radius; + gso->brush_rect.xmax = mouse[0] + radius; + gso->brush_rect.ymax = mouse[1] + radius; + + /* Calc 2D direction vector and relative angle. */ + brush_calc_dvec_2d(gso); + + changed = gp_weightpaint_brush_apply_to_layers(C, gso); + + /* Updates */ + if (changed) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Store values for next step */ + gso->mval_prev[0] = gso->mval[0]; + gso->mval_prev[1] = gso->mval[1]; + gso->pressure_prev = gso->pressure; + gso->first = false; +} + +/* Running --------------------------------------------- */ + +/* helper - a record stroke, and apply paint event */ +static void gp_weightpaint_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushWeightpaintData *gso = op->customdata; + PointerRNA itemptr; + float mouse[2]; + + mouse[0] = event->mval[0] + 1; + mouse[1] = event->mval[1] + 1; + + /* fill in stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "is_start", gso->first); + + /* Handle pressure sensitivity (which is supplied by tablets). */ + float pressure = event->tablet.pressure; + CLAMP(pressure, 0.0f, 1.0f); + RNA_float_set(&itemptr, "pressure", pressure); + + /* apply */ + gp_weightpaint_brush_apply(C, op, &itemptr); +} + +/* reapply */ +static int gp_weightpaint_brush_exec(bContext *C, wmOperator *op) +{ + if (!gp_weightpaint_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + RNA_BEGIN (op->ptr, itemptr, "stroke") { + gp_weightpaint_brush_apply(C, op, &itemptr); + } + RNA_END; + + gp_weightpaint_brush_exit(C, op); + + return OPERATOR_FINISHED; +} + +/* start modal painting */ +static int gp_weightpaint_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushWeightpaintData *gso = NULL; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL; + + /* the operator cannot work while play animation */ + if (is_playing) { + BKE_report(op->reports, RPT_ERROR, "Cannot Paint while play animation"); + + return OPERATOR_CANCELLED; + } + + /* init painting data */ + if (!gp_weightpaint_brush_init(C, op)) { + return OPERATOR_CANCELLED; + } + + gso = op->customdata; + + /* register modal handler */ + WM_event_add_modal_handler(C, op); + + /* start drawing immediately? */ + if (is_modal == false) { + ARegion *region = CTX_wm_region(C); + + /* apply first dab... */ + gso->is_painting = true; + gp_weightpaint_brush_apply_event(C, op, event); + + /* redraw view with feedback */ + ED_region_tag_redraw(region); + } + + return OPERATOR_RUNNING_MODAL; +} + +/* painting - handle events */ +static int gp_weightpaint_brush_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGP_BrushWeightpaintData *gso = op->customdata; + const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input"); + bool redraw_region = false; + bool redraw_toolsettings = false; + + /* The operator can be in 2 states: Painting and Idling */ + if (gso->is_painting) { + /* Painting */ + switch (event->type) { + /* Mouse Move = Apply somewhere else */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + /* apply brush effect at new position */ + gp_weightpaint_brush_apply_event(C, op, event); + + /* force redraw, so that the cursor will at least be valid */ + redraw_region = true; + break; + + /* Painting mbut release = Stop painting (back to idle) */ + case LEFTMOUSE: + if (is_modal) { + /* go back to idling... */ + gso->is_painting = false; + } + else { + /* end painting, since we're not modal */ + gso->is_painting = false; + + gp_weightpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + } + break; + + /* Abort painting if any of the usual things are tried */ + case MIDDLEMOUSE: + case RIGHTMOUSE: + case ESCKEY: + gp_weightpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + } + } + else { + /* Idling */ + BLI_assert(is_modal == true); + + switch (event->type) { + /* Painting mbut press = Start painting (switch to painting state) */ + case LEFTMOUSE: + /* do initial "click" apply */ + gso->is_painting = true; + gso->first = true; + + gp_weightpaint_brush_apply_event(C, op, event); + break; + + /* Exit modal operator, based on the "standard" ops */ + case RIGHTMOUSE: + case ESCKEY: + gp_weightpaint_brush_exit(C, op); + return OPERATOR_FINISHED; + + /* MMB is often used for view manipulations */ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Mouse movements should update the brush cursor - Just redraw the active region */ + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + redraw_region = true; + break; + + /* Change Frame - Allowed */ + case LEFTARROWKEY: + case RIGHTARROWKEY: + case UPARROWKEY: + case DOWNARROWKEY: + return OPERATOR_PASS_THROUGH; + + /* Camera/View Gizmo's - Allowed */ + /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */ + case PAD0: + case PAD1: + case PAD2: + case PAD3: + case PAD4: + case PAD5: + case PAD6: + case PAD7: + case PAD8: + case PAD9: + return OPERATOR_PASS_THROUGH; + + /* Unhandled event */ + default: + break; + } + } + + /* Redraw region? */ + if (redraw_region) { + ED_region_tag_redraw(CTX_wm_region(C)); + } + + /* Redraw toolsettings (brush settings)? */ + if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + + return OPERATOR_RUNNING_MODAL; +} + +void GPENCIL_OT_weight_paint(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stroke Weight Paint"; + ot->idname = "GPENCIL_OT_weight_paint"; + ot->description = "Paint stroke points with a color"; + + /* api callbacks */ + ot->exec = gp_weightpaint_brush_exec; + ot->invoke = gp_weightpaint_brush_invoke; + ot->modal = gp_weightpaint_brush_modal; + ot->cancel = gp_weightpaint_brush_exit; + ot->poll = gp_weightpaint_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + PropertyRNA *prop; + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 8d2d9b934d4..8dd162ea538 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -39,6 +39,7 @@ struct bGPDspoint; struct bGPDstroke; struct bGPdata; struct tGPspoint; +struct GP_SpaceConversion; struct ARegion; struct Depsgraph; @@ -46,6 +47,7 @@ struct Main; struct RegionView3D; struct ReportList; struct Scene; +struct ToolSettings; struct ScrArea; struct View3D; struct ViewLayer; @@ -75,23 +77,8 @@ typedef struct tGPspoint { float uv_rot; /* uv rotation for dor mode */ float rnd[3]; /* rnd value */ bool rnd_dirty; /* rnd flag */ - short tflag; /* Internal flag */ } tGPspoint; -/* tGPspoint->flag */ -typedef enum etGPspoint_tFlag { - /* Created by Fake event (used when mouse/pen move very fast while drawing). */ - GP_TPOINT_FAKE = (1 << 0), -} etGPspoint_tFlag; - -/* used to sort by zdepth gpencil objects in viewport */ -/* TODO: this could be a system parameter in userprefs screen */ -#define GP_CACHE_BLOCK_SIZE 16 -typedef struct tGPencilSort { - struct Base *base; - float zdepth; -} tGPencilSort; - /* ----------- Grease Pencil Tools/Context ------------- */ /* Context-dependent */ @@ -210,12 +197,6 @@ bool ED_gpencil_add_lattice_modifier(const struct bContext *C, /* ------------ Transformation Utilities ------------ */ -/* get difference matrix */ -void ED_gpencil_parent_location(const struct Depsgraph *depsgraph, - struct Object *obact, - struct bGPdata *gpd, - struct bGPDlayer *gpl, - float diff_mat[4][4]); /* reset parent matrix for all layers */ void ED_gpencil_reset_layers_parent(struct Depsgraph *depsgraph, struct Object *obact, @@ -231,7 +212,6 @@ void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4 /* ------------ Object Utilities ------------ */ struct Object *ED_gpencil_add_object(struct bContext *C, - struct Scene *scene, const float loc[3], unsigned short local_view_bits); void ED_gpencil_add_defaults(struct bContext *C, struct Object *ob); @@ -251,11 +231,11 @@ void ED_gp_project_point_to_plane(const struct Scene *scene, const float origin[3], const int axis, struct bGPDspoint *pt); -void ED_gp_get_drawing_reference(const struct Scene *scene, - const struct Object *ob, - struct bGPDlayer *gpl, - char align_flag, - float vec[3]); +void ED_gpencil_drawing_reference_get(const struct Scene *scene, + const struct Object *ob, + struct bGPDlayer *gpl, + char align_flag, + float vec[3]); void ED_gpencil_project_stroke_to_view(struct bContext *C, struct bGPDlayer *gpl, struct bGPDstroke *gps); @@ -277,7 +257,6 @@ void ED_gpencil_tpoint_to_point(struct ARegion *region, float origin[3], const struct tGPspoint *tpt, struct bGPDspoint *pt); -void ED_gpencil_calc_stroke_uv(struct Object *ob, struct bGPDstroke *gps); void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat); /* extend selection to stroke intersections @@ -303,9 +282,30 @@ struct tGPspoint *ED_gpencil_sbuffer_ensure(struct tGPspoint *buffer_array, int *buffer_size, int *buffer_used, const bool clear); +void ED_gpencil_sbuffer_update_eval(struct bGPdata *gpd, struct Object *ob_eval); + /* Tag all scene grease pencil object to update. */ void ED_gpencil_tag_scene_gpencil(struct Scene *scene); +/* Vertex color set. */ +void ED_gpencil_fill_vertex_color_set(struct ToolSettings *ts, + struct Brush *brush, + struct bGPDstroke *gps); +void ED_gpencil_point_vertex_color_set(struct ToolSettings *ts, + struct Brush *brush, + struct bGPDspoint *pt); +void ED_gpencil_sbuffer_vertex_color_set(struct Depsgraph *depsgraph, + struct Object *ob, + struct ToolSettings *ts, + struct Brush *brush, + struct Material *material); + +bool ED_gpencil_stroke_check_collision(struct GP_SpaceConversion *gsc, + struct bGPDstroke *gps, + float mouse[2], + const int radius, + const float diff_mat[4][4]); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 165a53203f3..a0a3d0a3b85 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -4115,9 +4115,9 @@ static void ui_def_but_rna__panel_type(bContext *C, uiLayout *layout, void *but_ void ui_but_rna_menu_convert_to_panel_type(uiBut *but, const char *panel_type) { - BLI_assert(but->type == UI_BTYPE_MENU); - BLI_assert(but->menu_create_func == ui_def_but_rna__menu); - BLI_assert((void *)but->poin == but); + BLI_assert(ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR)); + // BLI_assert(but->menu_create_func == ui_def_but_rna__menu); + // BLI_assert((void *)but->poin == but); but->menu_create_func = ui_def_but_rna__panel_type; but->func_argN = BLI_strdup(panel_type); } diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index 790609a17d8..f9f5c745e94 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -39,6 +39,7 @@ #include "BKE_gpencil.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "UI_interface.h" @@ -65,6 +66,8 @@ typedef struct EyedropperGPencil { struct ColorManagedDisplay *display; /** color under cursor RGB */ float color[3]; + /** Mode */ + int mode; } EyedropperGPencil; /* Helper: Draw status message while the user is running the operator */ @@ -89,6 +92,7 @@ static bool eyedropper_gpencil_init(bContext *C, wmOperator *op) display_device = scene->display_settings.display_device; eye->display = IMB_colormanagement_display_get_named(display_device); + eye->mode = RNA_enum_get(op->ptr, "mode"); return true; } @@ -101,31 +105,15 @@ static void eyedropper_gpencil_exit(bContext *C, wmOperator *op) MEM_SAFE_FREE(op->customdata); } -/* Set the material. */ -static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) +static void eyedropper_add_material( + bContext *C, float col_conv[4], const bool only_stroke, const bool only_fill, const bool both) { Main *bmain = CTX_data_main(C); Object *ob = CTX_data_active_object(C); Material *ma = NULL; - const bool only_stroke = ((!event->ctrl) && (!event->shift)); - const bool only_fill = ((!event->ctrl) && (event->shift)); - const bool both = ((event->ctrl) && (event->shift)); - - float col_conv[4]; bool found = false; - /* Convert from linear rgb space to display space because grease pencil colors are in display - * space, and this conversion is needed to undo the conversion to linear performed by - * eyedropper_color_sample_fl. */ - if (eye->display) { - copy_v3_v3(col_conv, eye->color); - IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); - } - else { - copy_v3_v3(col_conv, eye->color); - } - /* Look for a similar material in grease pencil slots. */ short *totcol = BKE_object_material_len_p(ob); for (short i = 0; i < *totcol; i++) { @@ -138,15 +126,15 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed if (gp_style != NULL) { /* Check stroke color. */ bool found_stroke = compare_v3v3(gp_style->stroke_rgba, col_conv, 0.01f) && - (gp_style->flag & GP_STYLE_STROKE_SHOW); + (gp_style->flag & GP_MATERIAL_STROKE_SHOW); /* Check fill color. */ bool found_fill = compare_v3v3(gp_style->fill_rgba, col_conv, 0.01f) && - (gp_style->flag & GP_STYLE_FILL_SHOW); + (gp_style->flag & GP_MATERIAL_FILL_SHOW); - if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) { + if ((only_stroke) && (found_stroke) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0)) { found = true; } - else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0)) { + else if ((only_fill) && (found_fill) && ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) == 0)) { found = true; } else if ((both) && (found_stroke) && (found_fill)) { @@ -180,22 +168,22 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed /* Only create Stroke (default option). */ if (only_stroke) { /* Stroke color. */ - gp_style_new->flag |= GP_STYLE_STROKE_SHOW; - gp_style_new->flag &= ~GP_STYLE_FILL_SHOW; + gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW; + gp_style_new->flag &= ~GP_MATERIAL_FILL_SHOW; copy_v3_v3(gp_style_new->stroke_rgba, col_conv); zero_v4(gp_style_new->fill_rgba); } /* Fill Only. */ else if (only_fill) { /* Fill color. */ - gp_style_new->flag &= ~GP_STYLE_STROKE_SHOW; - gp_style_new->flag |= GP_STYLE_FILL_SHOW; + gp_style_new->flag &= ~GP_MATERIAL_STROKE_SHOW; + gp_style_new->flag |= GP_MATERIAL_FILL_SHOW; zero_v4(gp_style_new->stroke_rgba); copy_v3_v3(gp_style_new->fill_rgba, col_conv); } /* Stroke and Fill. */ else if (both) { - gp_style_new->flag |= GP_STYLE_STROKE_SHOW | GP_STYLE_FILL_SHOW; + gp_style_new->flag |= GP_MATERIAL_STROKE_SHOW | GP_MATERIAL_FILL_SHOW; copy_v3_v3(gp_style_new->stroke_rgba, col_conv); copy_v3_v3(gp_style_new->fill_rgba, col_conv); } @@ -203,6 +191,69 @@ static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, Eyed ED_undo_push(C, "Add Grease Pencil Material"); } +/* Create a new palette color and palette if needed. */ +static void eyedropper_add_palette_color(bContext *C, float col_conv[4]) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + GpPaint *gp_paint = ts->gp_paint; + GpVertexPaint *gp_vertexpaint = ts->gp_vertexpaint; + Paint *paint = &gp_paint->paint; + Paint *vertexpaint = &gp_vertexpaint->paint; + + /* Check for Palette in Draw and Vertex Paint Mode. */ + if (paint->palette == NULL) { + paint->palette = BKE_palette_add(bmain, "Grease Pencil"); + if (vertexpaint->palette == NULL) { + vertexpaint->palette = paint->palette; + } + } + /* Check if the color exist already. */ + Palette *palette = paint->palette; + for (PaletteColor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (compare_v3v3(palcolor->rgb, col_conv, 0.01f)) { + return; + } + } + + /* Create Colors. */ + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, col_conv); + } +} + +/* Set the material or the palette color. */ +static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) +{ + + const bool only_stroke = ((!event->ctrl) && (!event->shift)); + const bool only_fill = ((!event->ctrl) && (event->shift)); + const bool both = ((event->ctrl) && (event->shift)); + + float col_conv[4]; + + /* Convert from linear rgb space to display space because grease pencil colors are in display + * space, and this conversion is needed to undo the conversion to linear performed by + * eyedropper_color_sample_fl. */ + if (eye->display) { + copy_v3_v3(col_conv, eye->color); + IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display); + } + else { + copy_v3_v3(col_conv, eye->color); + } + + /* Add material or Palette color*/ + if (eye->mode == 0) { + eyedropper_add_material(C, col_conv, only_stroke, only_fill, both); + } + else { + eyedropper_add_palette_color(C, col_conv); + } +} + /* Sample the color below cursor. */ static void eyedropper_gpencil_color_sample(bContext *C, EyedropperGPencil *eye, int mx, int my) { @@ -307,6 +358,12 @@ static bool eyedropper_gpencil_poll(bContext *C) void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot) { + static const EnumPropertyItem items_mode[] = { + {0, "MATERIAL", 0, "Material", ""}, + {1, "PALETTE", 0, "Palette", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Grease Pencil Eyedropper"; ot->idname = "UI_OT_eyedropper_gpencil_color"; @@ -321,4 +378,7 @@ void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", items_mode, 0, "Mode", ""); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 6a9632f54bb..088a904ec78 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3994,7 +3994,12 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat copy_v3_v3(data->vec, data->origvec); but->editvec = data->vec; - handlefunc = ui_block_func_COLOR; + if (ui_but_menu_draw_as_popover(but)) { + popoverfunc = but->menu_create_func; + } + else { + handlefunc = ui_block_func_COLOR; + } arg = but; break; @@ -4063,8 +4068,8 @@ int ui_but_menu_direction(uiBut *but) } /** - * Hack for #uiList #UI_BTYPE_LISTROW buttons to "give" events to overlaying #UI_BTYPE_TEXT buttons - * (Ctrl-Click rename feature & co). + * Hack for #uiList #UI_BTYPE_LISTROW buttons to "give" events to overlaying #UI_BTYPE_TEXT + * buttons (Ctrl-Click rename feature & co). */ static uiBut *ui_but_list_row_text_activate(bContext *C, uiBut *but, @@ -5553,14 +5558,13 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co button_activate_state(C, but, BUTTON_STATE_EXIT); ui_apply_but(C, but->block, but, data, true); - /* Button's state need to be changed to EXIT so moving mouse away from this mouse wouldn't - * lead to cancel changes made to this button, but changing state to EXIT also makes no - * button active for a while which leads to triggering operator - * when doing fast scrolling mouse wheel. - * using post activate stuff from button allows to make button be active again after - * checking for all all that mouse leave and cancel stuff, - * so quick scroll wouldn't be an issue anymore. - * Same goes for scrolling wheel in another direction below (sergey). + /* Button's state need to be changed to EXIT so moving mouse away from this mouse + * wouldn't lead to cancel changes made to this button, but changing state to EXIT also + * makes no button active for a while which leads to triggering operator when doing fast + * scrolling mouse wheel. using post activate stuff from button allows to make button be + * active again after checking for all all that mouse leave and cancel stuff, so quick + * scroll wouldn't be an issue anymore. Same goes for scrolling wheel in another + * direction below (sergey). */ data->postbut = but; data->posttype = BUTTON_ACTIVATE_OVER; @@ -5797,15 +5801,27 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else { Scene *scene = CTX_data_scene(C); + bool updated = false; if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); BKE_brush_color_set(scene, brush, color); + updated = true; } else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); IMB_colormanagement_scene_linear_to_srgb_v3(color); BKE_brush_color_set(scene, brush, color); + updated = true; + } + + if (updated) { + PointerRNA brush_ptr; + PropertyRNA *brush_color_prop; + + RNA_id_pointer_create(&brush->id, &brush_ptr); + brush_color_prop = RNA_struct_find_property(&brush_ptr, "color"); + RNA_property_update(C, &brush_ptr, brush_color_prop); } } @@ -9519,7 +9535,8 @@ static int ui_handle_menu_event(bContext *C, /* Closing sub-levels of pull-downs. * * The actual event is handled by the button under the cursor. - * This is done so we can right click on menu items even when they have sub-menus open. */ + * This is done so we can right click on menu items even when they have sub-menus open. + */ case RIGHTMOUSE: if (inside == false) { if (event->val == KM_PRESS && (block->flag & UI_BLOCK_LOOP)) { diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index c1018a67fb3..3e07023e52d 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -2050,7 +2050,10 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) } /* reset the icon */ - if ((ob != NULL) && (ob->mode & OB_MODE_PAINT_GPENCIL) && (br->gpencil_settings != NULL)) { + if ((ob != NULL) && + (ob->mode & (OB_MODE_PAINT_GPENCIL | OB_MODE_VERTEX_GPENCIL | OB_MODE_SCULPT_GPENCIL | + OB_MODE_WEIGHT_GPENCIL)) && + (br->gpencil_settings != NULL)) { switch (br->gpencil_settings->icon_id) { case GP_BRUSH_ICON_PENCIL: br->id.icon_id = ICON_GPBRUSH_PENCIL; @@ -2088,6 +2091,54 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) case GP_BRUSH_ICON_ERASE_STROKE: br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE; break; + case GP_BRUSH_ICON_TINT: + br->id.icon_id = ICON_BRUSH_TEXDRAW; + break; + case GP_BRUSH_ICON_VERTEX_DRAW: + br->id.icon_id = ICON_BRUSH_MIX; + break; + case GP_BRUSH_ICON_VERTEX_BLUR: + br->id.icon_id = ICON_BRUSH_BLUR; + break; + case GP_BRUSH_ICON_VERTEX_AVERAGE: + br->id.icon_id = ICON_BRUSH_BLUR; + break; + case GP_BRUSH_ICON_VERTEX_SMEAR: + br->id.icon_id = ICON_BRUSH_BLUR; + break; + case GP_BRUSH_ICON_VERTEX_REPLACE: + br->id.icon_id = ICON_BRUSH_MIX; + break; + case GP_BRUSH_ICON_GPBRUSH_SMOOTH: + br->id.icon_id = ICON_GPBRUSH_SMOOTH; + break; + case GP_BRUSH_ICON_GPBRUSH_THICKNESS: + br->id.icon_id = ICON_GPBRUSH_THICKNESS; + break; + case GP_BRUSH_ICON_GPBRUSH_STRENGTH: + br->id.icon_id = ICON_GPBRUSH_STRENGTH; + break; + case GP_BRUSH_ICON_GPBRUSH_RANDOMIZE: + br->id.icon_id = ICON_GPBRUSH_RANDOMIZE; + break; + case GP_BRUSH_ICON_GPBRUSH_GRAB: + br->id.icon_id = ICON_GPBRUSH_GRAB; + break; + case GP_BRUSH_ICON_GPBRUSH_PUSH: + br->id.icon_id = ICON_GPBRUSH_PUSH; + break; + case GP_BRUSH_ICON_GPBRUSH_TWIST: + br->id.icon_id = ICON_GPBRUSH_TWIST; + break; + case GP_BRUSH_ICON_GPBRUSH_PINCH: + br->id.icon_id = ICON_GPBRUSH_PINCH; + break; + case GP_BRUSH_ICON_GPBRUSH_CLONE: + br->id.icon_id = ICON_GPBRUSH_CLONE; + break; + case GP_BRUSH_ICON_GPBRUSH_WEIGHT: + br->id.icon_id = ICON_GPBRUSH_WEIGHT; + break; default: br->id.icon_id = ICON_GPBRUSH_PEN; break; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 8e65b818314..dd002f4e77f 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2325,7 +2325,7 @@ void uiItemFullR_with_popover(uiLayout *layout, uiItemFullR(layout, ptr, prop, index, value, flag, name, icon); but = but->next; while (but) { - if (but->rnaprop == prop && but->type == UI_BTYPE_MENU) { + if (but->rnaprop == prop && ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_COLOR)) { ui_but_rna_menu_convert_to_panel_type(but, panel_type); break; } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index c80c5317735..b752a1a1429 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5385,6 +5385,21 @@ void uiTemplateColorPicker(uiLayout *layout, } } +static void ui_template_palette_menu(bContext *UNUSED(C), uiLayout *layout, void *UNUSED(but_p)) +{ + uiLayout *row; + + uiItemL(layout, IFACE_("Sort by:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemEnumO_value(row, IFACE_("Hue"), ICON_NONE, "PALETTE_OT_sort", "type", 1); + row = uiLayoutRow(layout, false); + uiItemEnumO_value(row, IFACE_("Saturation"), ICON_NONE, "PALETTE_OT_sort", "type", 2); + row = uiLayoutRow(layout, false); + uiItemEnumO_value(row, IFACE_("Value"), ICON_NONE, "PALETTE_OT_sort", "type", 3); + row = uiLayoutRow(layout, false); + uiItemEnumO_value(row, IFACE_("Luminance"), ICON_NONE, "PALETTE_OT_sort", "type", 4); +} + void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, @@ -5396,6 +5411,8 @@ void uiTemplatePalette(uiLayout *layout, PaletteColor *color; uiBlock *block; uiLayout *col; + uiBut *but = NULL; + int row_cols = 0, col_id = 0; int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); @@ -5437,6 +5454,37 @@ void uiTemplatePalette(uiLayout *layout, UI_UNIT_X, UI_UNIT_Y, NULL); + if (color) { + but = uiDefIconButO(block, + UI_BTYPE_BUT, + "PALETTE_OT_color_move", + WM_OP_INVOKE_DEFAULT, + ICON_TRIA_UP, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL); + UI_but_operator_ptr_get(but); + RNA_enum_set(but->opptr, "type", -1); + + but = uiDefIconButO(block, + UI_BTYPE_BUT, + "PALETTE_OT_color_move", + WM_OP_INVOKE_DEFAULT, + ICON_TRIA_DOWN, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL); + UI_but_operator_ptr_get(but); + RNA_enum_set(but->opptr, "type", 1); + + /* Menu. */ + uiDefIconMenuBut( + block, ui_template_palette_menu, NULL, ICON_SORTSIZE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); + } col = uiLayoutColumn(layout, true); uiLayoutRow(col, true); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 06f532f99e3..17b6bfdb956 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1537,7 +1537,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) * Will also remove parent from grease pencil from other scenes, * even when use_global is false... */ for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->parent != NULL) { if (gpl->parent == ob) { gpl->parent = NULL; @@ -2394,7 +2394,7 @@ static int convert_exec(bContext *C, wmOperator *op) * Nurbs Surface are not supported. */ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - gpencil_ob = ED_gpencil_add_object(C, scene, ob->loc, local_view_bits); + gpencil_ob = ED_gpencil_add_object(C, ob->loc, local_view_bits); copy_v3_v3(gpencil_ob->rot, ob->rot); copy_v3_v3(gpencil_ob->scale, ob->scale); BKE_gpencil_convert_curve(bmain, scene, gpencil_ob, ob, false, false, true); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index f9ac0474d44..def93db2537 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1370,7 +1370,8 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C, OB_MODE_EDIT_GPENCIL, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL) && + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL) && (ob->type == OB_GPENCIL)) || (input->value == OB_MODE_OBJECT)) { RNA_enum_item_add(&item, &totitem, input); diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 9138e65dd2f..4543f1a19b3 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -91,6 +91,11 @@ GpencilModifierData *ED_object_gpencil_modifier_add( /* make sure modifier data has unique name */ BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md); + /* Enable edit mode visible by default. */ + if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) { + new_md->mode |= eGpencilModifierMode_Editmode; + } + bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); @@ -362,7 +367,7 @@ void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) PropertyRNA *prop; /* identifiers */ - ot->name = "Add Grease Pencil Modifier"; + ot->name = "Add Modifier"; ot->description = "Add a procedural operation/effect to the active grease pencil object"; ot->idname = "OBJECT_OT_gpencil_modifier_add"; diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 80e7e6312aa..edc2f15813c 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -88,6 +88,9 @@ static const char *object_mode_op_string(eObjectMode mode) if (mode == OB_MODE_WEIGHT_GPENCIL) { return "GPENCIL_OT_weightmode_toggle"; } + if (mode == OB_MODE_VERTEX_GPENCIL) { + return "GPENCIL_OT_vertexmode_toggle"; + } return NULL; } @@ -129,7 +132,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) break; case OB_GPENCIL: if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | - OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL)) { + OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) { return true; } break; diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 039714ca3ec..3b1fb49c383 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -696,7 +696,7 @@ static int apply_objects_internal(bContext *C, /* Unsupported configuration */ bool has_unparented_layers = false; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* Parented layers aren't supported as we can't easily re-evaluate * the scene to sample parent movement */ if (gpl->parent == NULL) { @@ -1394,13 +1394,13 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) /* recalculate all strokes * (all layers are considered without evaluating lock attributes) */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); /* undo matrix */ invert_m4_m4(inverse_diff_mat, diff_mat); - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { float mpt[3]; mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e72818479c4..444413a757f 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1723,9 +1723,81 @@ static void ed_default_handlers( wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); WM_event_add_keymap_handler(handlers, keymap_paint_fill); + wmKeyMap *keymap_paint_tint = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Paint (Tint)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_tint); + wmKeyMap *keymap_sculpt = WM_keymap_ensure( wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); WM_event_add_keymap_handler(handlers, keymap_sculpt); + + wmKeyMap *keymap_vertex = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex); + + wmKeyMap *keymap_vertex_draw = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex (Draw)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex_draw); + + wmKeyMap *keymap_vertex_blur = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex (Blur)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex_blur); + + wmKeyMap *keymap_vertex_average = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex (Average)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex_average); + + wmKeyMap *keymap_vertex_smear = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex (Smear)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex_smear); + + wmKeyMap *keymap_vertex_replace = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Vertex (Replace)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_vertex_replace); + + wmKeyMap *keymap_sculpt_smooth = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Smooth)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_smooth); + + wmKeyMap *keymap_sculpt_thickness = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Thickness)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_thickness); + + wmKeyMap *keymap_sculpt_strength = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Strength)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_strength); + + wmKeyMap *keymap_sculpt_grab = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Grab)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_grab); + + wmKeyMap *keymap_sculpt_push = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Push)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_push); + + wmKeyMap *keymap_sculpt_twist = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Twist)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_twist); + + wmKeyMap *keymap_sculpt_pinch = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Pinch)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_pinch); + + wmKeyMap *keymap_sculpt_randomize = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Randomize)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_randomize); + + wmKeyMap *keymap_sculpt_clone = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Sculpt (Clone)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt_clone); + + wmKeyMap *keymap_weight = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Weight Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_weight); + + wmKeyMap *keymap_weight_draw = WM_keymap_ensure( + wm->defaultconf, "Grease Pencil Stroke Weight (Draw)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_weight_draw); } } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index a840d199823..540a4c05247 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -555,7 +555,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); @@ -567,7 +567,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_annotation_data_get_active_direct((ID *)sc, sa, scene); if (gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); @@ -579,7 +579,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(sa, obact); if (gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe); @@ -609,7 +609,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpencil_layer_is_editable(gpl)) { + if (BKE_gpencil_layer_is_editable(gpl)) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } } @@ -625,7 +625,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpencil_layer_is_editable(gpl) && (gpl->actframe)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) { bGPDframe *gpf; bGPDstroke *gps; bGPDframe *init_gpf = gpl->actframe; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ee1cb09ae94..6fe1ba0b817 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -26,6 +26,9 @@ #include "BLI_math_vector.h" #include "BLI_string.h" +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + #include "DNA_customdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -33,9 +36,11 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "ED_paint.h" #include "ED_screen.h" @@ -294,6 +299,315 @@ static void PALETTE_OT_color_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* --- Extract Palette from Image. */ +static bool palette_extract_img_poll(bContext *C) +{ + SpaceLink *sl = CTX_wm_space_data(C); + if (sl->spacetype == SPACE_IMAGE) { + return true; + } + + return false; +} + +static int palette_extract_img_exec(bContext *C, wmOperator *op) +{ + const int threshold = RNA_int_get(op->ptr, "threshold"); + + Main *bmain = CTX_data_main(C); + bool done = false; + + SpaceImage *sima = CTX_wm_space_image(C); + Image *image = sima->image; + ImageUser iuser = sima->iuser; + void *lock; + ImBuf *ibuf; + GHash *color_table = BLI_ghash_int_new(__func__); + + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf->rect) { + /* Extract all colors. */ + for (int row = 0; row < ibuf->y; row++) { + for (int col = 0; col < ibuf->x; col++) { + float color[4]; + IMB_sampleImageAtLocation(ibuf, (float)col, (float)row, false, color); + const float range = pow(10.0f, threshold); + color[0] = truncf(color[0] * range) / range; + color[1] = truncf(color[1] * range) / range; + color[2] = truncf(color[2] * range) / range; + + uint key = rgb_to_cpack(color[0], color[1], color[2]); + if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) { + BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + + done = BKE_palette_from_hash(bmain, color_table, image->id.name + 2, false); + } + + /* Free memory. */ + BLI_ghash_free(color_table, NULL, NULL); + BKE_image_release_ibuf(image, ibuf, lock); + + if (done) { + BKE_reportf(op->reports, RPT_INFO, "Palette created"); + } + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_extract_from_image(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extract Palette from Image"; + ot->idname = "PALETTE_OT_extract_from_image"; + ot->description = "Extract all colors used in Image and create a Palette"; + + /* api callbacks */ + ot->exec = palette_extract_img_exec; + ot->poll = palette_extract_img_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4); +} + +/* Sort Palette color by Hue and Saturation. */ +static bool palette_sort_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + if (palette) { + return true; + } + + return false; +} + +static int palette_sort_exec(bContext *C, wmOperator *op) +{ + const int type = RNA_enum_get(op->ptr, "type"); + + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + + if (palette == NULL) { + return OPERATOR_CANCELLED; + } + + tPaletteColorHSV *color_array = NULL; + tPaletteColorHSV *col_elm = NULL; + + const int totcol = BLI_listbase_count(&palette->colors); + + if (totcol > 0) { + color_array = MEM_calloc_arrayN(totcol, sizeof(tPaletteColorHSV), __func__); + /* Put all colors in an array. */ + int t = 0; + for (PaletteColor *color = palette->colors.first; color; color = color->next) { + float h, s, v; + rgb_to_hsv(color->rgb[0], color->rgb[1], color->rgb[2], &h, &s, &v); + col_elm = &color_array[t]; + copy_v3_v3(col_elm->rgb, color->rgb); + col_elm->value = color->value; + col_elm->h = h; + col_elm->s = s; + col_elm->v = v; + t++; + } + /* Sort */ + if (type == 1) { + BKE_palette_sort_hsv(color_array, totcol); + } + else if (type == 2) { + BKE_palette_sort_svh(color_array, totcol); + } + else if (type == 3) { + BKE_palette_sort_vhs(color_array, totcol); + } + else { + BKE_palette_sort_luminance(color_array, totcol); + } + + /* Clear old color swatches. */ + PaletteColor *color_next = NULL; + for (PaletteColor *color = palette->colors.first; color; color = color_next) { + color_next = color->next; + BKE_palette_color_remove(palette, color); + } + + /* Recreate swatches sorted. */ + for (int i = 0; i < totcol; i++) { + col_elm = &color_array[i]; + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, col_elm->rgb); + } + } + } + + /* Free memory. */ + if (totcol > 0) { + MEM_SAFE_FREE(color_array); + } + + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_sort(wmOperatorType *ot) +{ + static const EnumPropertyItem sort_type[] = { + {1, "HSV", 0, "Hue, Saturation, Value", ""}, + {2, "SVH", 0, "Saturation, Value, Hue", ""}, + {3, "VHS", 0, "Value, Hue, Saturation", ""}, + {4, "LUMINANCE", 0, "Luminance", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Sort Palette"; + ot->idname = "PALETTE_OT_sort"; + ot->description = "Sort Palette Colors"; + + /* api callbacks */ + ot->exec = palette_sort_exec; + ot->poll = palette_sort_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", sort_type, 1, "Type", ""); +} + +/* Move colors in palette. */ +static int palette_color_move_exec(bContext *C, wmOperator *op) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *palcolor = BLI_findlink(&palette->colors, palette->active_color); + + if (palcolor == NULL) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "type"); + + BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ + if (BLI_listbase_link_move(&palette->colors, palcolor, direction)) { + palette->active_color += direction; + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_color_move(wmOperatorType *ot) +{ + static const EnumPropertyItem slot_move[] = { + {-1, "UP", 0, "Up", ""}, + {1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Palette Color"; + ot->idname = "PALETTE_OT_color_move"; + ot->description = "Move the active Color up/down in the list"; + + /* api callbacks */ + ot->exec = palette_color_move_exec; + ot->poll = palette_sort_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} + +/* Join Palette swatches. */ +static bool palette_join_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + if (palette) { + return true; + } + + return false; +} + +static int palette_join_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + Palette *palette_join = NULL; + bool done = false; + + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "palette", name); + + if ((palette == NULL) || (name[0] == '\0')) { + return OPERATOR_CANCELLED; + } + + palette_join = (Palette *)BKE_libblock_find_name(bmain, ID_PAL, name); + if (palette_join == NULL) { + return OPERATOR_CANCELLED; + } + + const int totcol = BLI_listbase_count(&palette_join->colors); + + if (totcol > 0) { + for (PaletteColor *color = palette_join->colors.first; color; color = color->next) { + PaletteColor *palcol = BKE_palette_color_add(palette); + if (palcol) { + copy_v3_v3(palcol->rgb, color->rgb); + palcol->value = color->value; + done = true; + } + } + } + + if (done) { + /* Clear old color swatches. */ + PaletteColor *color_next = NULL; + for (PaletteColor *color = palette_join->colors.first; color; color = color_next) { + color_next = color->next; + BKE_palette_color_remove(palette_join, color); + } + + /* Notifier. */ + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_join(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Join Palette Swatches"; + ot->idname = "PALETTE_OT_join"; + ot->description = "Join Palette Swatches"; + + /* api callbacks */ + ot->exec = palette_join_exec; + ot->poll = palette_join_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_string(ot->srna, "palette", NULL, MAX_ID_NAME - 2, "Palette", "Name of the Palette"); +} + static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op)) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -456,6 +770,9 @@ static const ePaintMode brush_select_paint_modes[] = { PAINT_MODE_WEIGHT, PAINT_MODE_TEXTURE_3D, PAINT_MODE_GPENCIL, + PAINT_MODE_VERTEX_GPENCIL, + PAINT_MODE_SCULPT_GPENCIL, + PAINT_MODE_WEIGHT_GPENCIL, }; static int brush_select_exec(bContext *C, wmOperator *op) @@ -965,6 +1282,11 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PALETTE_OT_color_add); WM_operatortype_append(PALETTE_OT_color_delete); + WM_operatortype_append(PALETTE_OT_extract_from_image); + WM_operatortype_append(PALETTE_OT_sort); + WM_operatortype_append(PALETTE_OT_color_move); + WM_operatortype_append(PALETTE_OT_join); + /* paint curve */ WM_operatortype_append(PAINTCURVE_OT_new); WM_operatortype_append(PAINTCURVE_OT_add_point); diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index e01986181bc..e17bf00106a 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -803,7 +803,7 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode) /* insert gp frames */ for (ale = anim_data.first; ale; ale = ale->next) { bGPDlayer *gpl = (bGPDlayer *)ale->data; - BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode); + BKE_gpencil_layer_frame_get(gpl, CFRA, add_frame_mode); } ANIM_animdata_update(ac, &anim_data); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 458c8690e3c..4322e4277ad 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1759,8 +1759,8 @@ static int mouse_action_keys(bAnimContext *ac, gpl->flag |= GP_LAYER_SELECT; /* Update other layer status. */ - if (BKE_gpencil_layer_getactive(gpd) != gpl) { - BKE_gpencil_layer_setactive(gpd, gpl); + if (BKE_gpencil_layer_active_get(gpd) != gpl) { + BKE_gpencil_layer_active_set(gpd, gpl); BKE_gpencil_layer_autolock_set(gpd, false); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 1c26dc8f834..3e88a7a7b88 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -817,7 +817,7 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) bGPDlayer *gpl = te->directdata; /* always make layer active */ - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); // XXX: name needs translation stuff BLI_uniquename( @@ -2252,6 +2252,9 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eGpencilModifierType_Armature: data.icon = ICON_MOD_ARMATURE; break; + case eGpencilModifierType_Vertexcolor: + data.icon = ICON_MOD_NORMALEDIT; + break; /* Default */ default: diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 6d449e98c7e..8ddacfc84c2 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -561,7 +561,7 @@ static eOLDrawState tree_element_active_gplayer(bContext *C, */ if (set != OL_SETSEL_NONE) { if (gpl) { - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd); } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a8514967748..8c884783913 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1215,6 +1215,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_WEIGHT_GPENCIL: ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); break; + case CTX_MODE_VERTEX_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); + break; default: break; } @@ -1232,6 +1235,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_EDIT_GPENCIL: ARRAY_SET_ITEMS(contexts, ".greasepencil_edit"); break; + case CTX_MODE_VERTEX_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); + break; default: break; } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 6fbae1d8cb1..83539900f36 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -409,7 +409,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, /* Helper: Find the layer created as ruler. */ static bGPDlayer *view3d_ruler_layer_get(bGPdata *gpd) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (gpl->flag & GP_LAYER_IS_RULER) { return gpl; } @@ -445,7 +445,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER; } - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); BKE_gpencil_free_strokes(gpf); for (ruler_item = gzgroup->gizmos.first; ruler_item; @@ -477,9 +477,10 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) } gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; - gps->gradient_f = 1.0f; - gps->gradient_s[0] = 1.0f; - gps->gradient_s[1] = 1.0f; + gps->hardeness = 1.0f; + gps->fill_opacity_fac = 1.0f; + copy_v2_fl(gps->aspect_ratio, 1.0f); + gps->uv_scale = 1.0f; BLI_addtail(&gpf->strokes, gps); changed = true; @@ -498,7 +499,7 @@ static bool view3d_ruler_from_gpencil(const bContext *C, wmGizmoGroup *gzgroup) gpl = view3d_ruler_layer_get(scene->gpd); if (gpl) { bGPDframe *gpf; - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_USE_PREV); + gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_USE_PREV); if (gpf) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 7649bd45a1a..bd92193206f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2265,7 +2265,8 @@ static bool ed_object_select_pick(bContext *C, if (ELEM(basact->object->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL)) { + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL)) { ED_gpencil_toggle_brush_cursor(C, true, NULL); } else { diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 009164057ce..f93a3ec260b 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -887,7 +887,7 @@ static void posttrans_gpd_clean(bGPdata *gpd) for (gpf = gpl->frames.first; gpf; gpf = gpfn) { gpfn = gpf->next; if (gpfn && gpf->framenum == gpfn->framenum) { - BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_frame_delete(gpl, gpf); } } } @@ -2740,9 +2740,11 @@ void createTransData(bContext *C, TransInfo *t) has_transform_context = false; } } - else if ((ob) && - (ELEM( - ob->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, OB_MODE_WEIGHT_GPENCIL))) { + else if ((ob) && (ELEM(ob->mode, + OB_MODE_PAINT_GPENCIL, + OB_MODE_SCULPT_GPENCIL, + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL))) { /* In grease pencil all transformations must be canceled if not Object or Edit. */ has_transform_context = false; } diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index c61961e46d1..17e69ff38b8 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -83,6 +83,8 @@ void createTransGPencil(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; + const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) || + (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS)); TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); @@ -108,7 +110,7 @@ void createTransGPencil(bContext *C, TransInfo *t) */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf; bGPDstroke *gps; bGPDframe *init_gpf = gpl->actframe; @@ -180,7 +182,7 @@ void createTransGPencil(bContext *C, TransInfo *t) /* Second Pass: Build transdata array */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene; bGPDframe *gpf = gpl->actframe; bGPDstroke *gps; @@ -196,11 +198,11 @@ void createTransGPencil(bContext *C, TransInfo *t) int f_end = 0; if (use_multiframe_falloff) { - BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end); } /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat); /* undo matrix */ invert_m4_m4(inverse_diff_mat, diff_mat); @@ -312,9 +314,9 @@ void createTransGPencil(bContext *C, TransInfo *t) } /* for other transform modes (e.g. shrink-fatten), need to additional data - * but never for scale or mirror + * but never for mirror */ - if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) { + if ((t->mode != TFM_MIRROR) && (is_scale_thickness)) { if (t->mode != TFM_GPENCIL_OPACITY) { td->val = &pt->pressure; td->ival = pt->pressure; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 3fe1b99adfb..bb4d50fcf54 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1149,12 +1149,15 @@ static void recalcData_sequencer(TransInfo *t) static void recalcData_gpencil_strokes(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + bGPDstroke *gps_prev = NULL; TransData *td = tc->data; for (int i = 0; i < tc->data_len; i++, td++) { bGPDstroke *gps = td->extra; - if (gps != NULL) { - gps->flag |= GP_STROKE_RECALC_GEOMETRY; + if ((gps != NULL) && (gps != gps_prev)) { + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + gps_prev = gps; } } } diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 02767156ef4..0548cc4e503 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -792,13 +792,13 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (is_gp_edit) { float diff_mat[4][4]; const bool use_mat_local = true; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { /* calculate difference matrix */ - ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat); + BKE_gpencil_parent_matrix_get(depsgraph, ob, gpl, diff_mat); for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 5b5d1338637..cb4f4dfa46c 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -202,7 +202,8 @@ static int ed_undo_step_impl( if (ELEM(obact->mode, OB_MODE_PAINT_GPENCIL, OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL)) { + OB_MODE_WEIGHT_GPENCIL, + OB_MODE_VERTEX_GPENCIL)) { ED_gpencil_toggle_brush_cursor(C, true, NULL); } else { diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 87743911add..709674e6b43 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/MOD_gpencilthick.c intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c + intern/MOD_gpencilvertexcolor.c MOD_gpencil_modifiertypes.h ) diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index f5c064c1c07..b8e8e8fc87f 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -43,6 +43,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Offset; extern GpencilModifierTypeInfo modifierType_Gpencil_Armature; extern GpencilModifierTypeInfo modifierType_Gpencil_Time; extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply; +extern GpencilModifierTypeInfo modifierType_Gpencil_Vertexcolor; /* MOD_gpencil_util.c */ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 6f37cedba49..1462b6f72c9 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -72,6 +72,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Armature); INIT_GP_TYPE(Time); INIT_GP_TYPE(Multiply); + INIT_GP_TYPE(Vertexcolor); #undef INIT_GP_TYPE } @@ -186,44 +187,3 @@ float get_modifier_point_weight(MDeformVert *dvert, bool inverse, int def_nr) return weight; } - -/* set material when apply modifiers (used in tint and color modifier) */ -void gpencil_apply_modifier_material( - Main *bmain, Object *ob, Material *mat, GHash *gh_color, bGPDstroke *gps, bool crt_material) -{ - MaterialGPencilStyle *gp_style = mat->gp_style; - - /* look for color */ - if (crt_material) { - Material *newmat = BLI_ghash_lookup(gh_color, mat->id.name); - if (newmat == NULL) { - BKE_object_material_slot_add(bmain, ob); - newmat = BKE_material_copy(bmain, mat); - newmat->preview = NULL; - - BKE_object_material_assign(bmain, ob, newmat, ob->totcol, BKE_MAT_ASSIGN_USERPREF); - - copy_v4_v4(newmat->gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); - copy_v4_v4(newmat->gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); - - BLI_ghash_insert(gh_color, mat->id.name, newmat); - DEG_id_tag_update(&newmat->id, ID_RECALC_COPY_ON_WRITE); - } - /* Reassign color index. */ - gps->mat_nr = BKE_gpencil_object_material_get_index(ob, newmat); - } - else { - /* reuse existing color (but update only first time) */ - if (BLI_ghash_lookup(gh_color, mat->id.name) == NULL) { - copy_v4_v4(gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); - copy_v4_v4(gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); - BLI_ghash_insert(gh_color, mat->id.name, mat); - } - /* update previews (icon and thumbnail) */ - if (mat->preview != NULL) { - mat->preview->flag[ICON_SIZE_ICON] |= PRV_CHANGED; - mat->preview->flag[ICON_SIZE_PREVIEW] |= PRV_CHANGED; - } - DEG_id_tag_update(&mat->id, ID_RECALC_COPY_ON_WRITE); - } -} diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h index 2b1f8dbc71a..fc4522bc028 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -47,11 +47,4 @@ bool is_stroke_affected_by_modifier(struct Object *ob, float get_modifier_point_weight(struct MDeformVert *dvert, bool inverse, int def_nr); -void gpencil_apply_modifier_material(struct Main *bmain, - struct Object *ob, - struct Material *mat, - struct GHash *gh_color, - struct bGPDstroke *gps, - bool crt_material); - #endif /* __MOD_GPENCIL_UTIL_H__ */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c index eceb45780cf..a8aad763422 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -116,6 +117,8 @@ static void deformStroke(GpencilModifierData *md, } gpencil_deform_verts(mmd, ob, gps); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) @@ -131,8 +134,8 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData return; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* apply armature effects on this frame * NOTE: this assumes that we don't want armature animation on non-keyframed frames */ @@ -140,7 +143,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData BKE_scene_graph_update_for_newframe(depsgraph, bmain); /* compute armature effects on this frame */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md_eval, depsgraph, object_eval, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index 11ba639fa83..1fc8c19b542 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -27,6 +27,10 @@ #include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_hash.h" +#include "BLI_rand.h" + #include "BLI_blenlib.h" #include "BLI_rand.h" #include "BLI_math.h" @@ -54,26 +58,24 @@ #include "MOD_gpencil_util.h" #include "MOD_gpencil_modifiertypes.h" +typedef struct tmpStrokes { + struct tmpStrokes *next, *prev; + bGPDframe *gpf; + bGPDstroke *gps; +} tmpStrokes; + static void initData(GpencilModifierData *md) { ArrayGpencilModifierData *gpmd = (ArrayGpencilModifierData *)md; gpmd->count = 2; - gpmd->offset[0] = 1.0f; - gpmd->offset[1] = 0.0f; - gpmd->offset[2] = 0.0f; - gpmd->shift[0] = 0.0f; + gpmd->shift[0] = 1.0f; gpmd->shift[1] = 0.0f; gpmd->shift[2] = 0.0f; - gpmd->scale[0] = 1.0f; - gpmd->scale[1] = 1.0f; - gpmd->scale[2] = 1.0f; - gpmd->rnd_rot = 0.5f; - gpmd->rnd_size = 0.5f; + zero_v3(gpmd->offset); + zero_v3(gpmd->rnd_scale); gpmd->object = NULL; - - /* fill random values */ - BLI_array_frand(gpmd->rnd, 20, 1); - gpmd->rnd[0] = 1; + gpmd->flag |= GP_ARRAY_USE_RELATIVE; + gpmd->seed = 1; } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) @@ -90,46 +92,24 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob, float r_offset[4][4]) { float offset[3], rot[3], scale[3]; - int ri = mmd->rnd[0]; - float factor; - - offset[0] = mmd->offset[0] * elem_idx; - offset[1] = mmd->offset[1] * elem_idx; - offset[2] = mmd->offset[2] * elem_idx; - - /* rotation */ - if (mmd->flag & GP_ARRAY_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); - } + ARRAY_SET_ITEMS(scale, 1.0f, 1.0f, 1.0f); + zero_v3(rot); - /* scale */ - if (mmd->flag & GP_ARRAY_RANDOM_SIZE) { - factor = mmd->rnd_size * mmd->rnd[ri]; - mul_v3_v3fl(scale, mmd->scale, factor); - add_v3_v3(scale, mmd->scale); + if (mmd->flag & GP_ARRAY_USE_OFFSET) { + offset[0] = mmd->offset[0] * elem_idx; + offset[1] = mmd->offset[1] * elem_idx; + offset[2] = mmd->offset[2] * elem_idx; } else { - copy_v3_v3(scale, mmd->scale); + zero_v3(offset); } - /* advance random index */ - mmd->rnd[0]++; - if (mmd->rnd[0] > 19) { - mmd->rnd[0] = 1; - } - - /* calculate matrix */ + /* Calculate matrix */ loc_eul_size_to_mat4(r_mat, offset, rot, scale); - copy_m4_m4(r_offset, r_mat); /* offset object */ - if (mmd->object) { + if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) { float mat_offset[4][4]; float obinv[4][4]; @@ -147,140 +127,158 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob, /* array modifier - generate geometry callback (for viewport/rendering) */ static void generate_geometry(GpencilModifierData *md, - Depsgraph *UNUSED(depsgraph), - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf) + Depsgraph *depsgraph, + Scene *scene, + Object *ob) { ArrayGpencilModifierData *mmd = (ArrayGpencilModifierData *)md; ListBase stroke_cache = {NULL, NULL}; - bGPDstroke *gps; - int idx; - - /* Check which strokes we can use once, and store those results in an array - * for quicker checking of what's valid (since string comparisons are expensive) - */ - const int num_strokes = BLI_listbase_count(&gpf->strokes); - int num_valid = 0; + /* Load the strokes to be duplicated. */ + bGPdata *gpd = (bGPdata *)ob->data; + bool found = false; + + /* Get bounbox for relative offset. */ + float size[3] = {0.0f, 0.0f, 0.0f}; + if (mmd->flag & GP_ARRAY_USE_RELATIVE) { + BoundBox *bb = BKE_object_boundbox_get(ob); + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + BKE_boundbox_init_from_minmax(bb, min, max); + BKE_boundbox_calc_size_aabb(bb, size); + mul_v3_fl(size, 2.0f); + /* Need a minimum size (for flat drawings). */ + CLAMP3_MIN(size, 0.01f); + } - bool *valid_strokes = MEM_callocN(sizeof(bool) * num_strokes, __func__); + int seed = mmd->seed; + /* Make sure different modifiers get different seeds. */ + seed += BLI_hash_string(ob->id.name + 2); + seed += BLI_hash_string(md->name); - for (gps = gpf->strokes.first, idx = 0; gps; gps = gps->next, idx++) { - /* Record whether this stroke can be used - * ATTENTION: The logic here is the inverse of what's used everywhere else! - */ - if (is_stroke_affected_by_modifier(ob, - mmd->layername, - mmd->materialname, - mmd->pass_index, - mmd->layer_pass, - 1, - gpl, - gps, - mmd->flag & GP_ARRAY_INVERT_LAYER, - mmd->flag & GP_ARRAY_INVERT_PASS, - mmd->flag & GP_ARRAY_INVERT_LAYERPASS, - mmd->flag & GP_ARRAY_INVERT_MATERIAL)) { - valid_strokes[idx] = true; - num_valid++; + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->materialname, + mmd->pass_index, + mmd->layer_pass, + 1, + gpl, + gps, + mmd->flag & GP_ARRAY_INVERT_LAYER, + mmd->flag & GP_ARRAY_INVERT_PASS, + mmd->flag & GP_ARRAY_INVERT_LAYERPASS, + mmd->flag & GP_ARRAY_INVERT_MATERIAL)) { + tmpStrokes *tmp = MEM_callocN(sizeof(tmpStrokes), __func__); + tmp->gpf = gpf; + tmp->gps = gps; + BLI_addtail(&stroke_cache, tmp); + + found = true; + } } } - /* Early exit if no strokes can be copied */ - if (num_valid == 0) { - if (G.debug & G_DEBUG) { - printf("GP Array Mod - No strokes to be included\n"); - } + if (found) { + /* Generate new instances of all existing strokes, + * keeping each instance together so they maintain + * the correct ordering relative to each other + */ + float current_offset[4][4]; + unit_m4(current_offset); - MEM_SAFE_FREE(valid_strokes); - return; - } + float rand_offset = BLI_hash_int_01(seed); - /* Generate new instances of all existing strokes, - * keeping each instance together so they maintain - * the correct ordering relative to each other - */ - float current_offset[4][4]; - unit_m4(current_offset); + for (int x = 0; x < mmd->count; x++) { + /* original strokes are at index = 0 */ + if (x == 0) { + continue; + } - for (int x = 0; x < mmd->count; x++) { - /* original strokes are at index = 0 */ - if (x == 0) { - continue; - } + /* Compute transforms for this instance */ + float mat[4][4]; + float mat_offset[4][4]; + BKE_gpencil_instance_modifier_instance_tfm(ob, mmd, x, mat, mat_offset); - /* Compute transforms for this instance */ - float mat[4][4]; - float mat_offset[4][4]; - BKE_gpencil_instance_modifier_instance_tfm(ob, mmd, x, mat, mat_offset); + if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) { + /* recalculate cumulative offset here */ + mul_m4_m4m4(current_offset, current_offset, mat_offset); + } + else { + copy_m4_m4(current_offset, mat); + } - if (mmd->object) { - /* recalculate cumulative offset here */ - mul_m4_m4m4(current_offset, current_offset, mat_offset); - } - else { - copy_m4_m4(current_offset, mat); - } - /* apply shift */ - madd_v3_v3fl(current_offset[3], mmd->shift, x); - - /* Duplicate original strokes to create this instance */ - for (gps = gpf->strokes.first, idx = 0; gps; gps = gps->next, idx++) { - /* check if stroke can be duplicated */ - if (valid_strokes[idx]) { - /* Calculate original stroke center (only first loop). */ - float r_min[3], r_max[3], center[3]; - if (x == 1) { - INIT_MINMAX(r_min, r_max); - BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); - add_v3_v3v3(center, r_min, r_max); - mul_v3_fl(center, 0.5f); - sub_v3_v3v3(center, center, ob->obmat[3]); - } + /* Apply relative offset. */ + if (mmd->flag & GP_ARRAY_USE_RELATIVE) { + float relative[3]; + mul_v3_v3v3(relative, mmd->shift, size); + madd_v3_v3fl(current_offset[3], relative, x); + } + float rand[3][3]; + for (int j = 0; j < 3; j++) { + uint primes[3] = {2, 3, 7}; + double offset[3] = {0.0, 0.0, 0.0}; + double r[3]; + /* To ensure a nice distribution, we use halton sequence and offset using the seed. */ + BLI_halton_3d(primes, offset, x, r); + + for (int i = 0; i < 3; i++) { + rand[j][i] = fmodf(r[i] * 2.0 - 1.0 + rand_offset, 1.0f); + rand[j][i] = fmodf(sin(rand[j][i] * 12.9898 + j * 78.233) * 43758.5453, 1.0f); + } + } + /* Calculate Random matrix. */ + float mat_rnd[4][4]; + float loc[3], rot[3]; + float scale[3] = {1.0f, 1.0f, 1.0f}; + mul_v3_v3v3(loc, mmd->rnd_offset, rand[0]); + mul_v3_v3v3(rot, mmd->rnd_rot, rand[1]); + madd_v3_v3v3(scale, mmd->rnd_scale, rand[2]); + + loc_eul_size_to_mat4(mat_rnd, loc, rot, scale); + + /* Duplicate original strokes to create this instance. */ + LISTBASE_FOREACH_BACKWARD (tmpStrokes *, iter, &stroke_cache) { /* Duplicate stroke */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(iter->gps, true); /* Move points */ - for (int i = 0; i < gps->totpoints; i++) { + for (int i = 0; i < iter->gps->totpoints; i++) { bGPDspoint *pt = &gps_dst->points[i]; + /* Apply randomness matrix. */ + mul_m4_v3(mat_rnd, &pt->x); + /* Apply object local transform (Rot/Scale). */ - if (mmd->object) { + if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) { mul_m4_v3(mat, &pt->x); } - /* Translate to object origin. */ - float fpt[3]; - sub_v3_v3v3(fpt, &pt->x, center); /* Global Rotate and scale. */ - mul_mat3_m4_v3(current_offset, fpt); + mul_mat3_m4_v3(current_offset, &pt->x); /* Global translate. */ - add_v3_v3(fpt, center); - add_v3_v3v3(&pt->x, fpt, current_offset[3]); + add_v3_v3(&pt->x, current_offset[3]); } - /* if replace material, use new one */ + /* If replace material, use new one. */ if ((mmd->mat_rpl > 0) && (mmd->mat_rpl <= ob->totcol)) { gps_dst->mat_nr = mmd->mat_rpl - 1; } - /* Add new stroke to cache, to be added to the frame once - * all duplicates have been made - */ - BLI_addtail(&stroke_cache, gps_dst); + /* Add new stroke. */ + BLI_addhead(&iter->gpf->strokes, gps_dst); + /* Calc bounding box. */ + BKE_gpencil_stroke_boundingbox_calc(gps_dst); } } - } - /* merge newly created stroke instances back into the main stroke list */ - if (mmd->flag & GP_ARRAY_KEEP_ONTOP) { - BLI_movelisttolist_reverse(&gpf->strokes, &stroke_cache); - } - else { - BLI_movelisttolist(&gpf->strokes, &stroke_cache); + /* Free temp data. */ + LISTBASE_FOREACH_MUTABLE (tmpStrokes *, tmp, &stroke_cache) { + BLI_freelinkN(&stroke_cache, tmp); + } } - - /* free temp data */ - MEM_SAFE_FREE(valid_strokes); } static void bakeModifier(Main *UNUSED(bmain), @@ -288,23 +286,17 @@ static void bakeModifier(Main *UNUSED(bmain), GpencilModifierData *md, Object *ob) { - - bGPdata *gpd = ob->data; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - generate_geometry(md, depsgraph, ob, gpl, gpf); - } - } + Scene *scene = DEG_get_evaluated_scene(depsgraph); + generate_geometry(md, depsgraph, scene, ob); } /* -------------------------------- */ /* Generic "generateStrokes" callback */ -static void generateStrokes( - GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) { - generate_geometry(md, depsgraph, ob, gpl, gpf); + Scene *scene = DEG_get_evaluated_scene(depsgraph); + generate_geometry(md, depsgraph, scene, ob); } static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index e3e7168330d..7e89d8e2d6f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -174,10 +174,8 @@ static void reduce_stroke_points(bGPDstroke *gps, gps->dvert = new_dvert; gps->totpoints = num_points; - /* mark stroke as needing to have its geometry caches rebuilt */ - gps->flag |= GP_STROKE_RECALC_GEOMETRY; - gps->tot_triangles = 0; - MEM_SAFE_FREE(gps->triangles); + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* --------------------------------------------- */ @@ -400,19 +398,15 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa } /* --------------------------------------------- */ - -/* Entry-point for Build Modifier */ -static void generateStrokes(GpencilModifierData *md, - Depsgraph *depsgraph, - Object *UNUSED(ob), - bGPDlayer *gpl, - bGPDframe *gpf) +static void generate_geometry(GpencilModifierData *md, + Depsgraph *depsgraph, + bGPDlayer *gpl, + bGPDframe *gpf) { BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md; const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW); const float ctime = DEG_get_ctime(depsgraph); - // printf("GP Build Modifier - %f\n", ctime); /* Early exit if it's an empty frame */ if (gpf->strokes.first == NULL) { @@ -459,7 +453,7 @@ static void generateStrokes(GpencilModifierData *md, * By default, the upper bound is given by the "maximum length" setting */ float start_frame = gpf->framenum + mmd->start_delay; - float end_frame = gpf->framenum + mmd->length; + float end_frame = start_frame + mmd->length; if (gpf->next) { /* Use the next frame or upper bound as end frame, whichever is lower/closer */ @@ -506,7 +500,6 @@ static void generateStrokes(GpencilModifierData *md, /* Determine how far along we are between the keyframes */ float fac = (ctime - start_frame) / (end_frame - start_frame); - // printf(" Progress on %d = %f (%f - %f)\n", gpf->framenum, fac, start_frame, end_frame); /* Time management mode */ switch (mmd->mode) { @@ -526,6 +519,21 @@ static void generateStrokes(GpencilModifierData *md, } } +/* Entry-point for Build Modifier */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + generate_geometry(md, depsgraph, gpl, gpf); + } +} + /* ******************************************** */ GpencilModifierTypeInfo modifierType_Gpencil_Build = { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c index d9869c92e06..3a930b40c8b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -26,7 +26,6 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math_color.h" #include "BLI_math_vector.h" @@ -35,6 +34,7 @@ #include "DNA_gpencil_types.h" #include "DNA_gpencil_modifier_types.h" +#include "BKE_colortools.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_main.h" @@ -52,13 +52,28 @@ static void initData(GpencilModifierData *md) ARRAY_SET_ITEMS(gpmd->hsv, 0.5f, 1.0f, 1.0f); gpmd->layername[0] = '\0'; gpmd->materialname[0] = '\0'; - gpmd->flag |= GP_COLOR_CREATE_COLORS; gpmd->modify_color = GP_MODIFY_COLOR_BOTH; + + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemapping_initialize(curve); + } } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + ColorGpencilModifierData *gmd = (ColorGpencilModifierData *)md; + ColorGpencilModifierData *tgmd = (ColorGpencilModifierData *)target; + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } /* color correction strokes */ @@ -72,6 +87,7 @@ static void deformStroke(GpencilModifierData *md, ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; float hsv[3], factor[3]; + const bool use_curve = (mmd->flag & GP_COLOR_CUSTOM_CURVE) != 0 && mmd->curve_intensity; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -89,60 +105,76 @@ static void deformStroke(GpencilModifierData *md, } copy_v3_v3(factor, mmd->hsv); - /* keep initial values unchanged, subtracting the default values. */ - factor[0] -= 0.5f; - factor[1] -= 1.0f; - factor[2] -= 1.0f; + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { - rgb_to_hsv_v(gps->runtime.tmp_stroke_rgba, hsv); - add_v3_v3(hsv, factor); - CLAMP3(hsv, 0.0f, 1.0f); - hsv_to_rgb_v(hsv, gps->runtime.tmp_stroke_rgba); + /* Apply to Vertex Color. */ + /* Fill */ + if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (gps->vert_color_fill[3] == 0.0f) && + (gp_style->fill_rgba[3] > 0.0f)) { + copy_v4_v4(gps->vert_color_fill, gp_style->fill_rgba); + gps->vert_color_fill[3] = 1.0f; + } + + rgb_to_hsv_v(gps->vert_color_fill, hsv); + hsv[0] = fractf(hsv[0] + factor[0] + 0.5f); + hsv[1] = clamp_f(hsv[1] * factor[1], 0.0f, 1.0f); + hsv[2] = hsv[2] * factor[2]; + hsv_to_rgb_v(hsv, gps->vert_color_fill); } - if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { - rgb_to_hsv_v(gps->runtime.tmp_fill_rgba, hsv); - add_v3_v3(hsv, factor); - CLAMP3(hsv, 0.0f, 1.0f); - hsv_to_rgb_v(hsv, gps->runtime.tmp_fill_rgba); + /* Stroke */ + if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (pt->vert_color[3] == 0.0f) && (gp_style->stroke_rgba[3] > 0.0f)) { + copy_v4_v4(pt->vert_color, gp_style->stroke_rgba); + pt->vert_color[3] = 1.0f; + } + + /* Custom curve to modulate value. */ + float factor_value[3]; + copy_v3_v3(factor_value, factor); + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + float mixfac = BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); + mul_v3_fl(factor_value, mixfac); + } + + rgb_to_hsv_v(pt->vert_color, hsv); + hsv[0] = fractf(hsv[0] + factor_value[0] + 0.5f); + hsv[1] = clamp_f(hsv[1] * factor_value[1], 0.0f, 1.0f); + hsv[2] = hsv[2] * factor_value[2]; + hsv_to_rgb_v(hsv, pt->vert_color); + } } } -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { - ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; bGPdata *gpd = ob->data; - GHash *gh_color = BLI_ghash_str_new("GP_Color modifier"); - 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) { - - Material *mat = BKE_gpencil_material(ob, gps->mat_nr + 1); - if (mat == NULL) { - continue; - } - MaterialGPencilStyle *gp_style = mat->gp_style; - /* skip stroke if it doesn't have color info */ - if (ELEM(NULL, gp_style)) { - continue; - } - - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); - - gpencil_apply_modifier_material( - bmain, ob, mat, gh_color, gps, (bool)(mmd->flag & GP_COLOR_CREATE_COLORS)); } } } - /* free hash buffers */ - if (gh_color) { - BLI_ghash_free(gh_color, NULL, NULL); - gh_color = NULL; +} + +static void freeData(GpencilModifierData *md) +{ + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + BKE_curvemapping_free(gpmd->curve_intensity); } } @@ -161,7 +193,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Color = { /* remapTime */ NULL, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c index ae51bad3ca3..41380ec4c68 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -263,6 +264,8 @@ static void deformStroke(GpencilModifierData *md, } gp_hook_co_apply(&tData, weight, pt); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* FIXME: Ideally we be doing this on a copy of the main depsgraph @@ -279,8 +282,8 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData return; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* apply hook effects on this frame * NOTE: this assumes that we don't want hook animation on non-keyframed frames */ @@ -288,7 +291,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData BKE_scene_graph_update_for_newframe(depsgraph, bmain); /* compute hook effects on this frame */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c index dba554fec36..d87f3a8a199 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" @@ -108,6 +109,8 @@ static void deformStroke(GpencilModifierData *md, } calc_latt_deform((struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } /* FIXME: Ideally we be doing this on a copy of the main depsgraph @@ -125,8 +128,8 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData return; } - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* apply lattice effects on this frame * NOTE: this assumes that we don't want lattice animation on non-keyframed frames */ @@ -137,7 +140,7 @@ static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData BKE_gpencil_lattice_init(ob); /* compute lattice effects on this frame */ - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c index 42d45512dc5..0f9b1034352 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -113,12 +113,7 @@ static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstro } } -/* Generic "generateStrokes" callback */ -static void generateStrokes(GpencilModifierData *md, - Depsgraph *UNUSED(depsgraph), - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf) +static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) { MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; bGPDstroke *gps, *gps_new = NULL; @@ -145,7 +140,7 @@ static void generateStrokes(GpencilModifierData *md, mmd->flag & GP_MIRROR_INVERT_PASS, mmd->flag & GP_MIRROR_INVERT_LAYERPASS, mmd->flag & GP_MIRROR_INVERT_MATERIAL)) { - gps_new = BKE_gpencil_stroke_duplicate(gps); + gps_new = BKE_gpencil_stroke_duplicate(gps, true); update_position(ob, mmd, gps_new, xi); BLI_addtail(&gpf->strokes, gps_new); } @@ -154,20 +149,35 @@ static void generateStrokes(GpencilModifierData *md, } } +/* Generic "generateStrokes" callback */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + generate_geometry(md, ob, gpl, gpf); + } +} + static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) { Scene *scene = DEG_get_evaluated_scene(depsgraph); bGPdata *gpd = ob->data; int oldframe = (int)DEG_get_ctime(depsgraph); - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { /* apply mirror effects on this frame */ CFRA = gpf->framenum; BKE_scene_graph_update_for_newframe(depsgraph, bmain); /* compute mirror effects on this frame */ - generateStrokes(md, depsgraph, ob, gpl, gpf); + generate_geometry(md, ob, gpl, gpf); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c index 919dbf91862..16b0c4a5e38 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c @@ -66,7 +66,10 @@ static void initData(GpencilModifierData *md) MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md; mmd->duplications = 3; mmd->distance = 0.1f; - mmd->split_angle = 1.0f; + mmd->split_angle = DEG2RADF(1.0f); + mmd->fading_center = 0.5f; + mmd->fading_thickness = 0.5f; + mmd->fading_opacity = 0.5f; } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) @@ -74,60 +77,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) BKE_gpencil_modifier_copyData_generic(md, target); } -static void splitStroke(bGPDframe *gpf, bGPDstroke *gps, float split_angle) -{ - bGPDspoint *pt = gps->points; - bGPDstroke *new_gps = gps; - int i; - volatile float angle; - - if (split_angle <= FLT_EPSILON) { - return; - } - - for (i = 1; i < new_gps->totpoints - 1; i++) { - angle = angle_v3v3v3(&pt[i - 1].x, &pt[i].x, &pt[i + 1].x); - if (angle < split_angle) { - if (BKE_gpencil_split_stroke(gpf, new_gps, i, &new_gps)) { - pt = new_gps->points; - i = 0; - continue; /* then i == 1 again */ - } - } - } -} - static void minter_v3_v3v3v3_ref( - float *result, float *left, float *middle, float *right, float *stroke_normal) + float *result, float *prev, float *curr, float *next, float *stroke_normal) { - float left_arm[3], right_arm[3], inter1[3], inter2[3]; + float vec[3], inter1[3], inter2[3]; + ARRAY_SET_ITEMS(inter1, 0.0f, 0.0f, 0.0f); + ARRAY_SET_ITEMS(inter2, 0.0f, 0.0f, 0.0f); + float minter[3]; - if (left) { - sub_v3_v3v3(left_arm, middle, left); - cross_v3_v3v3(inter1, stroke_normal, left_arm); + if (prev) { + sub_v3_v3v3(vec, curr, prev); + cross_v3_v3v3(inter1, stroke_normal, vec); } - if (right) { - sub_v3_v3v3(right_arm, right, middle); - cross_v3_v3v3(inter2, stroke_normal, right_arm); + if (next) { + sub_v3_v3v3(vec, next, curr); + cross_v3_v3v3(inter2, stroke_normal, vec); } - if (!left) { + if (!prev) { normalize_v3(inter2); copy_v3_v3(result, inter2); return; } - - if (!right) { + if (!next) { normalize_v3(inter1); copy_v3_v3(result, inter1); return; } - interp_v3_v3v3(minter, inter1, inter2, 0.5); normalize_v3(minter); copy_v3_v3(result, minter); } -static void duplicateStroke(bGPDstroke *gps, +static void duplicateStroke(Object *ob, + bGPDstroke *gps, int count, float dist, float offset, @@ -138,14 +120,15 @@ static void duplicateStroke(bGPDstroke *gps, float fading_opacity) { int i; - bGPDstroke *new_gps; + bGPDstroke *new_gps = NULL; float stroke_normal[3]; - float minter[3]; bGPDspoint *pt; - float offset_factor; float thickness_factor; float opacity_factor; + /* Apply object scale to offset distance. */ + offset *= mat4_to_scale(ob->obmat); + BKE_gpencil_stroke_normal(gps, stroke_normal); if (len_v3(stroke_normal) < FLT_EPSILON) { add_v3_fl(stroke_normal, 1); @@ -160,6 +143,7 @@ static void duplicateStroke(bGPDstroke *gps, pt = gps->points; for (int j = 0; j < gps->totpoints; j++) { + float minter[3]; if (j == 0) { minter_v3_v3v3v3_ref(minter, NULL, &pt[j].x, &pt[j + 1].x, stroke_normal); } @@ -174,11 +158,11 @@ static void duplicateStroke(bGPDstroke *gps, sub_v3_v3v3(&t2_array[j * 3], &pt[j].x, minter); } - /* This ensures the original stroke is the last one to be processed. */ + /* This ensures the original stroke is the last one + * to be processed, since we duplicate its data. */ for (i = count - 1; i >= 0; i--) { if (i != 0) { - new_gps = BKE_gpencil_stroke_duplicate(gps); - new_gps->flag |= GP_STROKE_RECALC_GEOMETRY; + new_gps = BKE_gpencil_stroke_duplicate(gps, true); BLI_addtail(results, new_gps); } else { @@ -187,34 +171,26 @@ static void duplicateStroke(bGPDstroke *gps, pt = new_gps->points; - if (count == 1) { - offset_factor = 0; - } - else { - offset_factor = (float)i / (float)(count - 1); - } + float offset_fac = (count == 1) ? 0.5f : (i / (float)(count - 1)); if (fading) { - thickness_factor = (offset_factor > fading_center) ? - (interpf(1 - fading_thickness, 1.0f, offset_factor - fading_center)) : - (interpf( - 1.0f, 1 - fading_thickness, offset_factor - fading_center + 1)); - opacity_factor = (offset_factor > fading_center) ? - (interpf(1 - fading_opacity, 1.0f, offset_factor - fading_center)) : - (interpf(1.0f, 1 - fading_opacity, offset_factor - fading_center + 1)); + thickness_factor = interpf(1.0f - fading_thickness, 1.0f, fabsf(offset_fac - fading_center)); + opacity_factor = interpf(1.0f - fading_opacity, 1.0f, fabsf(offset_fac - fading_center)); } for (int j = 0; j < new_gps->totpoints; j++) { - interp_v3_v3v3(&pt[j].x, - &t1_array[j * 3], - &t2_array[j * 3], - interpf(1 + offset, offset, offset_factor)); + float fac = interpf(1 + offset, offset, offset_fac); + interp_v3_v3v3(&pt[j].x, &t1_array[j * 3], &t2_array[j * 3], fac); if (fading) { pt[j].pressure = gps->points[j].pressure * thickness_factor; pt[j].strength = gps->points[j].strength * opacity_factor; } } } + /* Calc geometry data. */ + if (new_gps != NULL) { + BKE_gpencil_stroke_geometry_update(new_gps); + } MEM_freeN(t1_array); MEM_freeN(t2_array); } @@ -224,11 +200,10 @@ static void bakeModifier(Main *UNUSED(bmain), GpencilModifierData *md, Object *ob) { - bGPdata *gpd = ob->data; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { ListBase duplicates = {0}; MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md; bGPDstroke *gps; @@ -247,11 +222,9 @@ static void bakeModifier(Main *UNUSED(bmain), mmd->flag & GP_MIRROR_INVERT_MATERIAL)) { continue; } - if (mmd->flags & GP_MULTIPLY_ENABLE_ANGLE_SPLITTING) { - splitStroke(gpf, gps, mmd->split_angle); - } if (mmd->duplications > 0) { - duplicateStroke(gps, + duplicateStroke(ob, + gps, mmd->duplications, mmd->distance, mmd->offset, @@ -262,23 +235,15 @@ static void bakeModifier(Main *UNUSED(bmain), mmd->fading_opacity); } } - if (duplicates.first) { - ((bGPDstroke *)gpf->strokes.last)->next = duplicates.first; - ((bGPDstroke *)duplicates.first)->prev = gpf->strokes.last; - gpf->strokes.last = duplicates.first; + if (!BLI_listbase_is_empty(&duplicates)) { + BLI_movelisttolist(&gpf->strokes, &duplicates); } } } } /* -------------------------------- */ - -/* Generic "generateStrokes" callback */ -static void generateStrokes(GpencilModifierData *md, - Depsgraph *UNUSED(depsgraph), - Object *ob, - bGPDlayer *gpl, - bGPDframe *gpf) +static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) { MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)md; bGPDstroke *gps; @@ -298,11 +263,9 @@ static void generateStrokes(GpencilModifierData *md, mmd->flag & GP_MIRROR_INVERT_MATERIAL)) { continue; } - if (mmd->flags & GP_MULTIPLY_ENABLE_ANGLE_SPLITTING) { - splitStroke(gpf, gps, mmd->split_angle); - } if (mmd->duplications > 0) { - duplicateStroke(gps, + duplicateStroke(ob, + gps, mmd->duplications, mmd->distance, mmd->offset, @@ -313,10 +276,23 @@ static void generateStrokes(GpencilModifierData *md, mmd->fading_opacity); } } - if (duplicates.first) { - ((bGPDstroke *)gpf->strokes.last)->next = duplicates.first; - ((bGPDstroke *)duplicates.first)->prev = gpf->strokes.last; - gpf->strokes.last = duplicates.first; + if (!BLI_listbase_is_empty(&duplicates)) { + BLI_movelisttolist(&gpf->strokes, &duplicates); + } +} + +/* Generic "generateStrokes" callback */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl); + if (gpf == NULL) { + continue; + } + generate_geometry(md, ob, gpl, gpf); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 157bd536609..9b3d37bf11f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -23,18 +23,23 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_rand.h" +#include "MEM_guardedalloc.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_colortools.h" #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" @@ -57,13 +62,38 @@ static void initData(GpencilModifierData *md) gpmd->layername[0] = '\0'; gpmd->materialname[0] = '\0'; gpmd->vgname[0] = '\0'; - gpmd->step = 1; - gpmd->seed = 0; + gpmd->step = 4; + gpmd->seed = 1; + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemap_reset(curve->cm, &curve->clipr, CURVE_PRESET_BELL, CURVEMAP_SLOPE_POSITIVE); + BKE_curvemapping_initialize(curve); + } +} + +static void freeData(GpencilModifierData *md) +{ + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + BKE_curvemapping_free(gpmd->curve_intensity); + } } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + NoiseGpencilModifierData *gmd = (NoiseGpencilModifierData *)md; + NoiseGpencilModifierData *tgmd = (NoiseGpencilModifierData *)target; + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } static bool dependsOnTime(GpencilModifierData *md) @@ -72,24 +102,36 @@ static bool dependsOnTime(GpencilModifierData *md) return (mmd->flag & GP_NOISE_USE_RANDOM) != 0; } +static float *noise_table(int len, int seed) +{ + float *table = MEM_callocN(sizeof(float) * len, __func__); + for (int i = 0; i < len; i++) { + table[i] = BLI_hash_int_01(BLI_hash_int_2d(seed, i + 1)); + } + return table; +} + +BLI_INLINE float table_sample(float *table, float x) +{ + return interpf(table[(int)ceilf(x)], table[(int)floor(x)], fractf(x)); +} + /* aply noise effect based on stroke direction */ static void deformStroke(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, - bGPDframe *UNUSED(gpf), + bGPDframe *gpf, bGPDstroke *gps) { NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; - bGPDspoint *pt0, *pt1; MDeformVert *dvert = NULL; - float shift, vran, vdir; + /* Noise value in range [-1..1] */ float normal[3]; float vec1[3], vec2[3]; - int sc_frame = 0; - int stroke_seed = 0; const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); - const float unit_v3[3] = {1.0f, 1.0f, 1.0f}; + const bool invert_group = (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0; + const bool use_curve = (mmd->flag & GP_NOISE_CUSTOM_CURVE) != 0 && mmd->curve_intensity; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -106,134 +148,99 @@ static void deformStroke(GpencilModifierData *md, return; } - sc_frame = (int)DEG_get_ctime(depsgraph); + int seed = mmd->seed; + /* FIXME(fclem): This is really slow. We should get the stroke index in another way. */ + int stroke_seed = BLI_findindex(&gpf->strokes, gps); + seed += stroke_seed; - zero_v3(vec2); + /* Make sure different modifiers get different seeds. */ + seed += BLI_hash_string(ob->id.name + 2); + seed += BLI_hash_string(md->name); + + if (mmd->flag & GP_NOISE_USE_RANDOM) { + seed += ((int)DEG_get_ctime(depsgraph)) / mmd->step; + } + + /* Sanitize as it can create out of bound reads. */ + float noise_scale = clamp_f(mmd->noise_scale, 0.0f, 1.0f); + + int len = ceilf(gps->totpoints * noise_scale) + 1; + float *noise_table_position = (mmd->factor > 0.0f) ? noise_table(len, seed + 2) : NULL; + float *noise_table_strength = (mmd->factor_strength > 0.0f) ? noise_table(len, seed + 3) : NULL; + float *noise_table_thickness = (mmd->factor_thickness > 0.0f) ? noise_table(len, seed) : NULL; + float *noise_table_uvs = (mmd->factor_uvs > 0.0f) ? noise_table(len, seed + 4) : NULL; /* calculate stroke normal*/ if (gps->totpoints > 2) { BKE_gpencil_stroke_normal(gps, normal); } else { - copy_v3_v3(normal, unit_v3); + copy_v3_fl(normal, 1.0f); } /* move points */ for (int i = 0; i < gps->totpoints; i++) { - if (((i == 0) || (i == gps->totpoints - 1)) && ((mmd->flag & GP_NOISE_MOVE_EXTREME) == 0)) { - continue; - } - - /* first point is special */ - if (i == 0) { - if (gps->dvert) { - dvert = &gps->dvert[0]; - } - pt0 = (gps->totpoints > 1) ? &gps->points[1] : &gps->points[0]; - pt1 = &gps->points[0]; - } - else { - int prev_idx = i - 1; - CLAMP_MIN(prev_idx, 0); - if (gps->dvert) { - dvert = &gps->dvert[prev_idx]; - } - pt0 = &gps->points[prev_idx]; - pt1 = &gps->points[i]; - } - + bGPDspoint *pt = &gps->points[i]; /* verify vertex group */ - const float weight = get_modifier_point_weight( - dvert, (mmd->flag & GP_NOISE_INVERT_VGROUP) != 0, def_nr); + dvert = &gps->dvert[i]; + float weight = get_modifier_point_weight(dvert, invert_group, def_nr); if (weight < 0.0f) { continue; } - /* initial vector (p0 -> p1) */ - if (i == 0) { - sub_v3_v3v3(vec1, &pt0->x, &pt1->x); - } - else { - sub_v3_v3v3(vec1, &pt1->x, &pt0->x); + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); } - vran = len_v3(vec1); - /* Vector orthogonal to normal. */ - cross_v3_v3v3(vec2, vec1, normal); - normalize_v3(vec2); - /* Use random noise */ - if (mmd->flag & GP_NOISE_USE_RANDOM) { - stroke_seed = BLI_hash_int_2d((sc_frame / mmd->step) + gps->totpoints, mmd->seed + 1); - vran = BLI_hash_frand(stroke_seed); - if (mmd->flag & GP_NOISE_FULL_STROKE) { - vdir = BLI_hash_frand(stroke_seed + 3); - } - else { - int f = (BLI_hash_frand(stroke_seed + 3) * 10.0f) + i; - vdir = f % 2; + + if (mmd->factor > 0.0f) { + /* Offset point randomly around the bi-normal vector. */ + if (gps->totpoints == 1) { + copy_v3_fl3(vec1, 1.0f, 0.0f, 0.0f); } - } - else { - vran = 1.0f; - if (mmd->flag & GP_NOISE_FULL_STROKE) { - vdir = gps->totpoints % 2; + else if (i != gps->totpoints - 1) { + /* Initial vector (p1 -> p0). */ + sub_v3_v3v3(vec1, &gps->points[i].x, &gps->points[i + 1].x); + /* if vec2 is zero, set to something */ + if (len_squared_v3(vec1) < 1e-8f) { + copy_v3_fl3(vec1, 1.0f, 0.0f, 0.0f); + } } else { - vdir = i % 2; + /* Last point reuse the penultimate normal (still stored in vec1) + * because the previous point is already modified. */ } - } + /* Vector orthogonal to normal. */ + cross_v3_v3v3(vec2, vec1, normal); + normalize_v3(vec2); - /* if vec2 is zero, set to something */ - if (gps->totpoints < 3) { - if ((vec2[0] == 0.0f) && (vec2[1] == 0.0f) && (vec2[2] == 0.0f)) { - copy_v3_v3(vec2, unit_v3); - } + float noise = table_sample(noise_table_position, i * noise_scale); + madd_v3_v3fl(&pt->x, vec2, (noise * 2.0f - 1.0f) * weight * mmd->factor * 0.1f); } - /* apply randomness to location of the point */ - if (mmd->flag & GP_NOISE_MOD_LOCATION) { - /* factor is too sensitive, so need divide */ - shift = ((vran * mmd->factor) / 1000.0f) * weight; - if (vdir > 0.5f) { - mul_v3_fl(vec2, shift); - } - else { - mul_v3_fl(vec2, shift * -1.0f); - } - add_v3_v3(&pt1->x, vec2); + if (mmd->factor_thickness > 0.0f) { + float noise = table_sample(noise_table_thickness, i * noise_scale); + pt->pressure *= max_ff(1.0f + (noise * 2.0f - 1.0f) * weight * mmd->factor_thickness, 0.0f); + CLAMP_MIN(pt->pressure, GPENCIL_STRENGTH_MIN); } - /* apply randomness to thickness */ - if (mmd->flag & GP_NOISE_MOD_THICKNESS) { - if (vdir > 0.5f) { - pt1->pressure -= pt1->pressure * vran * mmd->factor * weight; - } - else { - pt1->pressure += pt1->pressure * vran * mmd->factor * weight; - } - CLAMP_MIN(pt1->pressure, GPENCIL_STRENGTH_MIN); + if (mmd->factor_strength > 0.0f) { + float noise = table_sample(noise_table_strength, i * noise_scale); + pt->strength *= max_ff(1.0f - noise * weight * mmd->factor_strength, 0.0f); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } - /* apply randomness to color strength */ - if (mmd->flag & GP_NOISE_MOD_STRENGTH) { - if (vdir > 0.5f) { - pt1->strength -= pt1->strength * vran * mmd->factor * weight; - } - else { - pt1->strength += pt1->strength * vran * mmd->factor * weight; - } - CLAMP_MIN(pt1->strength, GPENCIL_STRENGTH_MIN); - } - /* apply randomness to uv rotation */ - if (mmd->flag & GP_NOISE_MOD_UV) { - if (vdir > 0.5f) { - pt1->uv_rot -= pt1->uv_rot * vran * mmd->factor * weight; - } - else { - pt1->uv_rot += pt1->uv_rot * vran * mmd->factor * weight; - } - CLAMP(pt1->uv_rot, -M_PI_2, M_PI_2); + if (mmd->factor_uvs > 0.0f) { + float noise = table_sample(noise_table_uvs, i * noise_scale); + pt->uv_rot += (noise * 2.0f - 1.0f) * weight * mmd->factor_uvs * M_PI_2; + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); } } + + MEM_SAFE_FREE(noise_table_position); + MEM_SAFE_FREE(noise_table_strength); + MEM_SAFE_FREE(noise_table_thickness); + MEM_SAFE_FREE(noise_table_uvs); } static void bakeModifier(struct Main *UNUSED(bmain), @@ -243,9 +250,9 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } @@ -267,7 +274,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Noise = { /* remapTime */ NULL, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ dependsOnTime, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c index 0979a2d90c9..5a9a1b68361 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -108,6 +109,8 @@ static void deformStroke(GpencilModifierData *md, mul_m4_v3(mat, &pt->x); } + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); } static void bakeModifier(struct Main *UNUSED(bmain), @@ -117,9 +120,9 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index 9647489358e..4b04e349067 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -26,7 +26,6 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math_vector.h" #include "DNA_meshdata_types.h" @@ -35,6 +34,7 @@ #include "DNA_gpencil_types.h" #include "DNA_gpencil_modifier_types.h" +#include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_material.h" #include "BKE_gpencil.h" @@ -54,13 +54,27 @@ static void initData(GpencilModifierData *md) gpmd->layername[0] = '\0'; gpmd->materialname[0] = '\0'; gpmd->vgname[0] = '\0'; - gpmd->flag |= GP_OPACITY_CREATE_COLORS; gpmd->modify_color = GP_MODIFY_COLOR_BOTH; + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemapping_initialize(curve); + } } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + OpacityGpencilModifierData *gmd = (OpacityGpencilModifierData *)md; + OpacityGpencilModifierData *tgmd = (OpacityGpencilModifierData *)target; + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } /* opacity strokes */ @@ -73,6 +87,7 @@ static void deformStroke(GpencilModifierData *md, { OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + const bool use_curve = (mmd->flag & GP_OPACITY_CUSTOM_CURVE) != 0 && mmd->curve_intensity; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -89,98 +104,79 @@ static void deformStroke(GpencilModifierData *md, return; } - if (mmd->opacity_mode == GP_OPACITY_MODE_MATERIAL) { - if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { - gps->runtime.tmp_stroke_rgba[3] *= mmd->factor; - /* if factor is > 1, then force opacity */ - if (mmd->factor > 1.0f) { - gps->runtime.tmp_stroke_rgba[3] += mmd->factor - 1.0f; - } - CLAMP(gps->runtime.tmp_stroke_rgba[3], 0.0f, 1.0f); - } - - if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { - gps->runtime.tmp_fill_rgba[3] *= mmd->factor; - /* if factor is > 1, then force opacity */ - if (mmd->factor > 1.0f && gps->runtime.tmp_fill_rgba[3] > 1e-5) { - gps->runtime.tmp_fill_rgba[3] += mmd->factor - 1.0f; - } - CLAMP(gps->runtime.tmp_fill_rgba[3], 0.0f, 1.0f); - } - - /* if opacity > 1.0, affect the strength of the stroke */ - if (mmd->factor > 1.0f) { - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - pt->strength += mmd->factor - 1.0f; - CLAMP(pt->strength, 0.0f, 1.0f); - } - } - } - /* Apply opacity by strength */ - else { - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + /* Stroke using strength. */ + if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { /* verify vertex group */ float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_OPACITY_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } + /* Custom curve to modulate value. */ + float factor_curve = mmd->factor; + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); + } + if (def_nr < 0) { - pt->strength += mmd->factor - 1.0f; + if (mmd->flag & GP_OPACITY_NORMALIZE) { + pt->strength = factor_curve; + } + else { + pt->strength += factor_curve - 1.0f; + } } else { /* High factor values, change weight too. */ - if ((mmd->factor > 1.0f) && (weight < 1.0f)) { - weight += mmd->factor - 1.0f; + if ((factor_curve > 1.0f) && (weight < 1.0f)) { + weight += factor_curve - 1.0f; CLAMP(weight, 0.0f, 1.0f); } - pt->strength += (mmd->factor - 1) * weight; + if (mmd->flag & GP_OPACITY_NORMALIZE) { + pt->strength = factor_curve; + } + else { + pt->strength += (factor_curve - 1) * weight; + } } + CLAMP(pt->strength, 0.0f, 1.0f); } } + + /* Fill using opacity factor. */ + if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { + gps->fill_opacity_fac = mmd->factor; + CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f); + } } -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { - OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; bGPdata *gpd = ob->data; - GHash *gh_color = BLI_ghash_str_new("GP_Opacity modifier"); - 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) { - - Material *mat = BKE_gpencil_material(ob, gps->mat_nr + 1); - if (mat == NULL) { - continue; - } - MaterialGPencilStyle *gp_style = mat->gp_style; - /* skip stroke if it doesn't have color info */ - if (ELEM(NULL, gp_style)) { - continue; - } - - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); - - if (mmd->opacity_mode == GP_OPACITY_MODE_MATERIAL) { - gpencil_apply_modifier_material( - bmain, ob, mat, gh_color, gps, (bool)(mmd->flag & GP_OPACITY_CREATE_COLORS)); - } } } } - /* free hash buffers */ - if (gh_color) { - BLI_ghash_free(gh_color, NULL, NULL); - gh_color = NULL; +} +static void freeData(GpencilModifierData *md) +{ + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + BKE_curvemapping_free(gpmd->curve_intensity); } } @@ -199,7 +195,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = { /* remapTime */ NULL, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index 9594fc8581e..60e26407b63 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "DNA_scene_types.h" @@ -84,21 +85,21 @@ static void deformStroke(GpencilModifierData *md, switch (mmd->mode) { case GP_SIMPLIFY_FIXED: { for (int i = 0; i < mmd->step; i++) { - BKE_gpencil_simplify_fixed(gps); + BKE_gpencil_stroke_simplify_fixed(gps); } break; } case GP_SIMPLIFY_ADAPTIVE: { /* simplify stroke using Ramer-Douglas-Peucker algorithm */ - BKE_gpencil_simplify_stroke(gps, mmd->factor); + BKE_gpencil_stroke_simplify_adaptive(gps, mmd->factor); break; } case GP_SIMPLIFY_SAMPLE: { - BKE_gpencil_sample_stroke(gps, mmd->length, false); + BKE_gpencil_stroke_sample(gps, mmd->length, false); break; } case GP_SIMPLIFY_MERGE: { - BKE_gpencil_merge_distance_stroke(gpf, gps, mmd->distance, true); + BKE_gpencil_stroke_merge_distance(gpf, gps, mmd->distance, true); break; } default: @@ -113,9 +114,9 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c index b6b5bf05a9d..896333318aa 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -23,6 +23,7 @@ #include +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" @@ -30,6 +31,7 @@ #include "DNA_gpencil_types.h" #include "DNA_gpencil_modifier_types.h" +#include "BKE_colortools.h" #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" @@ -49,11 +51,27 @@ static void initData(GpencilModifierData *md) gpmd->materialname[0] = '\0'; gpmd->vgname[0] = '\0'; gpmd->step = 1; + + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemapping_initialize(curve); + } } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + SmoothGpencilModifierData *gmd = (SmoothGpencilModifierData *)md; + SmoothGpencilModifierData *tgmd = (SmoothGpencilModifierData *)target; + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } /* aply smooth effect based on stroke direction */ @@ -66,6 +84,7 @@ static void deformStroke(GpencilModifierData *md, { SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md; const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + const bool use_curve = (mmd->flag & GP_SMOOTH_CUSTOM_CURVE) != 0 && mmd->curve_intensity; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -89,28 +108,34 @@ static void deformStroke(GpencilModifierData *md, MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; /* verify vertex group */ - const float weight = get_modifier_point_weight( + float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } + /* Custom curve to modulate value. */ + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); + } + const float val = mmd->factor * weight; /* perform smoothing */ if (mmd->flag & GP_SMOOTH_MOD_LOCATION) { - BKE_gpencil_smooth_stroke(gps, i, val); + BKE_gpencil_stroke_smooth(gps, i, val); } if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) { - BKE_gpencil_smooth_stroke_strength(gps, i, val); + BKE_gpencil_stroke_smooth_strength(gps, i, val); } if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0.0f)) { /* thickness need to repeat process several times */ for (int r2 = 0; r2 < r * 10; r2++) { - BKE_gpencil_smooth_stroke_thickness(gps, i, val); + BKE_gpencil_stroke_smooth_thickness(gps, i, val); } } if (mmd->flag & GP_SMOOTH_MOD_UV) { - BKE_gpencil_smooth_stroke_uv(gps, i, val); + BKE_gpencil_stroke_smooth_uv(gps, i, val); } } } @@ -124,15 +149,24 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } } } +static void freeData(GpencilModifierData *md) +{ + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + BKE_curvemapping_free(gpmd->curve_intensity); + } +} + GpencilModifierTypeInfo modifierType_Gpencil_Smooth = { /* name */ "Smooth", /* structName */ "SmoothGpencilModifierData", @@ -148,7 +182,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Smooth = { /* remapTime */ NULL, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c index 89d6565d0dd..25abf0b81eb 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" @@ -84,7 +85,7 @@ static void deformStroke(GpencilModifierData *md, return; } - BKE_gpencil_subdivide(gps, mmd->level, mmd->flag); + BKE_gpencil_stroke_subdivide(gps, mmd->level, mmd->type); } static void bakeModifier(struct Main *UNUSED(bmain), @@ -94,9 +95,9 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } @@ -104,7 +105,7 @@ static void bakeModifier(struct Main *UNUSED(bmain), } GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = { - /* name */ "Subdivision", + /* name */ "Subdivide", /* structName */ "SubdivGpencilModifierData", /* structSize */ sizeof(SubdivGpencilModifierData), /* type */ eGpencilModifierTypeType_Gpencil, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index 694b932a6bf..894dff46ac2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -23,6 +23,8 @@ #include +#include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" @@ -45,7 +47,8 @@ static void initData(GpencilModifierData *md) { ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; gpmd->pass_index = 0; - gpmd->thickness = 2; + gpmd->thickness_fac = 1.0f; + gpmd->thickness = 30; gpmd->layername[0] = '\0'; gpmd->materialname[0] = '\0'; gpmd->vgname[0] = '\0'; @@ -105,70 +108,38 @@ static void deformStroke(GpencilModifierData *md, return; } - /* Check to see if we normalize the whole stroke or only certain points along it. */ - bool gps_has_affected_points = false; - bool gps_has_unaffected_points = false; - - if (mmd->flag & GP_THICK_NORMALIZE) { - for (int i = 0; i < gps->totpoints; i++) { - MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; - const float weight = get_modifier_point_weight( - dvert, (mmd->flag & GP_THICK_INVERT_VGROUP) != 0, def_nr); - if (weight < 0.0f) { - gps_has_unaffected_points = true; - } - else { - gps_has_affected_points = true; - } - - /* If both checks are true, we have what we need so we can stop looking. */ - if (gps_has_affected_points && gps_has_unaffected_points) { - break; - } - } - } - - /* If we are normalizing and all points of the stroke are affected, it's safe to reset thickness - */ - if (mmd->flag & GP_THICK_NORMALIZE && gps_has_affected_points && !gps_has_unaffected_points) { - gps->thickness = mmd->thickness; - } - /* Without this check, modifier alters the thickness of strokes which have no points in scope */ + float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1); for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; - float curvef = 1.0f; /* Verify point is part of vertex group. */ - const float weight = get_modifier_point_weight( + float weight = get_modifier_point_weight( dvert, (mmd->flag & GP_THICK_INVERT_VGROUP) != 0, def_nr); if (weight < 0.0f) { continue; } + float curvef = 1.0f; + if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { + /* Normalize value to evaluate curve. */ + float value = (float)i / (gps->totpoints - 1); + curvef = BKE_curvemapping_evaluateF(mmd->curve_thickness, 0, value); + } + + float target; if (mmd->flag & GP_THICK_NORMALIZE) { - if (gps_has_unaffected_points) { - /* Clamp value for very weird situations when stroke thickness can be zero. */ - CLAMP_MIN(gps->thickness, 1); - /* Calculate pressure value to match the width of strokes with reset thickness and 1.0 - * pressure. */ - pt->pressure = (float)mmd->thickness / (float)gps->thickness; - } - else { - /* Reset point pressure values so only stroke thickness counts. */ - pt->pressure = 1.0f; - } + target = mmd->thickness * stroke_thickness_inv; + target *= curvef; } else { - if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { - /* Normalize value to evaluate curve. */ - float value = (float)i / (gps->totpoints - 1); - curvef = BKE_curvemapping_evaluateF(mmd->curve_thickness, 0, value); - } - - pt->pressure += mmd->thickness * weight * curvef; - CLAMP_MIN(pt->pressure, 0.1f); + target = pt->pressure * mmd->thickness_fac; + weight *= curvef; } + + pt->pressure = interpf(target, pt->pressure, weight); + + CLAMP_MIN(pt->pressure, 0.1f); } } @@ -179,9 +150,9 @@ static void bakeModifier(struct Main *UNUSED(bmain), { bGPdata *gpd = ob->data; - 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) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 8f3956276dd..96f1f004812 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -26,7 +26,6 @@ #include "BLI_utildefines.h" #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math_vector.h" #include "DNA_scene_types.h" @@ -34,6 +33,7 @@ #include "DNA_gpencil_types.h" #include "DNA_gpencil_modifier_types.h" +#include "BKE_colortools.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_material.h" @@ -52,13 +52,28 @@ static void initData(GpencilModifierData *md) gpmd->layername[0] = '\0'; gpmd->materialname[0] = '\0'; ARRAY_SET_ITEMS(gpmd->rgb, 1.0f, 1.0f, 1.0f); - gpmd->flag |= GP_TINT_CREATE_COLORS; gpmd->modify_color = GP_MODIFY_COLOR_BOTH; + + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemapping_initialize(curve); + } } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + TintGpencilModifierData *gmd = (TintGpencilModifierData *)md; + TintGpencilModifierData *tgmd = (TintGpencilModifierData *)target; + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); } /* tint strokes */ @@ -70,6 +85,7 @@ static void deformStroke(GpencilModifierData *md, bGPDstroke *gps) { TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; + const bool use_curve = (mmd->flag & GP_TINT_CUSTOM_CURVE) != 0 && mmd->curve_intensity; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -86,69 +102,77 @@ static void deformStroke(GpencilModifierData *md, return; } - if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { - interp_v3_v3v3( - gps->runtime.tmp_stroke_rgba, gps->runtime.tmp_stroke_rgba, mmd->rgb, mmd->factor); - /* if factor is > 1, the alpha must be changed to get full tint */ - if (mmd->factor > 1.0f) { - gps->runtime.tmp_stroke_rgba[3] += mmd->factor - 1.0f; + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + + /* if factor > 1.0, affect the strength of the stroke */ + if (mmd->factor > 1.0f) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->strength += mmd->factor - 1.0f; + CLAMP(pt->strength, 0.0f, 1.0f); } - CLAMP4(gps->runtime.tmp_stroke_rgba, 0.0f, 1.0f); } + /* Apply to Vertex Color. */ + float mixfac = mmd->factor; + + CLAMP(mixfac, 0.0, 1.0f); + /* Fill */ if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) { - interp_v3_v3v3(gps->runtime.tmp_fill_rgba, gps->runtime.tmp_fill_rgba, mmd->rgb, mmd->factor); - /* if factor is > 1, the alpha must be changed to get full tint */ - if (mmd->factor > 1.0f && gps->runtime.tmp_fill_rgba[3] > 1e-5) { - gps->runtime.tmp_fill_rgba[3] += mmd->factor - 1.0f; + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (gps->vert_color_fill[3] == 0.0f) && + (gp_style->fill_rgba[3] > 0.0f)) { + copy_v4_v4(gps->vert_color_fill, gp_style->fill_rgba); + gps->vert_color_fill[3] = 1.0f; } - CLAMP4(gps->runtime.tmp_fill_rgba, 0.0f, 1.0f); + + interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, mmd->rgb, mixfac); } - /* if factor > 1.0, affect the strength of the stroke */ - if (mmd->factor > 1.0f) { + /* Stroke */ + if (mmd->modify_color != GP_MODIFY_COLOR_FILL) { for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; - pt->strength += mmd->factor - 1.0f; - CLAMP(pt->strength, 0.0f, 1.0f); + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (pt->vert_color[3] == 0.0f) && (gp_style->stroke_rgba[3] > 0.0f)) { + copy_v4_v4(pt->vert_color, gp_style->stroke_rgba); + pt->vert_color[3] = 1.0f; + } + + /* Custom curve to modulate value. */ + float mixvalue = mixfac; + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + mixvalue *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); + } + + interp_v3_v3v3(pt->vert_color, pt->vert_color, mmd->rgb, mixvalue); } } } -static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) { - TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; bGPdata *gpd = ob->data; - GHash *gh_color = BLI_ghash_str_new("GP_Tint modifier"); - 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) { - - Material *mat = BKE_gpencil_material(ob, gps->mat_nr + 1); - if (mat == NULL) { - continue; - } - MaterialGPencilStyle *gp_style = mat->gp_style; - /* skip stroke if it doesn't have color info */ - if (ELEM(NULL, gp_style)) { - continue; - } - - copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); - copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); - + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); - - gpencil_apply_modifier_material( - bmain, ob, mat, gh_color, gps, (bool)(mmd->flag & GP_TINT_CREATE_COLORS)); } } } - /* free hash buffers */ - if (gh_color) { - BLI_ghash_free(gh_color, NULL, NULL); - gh_color = NULL; +} + +static void freeData(GpencilModifierData *md) +{ + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + + if (gpmd->curve_intensity) { + BKE_curvemapping_free(gpmd->curve_intensity); } } @@ -167,7 +191,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Tint = { /* remapTime */ NULL, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilvertexcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilvertexcolor.c new file mode 100644 index 00000000000..b279d90dd4f --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilvertexcolor.c @@ -0,0 +1,314 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_utildefines.h" + +#include "BLI_math.h" +#include "BLI_listbase.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 "DNA_modifier_types.h" + +#include "BKE_action.h" +#include "BKE_colorband.h" +#include "BKE_colortools.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_layer.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_scene.h" + +#include "MEM_guardedalloc.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +static void initData(GpencilModifierData *md) +{ + VertexcolorGpencilModifierData *gpmd = (VertexcolorGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->materialname[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->object = NULL; + gpmd->radius = 1.0f; + gpmd->factor = 1.0f; + + /* Add default color ramp. */ + gpmd->colorband = BKE_colorband_add(false); + if (gpmd->colorband) { + BKE_colorband_init(gpmd->colorband, true); + CBData *ramp = gpmd->colorband->data; + ramp[0].r = ramp[0].g = ramp[0].b = ramp[0].a = 1.0f; + ramp[0].pos = 0.0f; + ramp[1].r = ramp[1].g = ramp[1].b = 0.0f; + ramp[1].a = 1.0f; + ramp[1].pos = 1.0f; + + gpmd->colorband->tot = 2; + } + + gpmd->curve_intensity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_intensity) { + CurveMapping *curve = gpmd->curve_intensity; + BKE_curvemapping_initialize(curve); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + VertexcolorGpencilModifierData *gmd = (VertexcolorGpencilModifierData *)md; + VertexcolorGpencilModifierData *tgmd = (VertexcolorGpencilModifierData *)target; + + MEM_SAFE_FREE(tgmd->colorband); + + if (tgmd->curve_intensity != NULL) { + BKE_curvemapping_free(tgmd->curve_intensity); + tgmd->curve_intensity = NULL; + } + + BKE_gpencil_modifier_copyData_generic(md, target); + + if (gmd->colorband) { + tgmd->colorband = MEM_dupallocN(gmd->colorband); + } + + tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity); +} + +/* deform stroke */ +static void deformStroke(GpencilModifierData *md, + Depsgraph *UNUSED(depsgraph), + Object *ob, + bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *gps) +{ + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + if (!mmd->object) { + return; + } + + const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + const bool use_curve = (mmd->flag & GP_VERTEXCOL_CUSTOM_CURVE) != 0 && mmd->curve_intensity; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->materialname, + mmd->pass_index, + mmd->layer_pass, + 1, + gpl, + gps, + mmd->flag & GP_VERTEXCOL_INVERT_LAYER, + mmd->flag & GP_VERTEXCOL_INVERT_PASS, + mmd->flag & GP_VERTEXCOL_INVERT_LAYERPASS, + mmd->flag & GP_VERTEXCOL_INVERT_MATERIAL)) { + return; + } + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + + float coba_res[4]; + float matrix[4][4]; + mul_m4_m4m4(matrix, mmd->object->imat, ob->obmat); + + /* loop points and apply deform */ + bool fill_done = false; + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + + if (!fill_done) { + /* Apply to fill. */ + if (mmd->mode != GPPAINT_MODE_STROKE) { + + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (gps->vert_color_fill[3] == 0.0f) && + (gp_style->fill_rgba[3] > 0.0f)) { + copy_v4_v4(gps->vert_color_fill, gp_style->fill_rgba); + gps->vert_color_fill[3] = 1.0f; + } + + float center[3]; + add_v3_v3v3(center, gps->boundbox_min, gps->boundbox_max); + mul_v3_fl(center, 0.5f); + float pt_loc[3]; + mul_v3_m4v3(pt_loc, matrix, &pt->x); + float dist = len_v3(pt_loc); + float mix_factor = clamp_f(dist / mmd->radius, 0.0f, 1.0f); + + BKE_colorband_evaluate(mmd->colorband, mix_factor, coba_res); + interp_v3_v3v3(gps->vert_color_fill, gps->vert_color_fill, coba_res, mmd->factor); + gps->vert_color_fill[3] = mmd->factor; + /* If no stroke, cancel loop. */ + if (mmd->mode != GPPAINT_MODE_BOTH) { + break; + } + } + + fill_done = true; + } + + /* Verify vertex group. */ + if (mmd->mode != GPPAINT_MODE_FILL) { + float weight = get_modifier_point_weight( + dvert, (mmd->flag & GP_VERTEXCOL_INVERT_VGROUP) != 0, def_nr); + if (weight < 0.0f) { + continue; + } + /* Custom curve to modulate value. */ + if (use_curve) { + float value = (float)i / (gps->totpoints - 1); + weight *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value); + } + + /* Calc world position of point. */ + float pt_loc[3]; + mul_v3_m4v3(pt_loc, matrix, &pt->x); + float dist = len_v3(pt_loc); + + /* If not using Vertex Color, use the material color. */ + if ((gp_style != NULL) && (pt->vert_color[3] == 0.0f) && (gp_style->stroke_rgba[3] > 0.0f)) { + copy_v4_v4(pt->vert_color, gp_style->stroke_rgba); + pt->vert_color[3] = 1.0f; + } + + /* Calc the factor using the distance and get mix color. */ + float mix_factor = clamp_f(dist / mmd->radius, 0.0f, 1.0f); + BKE_colorband_evaluate(mmd->colorband, mix_factor, coba_res); + + interp_v3_v3v3(pt->vert_color, pt->vert_color, coba_res, mmd->factor * weight * coba_res[3]); + } + } +} + +/* FIXME: Ideally we be doing this on a copy of the main depsgraph + * (i.e. one where we don't have to worry about restoring state) + */ +static void bakeModifier(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +{ + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) { + return; + } + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + /* apply effects on this frame + * NOTE: this assumes that we don't want animation on non-keyframed frames + */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* compute effects on this frame */ + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static void freeData(GpencilModifierData *md) +{ + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + if (mmd->colorband) { + MEM_freeN(mmd->colorband); + mmd->colorband = NULL; + } + if (mmd->curve_intensity) { + BKE_curvemapping_free(mmd->curve_intensity); + } +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + + return !mmd->object; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + VertexcolorGpencilModifierData *lmd = (VertexcolorGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Vertexcolor Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Vertexcolor Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Vertexcolor Modifier"); +} + +static void foreachObjectLink(GpencilModifierData *md, + Object *ob, + ObjectWalkFunc walk, + void *userData) +{ + VertexcolorGpencilModifierData *mmd = (VertexcolorGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Vertexcolor = { + /* name */ "Vertex Color", + /* structName */ "VertexcolorGpencilModifierData", + /* structSize */ sizeof(VertexcolorGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 7d0f8b0bcbf..213cbe30794 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -173,6 +173,8 @@ void GPU_framebuffer_clear(GPUFrameBuffer *fb, #define GPU_framebuffer_clear_color_depth_stencil(fb, col, depth, stencil) \ GPU_framebuffer_clear(fb, GPU_COLOR_BIT | GPU_DEPTH_BIT | GPU_STENCIL_BIT, col, depth, stencil) +void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4]); + void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data); void GPU_framebuffer_read_color( GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data); diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h index c636135d75b..8b0d25e51a3 100644 --- a/source/blender/gpu/GPU_shader_interface.h +++ b/source/blender/gpu/GPU_shader_interface.h @@ -55,6 +55,7 @@ typedef enum { GPU_UNIFORM_COLOR, /* vec4 color */ GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ + GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ GPU_UNIFORM_CUSTOM, /* custom uniform, not one of the above built-ins */ diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 338f4d906f6..50b7c23059d 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -95,6 +95,7 @@ typedef enum eGPUTextureFormat { #if 0 GPU_RGB10_A2, GPU_RGB10_A2UI, + GPU_SRGB8_A8, #endif GPU_R11F_G11F_B10F, GPU_DEPTH32F_STENCIL8, diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index cbc20f37f27..f5717645bc1 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -112,6 +112,8 @@ uint GPU_vertformat_attr_add( GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode); void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias); +void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count); + void GPU_vertformat_deinterleave(GPUVertFormat *format); int GPU_vertformat_attr_id_get(const GPUVertFormat *, const char *name); diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.c index b0fbb0eae49..6197d5f99b8 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.c @@ -614,6 +614,21 @@ void GPU_framebuffer_clear(GPUFrameBuffer *fb, glClear(mask); } +/* Clear all textures bound to this framebuffer with a different color. */ +void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4]) +{ + CHECK_FRAMEBUFFER_IS_BOUND(fb); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + if (fb->attachments[type].tex != NULL) { + glClearBufferfv(GL_COLOR, i, clear_cols[i]); + } + } +} + void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data) { CHECK_FRAMEBUFFER_IS_BOUND(fb); diff --git a/source/blender/gpu/intern/gpu_shader_interface.c b/source/blender/gpu/intern/gpu_shader_interface.c index 983c5dfc27a..f895993a27d 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.c +++ b/source/blender/gpu/intern/gpu_shader_interface.c @@ -67,6 +67,7 @@ static const char *BuiltinUniform_name(GPUUniformBuiltin u) [GPU_UNIFORM_COLOR] = "color", [GPU_UNIFORM_BASE_INSTANCE] = "baseInstance", [GPU_UNIFORM_RESOURCE_CHUNK] = "resourceChunk", + [GPU_UNIFORM_RESOURCE_ID] = "resourceId", [GPU_UNIFORM_CUSTOM] = NULL, [GPU_NUM_UNIFORMS] = NULL, diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.c index 65573b71c76..16d8f8f875b 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.c +++ b/source/blender/gpu/intern/gpu_vertex_format.c @@ -206,6 +206,47 @@ void GPU_vertformat_alias_add(GPUVertFormat *format, const char *alias) attr->names[attr->name_len++] = copy_attr_name(format, alias); } +/** + * Makes vertex attrib from the next vertices to be accessible in the vertex shader. + * For an attrib named "attr" you can access the next nth vertex using "attrn". + * Use this function after specifying all the attribs in the format. + * + * NOTE: This does NOT work when using indexed rendering. + * NOTE: Only works for first attrib name. (this limitation can be changed if needed) + * + * WARNING: this function creates a lot of aliases/attribs, make sure to keep the attrib name + * short to avoid overflowing the namebuffer. + * */ +void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count) +{ + /* Sanity check. Maximum can be upgraded if needed. */ + BLI_assert(load_count > 1 && load_count < 5); + /* We need a packed format because of format->stride. */ + if (!format->packed) { + VertexFormat_pack(format); + } + + BLI_assert((format->name_len + 1) * load_count < GPU_VERT_FORMAT_MAX_NAMES); + BLI_assert(format->attr_len * load_count <= GPU_VERT_ATTR_MAX_LEN); + BLI_assert(format->name_offset * load_count < GPU_VERT_ATTR_NAMES_BUF_LEN); + + const GPUVertAttr *attr = format->attrs; + int attr_len = format->attr_len; + for (int i = 0; i < attr_len; i++, attr++) { + const char *attr_name = GPU_vertformat_attr_name_get(format, attr, 0); + for (int j = 1; j < load_count; j++) { + char load_name[64]; + BLI_snprintf(load_name, sizeof(load_name), "%s%d", attr_name, j); + GPUVertAttr *dst_attr = &format->attrs[format->attr_len++]; + *dst_attr = *attr; + + dst_attr->names[0] = copy_attr_name(format, load_name); + dst_attr->name_len = 1; + dst_attr->offset += format->stride * j; + } + } +} + int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name) { for (int i = 0; i < format->attr_len; i++) { diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 8ac9b2e2e16..83ef910d0bb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -601,6 +601,9 @@ void bilinear_interpolation_color_wrap( void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]); void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3]); +void IMB_sampleImageAtLocation( + struct ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]); + /** * * \attention defined in readimage.c diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 6a234232a35..4cd47d19e62 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -37,6 +37,7 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#include "IMB_colormanagement.h" #include /* Only this one is used liberally here, and in imbuf */ @@ -490,3 +491,19 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol cp += 4; } } + +/* Sample pixel of image using NEAREST method. */ +void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]) +{ + if (ibuf->rect_float) { + nearest_interpolation_color(ibuf, NULL, color, x, y); + } + else { + unsigned char byte_color[4]; + nearest_interpolation_color(ibuf, byte_color, NULL, x, y); + rgba_uchar_to_float(color, byte_color); + if (make_linear_rgb) { + IMB_colormanagement_colorspace_to_scene_linear_v4(color, false, ibuf->rect_colorspace); + } + } +} diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 0109fba909b..3f703558e54 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -50,8 +50,7 @@ typedef struct BrushClone { typedef struct BrushGpencilSettings { /** Amount of smoothing to apply to newly created strokes. */ float draw_smoothfac; - /** Amount of sensitivity to apply to newly created strokes. */ - float draw_sensitivity; + char _pad2[4]; /** Amount of alpha strength to apply to newly created strokes. */ float draw_strength; /** Amount of jitter to apply to newly created strokes. */ @@ -64,18 +63,11 @@ typedef struct BrushGpencilSettings { float draw_random_press; /** Factor of strength for strength. */ float draw_random_strength; - /** Factor of randomness for subdivision. */ - float draw_random_sub; /** Number of times to apply smooth factor to new strokes. */ short draw_smoothlvl; /** Number of times to subdivide new strokes. */ short draw_subdivide; - short _pad; - - /** Number of times to apply thickness smooth factor to new strokes. */ - short thick_smoothlvl; - /** Amount of thickness smoothing to apply to newly created strokes. */ - float thick_smoothfac; + char _pad[4]; /** Factor for transparency. */ float fill_threshold; @@ -83,7 +75,7 @@ typedef struct BrushGpencilSettings { short fill_leak; /** Fill zoom factor */ short fill_factor; - char _pad_1[4]; + char _pad1[4]; /** Number of simplify steps. */ int fill_simplylvl; @@ -110,12 +102,24 @@ typedef struct BrushGpencilSettings { int flag; /** gradient control along y for color */ - float gradient_f; + float hardeness; /** factor xy of shape for dots gradients */ - float gradient_s[2]; + float aspect_ratio[2]; /** Simplify adaptive factor */ float simplify_f; + /** Mix colorfactor */ + float vertex_factor; + int vertex_mode; + + /** eGP_Sculpt_Flag. */ + int sculpt_flag; + /** eGP_Sculpt_Mode_Flag. */ + int sculpt_mode_flag; + /** Preset type (used to reset brushes - internal). */ + short preset_type; + char _pad3[6]; + struct CurveMapping *curve_sensitivity; struct CurveMapping *curve_strength; struct CurveMapping *curve_jitter; @@ -125,6 +129,49 @@ typedef struct BrushGpencilSettings { struct Material *material; } BrushGpencilSettings; +/* BrushGpencilSettings->preset_type. + * Use a range for each group and not continuous values.*/ +typedef enum eGPBrush_Presets { + GP_BRUSH_PRESET_UNKNOWN = 0, + + /* Draw 1-99. */ + GP_BRUSH_PRESET_AIRBRUSH = 1, + GP_BRUSH_PRESET_INK_PEN = 2, + GP_BRUSH_PRESET_INK_PEN_ROUGH = 3, + GP_BRUSH_PRESET_MARKER_BOLD = 4, + GP_BRUSH_PRESET_MARKER_CHISEL = 5, + GP_BRUSH_PRESET_PEN = 6, + GP_BRUSH_PRESET_PENCIL_SOFT = 7, + GP_BRUSH_PRESET_PENCIL = 8, + GP_BRUSH_PRESET_FILL_AREA = 9, + GP_BRUSH_PRESET_ERASER_SOFT = 10, + GP_BRUSH_PRESET_ERASER_HARD = 11, + GP_BRUSH_PRESET_ERASER_POINT = 12, + GP_BRUSH_PRESET_ERASER_STROKE = 13, + GP_BRUSH_PRESET_TINT = 14, + + /* Vertex Paint 100-199. */ + GP_BRUSH_PRESET_VERTEX_DRAW = 100, + GP_BRUSH_PRESET_VERTEX_BLUR = 101, + GP_BRUSH_PRESET_VERTEX_AVERAGE = 102, + GP_BRUSH_PRESET_VERTEX_SMEAR = 103, + GP_BRUSH_PRESET_VERTEX_REPLACE = 104, + + /* Sculpt 200-299. */ + GP_BRUSH_PRESET_SMOOTH_STROKE = 200, + GP_BRUSH_PRESET_STRENGTH_STROKE = 201, + GP_BRUSH_PRESET_THICKNESS_STROKE = 202, + GP_BRUSH_PRESET_GRAB_STROKE = 203, + GP_BRUSH_PRESET_PUSH_STROKE = 204, + GP_BRUSH_PRESET_TWIST_STROKE = 205, + GP_BRUSH_PRESET_PINCH_STROKE = 206, + GP_BRUSH_PRESET_RANDOMIZE_STROKE = 207, + GP_BRUSH_PRESET_CLONE_STROKE = 208, + + /* Weight Paint 300-399. */ + GP_BRUSH_PRESET_DRAW_WEIGHT = 300, +} eGPBrush_Presets; + /* BrushGpencilSettings->gp_flag */ typedef enum eGPDbrush_Flag { /* brush use pressure */ @@ -133,8 +180,6 @@ typedef enum eGPDbrush_Flag { GP_BRUSH_USE_STENGTH_PRESSURE = (1 << 1), /* brush use pressure for alpha factor */ GP_BRUSH_USE_JITTER_PRESSURE = (1 << 2), - /* enable screen cursor */ - GP_BRUSH_ENABLE_CURSOR = (1 << 5), /* fill hide transparent */ GP_BRUSH_FILL_HIDE = (1 << 6), /* show fill help lines */ @@ -187,6 +232,22 @@ typedef enum eGP_BrushIcons { GP_BRUSH_ICON_ERASE_STROKE = 10, GP_BRUSH_ICON_AIRBRUSH = 11, GP_BRUSH_ICON_CHISEL = 12, + GP_BRUSH_ICON_TINT = 13, + GP_BRUSH_ICON_VERTEX_DRAW = 14, + GP_BRUSH_ICON_VERTEX_BLUR = 15, + GP_BRUSH_ICON_VERTEX_AVERAGE = 16, + GP_BRUSH_ICON_VERTEX_SMEAR = 17, + GP_BRUSH_ICON_VERTEX_REPLACE = 18, + GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19, + GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20, + GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21, + GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22, + GP_BRUSH_ICON_GPBRUSH_GRAB = 23, + GP_BRUSH_ICON_GPBRUSH_PUSH = 24, + GP_BRUSH_ICON_GPBRUSH_TWIST = 25, + GP_BRUSH_ICON_GPBRUSH_PINCH = 26, + GP_BRUSH_ICON_GPBRUSH_CLONE = 27, + GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28, } eGP_BrushIcons; typedef enum eBrushCurvePreset { @@ -225,6 +286,38 @@ typedef enum eBrushClothForceFalloffType { BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1, } eBrushClothForceFalloffType; +/* Gpencilsettings.Vertex_mode */ +typedef enum eGp_Vertex_Mode { + /* Affect to Stroke only. */ + GPPAINT_MODE_STROKE = 0, + /* Affect to Fill only. */ + GPPAINT_MODE_FILL = 1, + /* Affect to both. */ + GPPAINT_MODE_BOTH = 2, +} eGp_Vertex_Mode; + +/* sculpt_flag */ +typedef enum eGP_Sculpt_Flag { + /* invert the effect of the brush */ + GP_SCULPT_FLAG_INVERT = (1 << 0), + /* smooth brush affects pressure values as well */ + GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2), + /* temporary invert action */ + GP_SCULPT_FLAG_TMP_INVERT = (1 << 3), +} eGP_Sculpt_Flag; + +/* sculpt_mode_flag */ +typedef enum eGP_Sculpt_Mode_Flag { + /* apply brush to position */ + GP_SCULPT_FLAGMODE_APPLY_POSITION = (1 << 0), + /* apply brush to strength */ + GP_SCULPT_FLAGMODE_APPLY_STRENGTH = (1 << 1), + /* apply brush to thickness */ + GP_SCULPT_FLAGMODE_APPLY_THICKNESS = (1 << 2), + /* apply brush to uv data */ + GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3), +} eGP_Sculpt_Mode_Flag; + typedef enum eAutomasking_flag { BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0), BRUSH_AUTOMASKING_FACE_SETS = (1 << 1), @@ -327,7 +420,13 @@ typedef struct Brush { char mask_tool; /** Active grease pencil tool. */ char gpencil_tool; - char _pad1[5]; + /** Active grease pencil vertex tool. */ + char gpencil_vertex_tool; + /** Active grease pencil sculpt tool. */ + char gpencil_sculpt_tool; + /** Active grease pencil weight tool. */ + char gpencil_weight_tool; + char _pad1_[6]; float autosmooth_factor; @@ -398,9 +497,20 @@ typedef struct Brush { float mask_stencil_pos[2]; float mask_stencil_dimension[2]; + char _pad6[4]; struct BrushGpencilSettings *gpencil_settings; } Brush; + +/* Struct to hold palette colors for sorting. */ +typedef struct tPaletteColorHSV { + float rgb[3]; + float value; + float h; + float s; + float v; +} tPaletteColorHSV; + typedef struct PaletteColor { struct PaletteColor *next, *prev; /* two values, one to store rgb, other to store values for sculpt/weight */ @@ -629,8 +739,37 @@ typedef enum eBrushGPaintTool { GPAINT_TOOL_DRAW = 0, GPAINT_TOOL_FILL = 1, GPAINT_TOOL_ERASE = 2, + GPAINT_TOOL_TINT = 3, } eBrushGPaintTool; +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPVertexTool { + GPVERTEX_TOOL_DRAW = 0, + GPVERTEX_TOOL_BLUR = 1, + GPVERTEX_TOOL_AVERAGE = 2, + GPVERTEX_TOOL_TINT = 3, + GPVERTEX_TOOL_SMEAR = 4, + GPVERTEX_TOOL_REPLACE = 5, +} eBrushGPVertexTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPSculptTool { + GPSCULPT_TOOL_SMOOTH = 0, + GPSCULPT_TOOL_THICKNESS = 1, + GPSCULPT_TOOL_STRENGTH = 2, + GPSCULPT_TOOL_GRAB = 3, + GPSCULPT_TOOL_PUSH = 4, + GPSCULPT_TOOL_TWIST = 5, + GPSCULPT_TOOL_PINCH = 6, + GPSCULPT_TOOL_RANDOMIZE = 7, + GPSCULPT_TOOL_CLONE = 8, +} eBrushGPSculptTool; + +/* BrushGpencilSettings->brush type */ +typedef enum eBrushGPWeightTool { + GPWEIGHT_TOOL_DRAW = 0, +} eBrushGPWeightTool; + /* direction that the brush displaces along */ enum { SCULPT_DISP_DIR_AREA = 0, diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 5fe12998998..1121bdf4df0 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -47,6 +47,7 @@ typedef enum GpencilModifierType { eGpencilModifierType_Armature = 15, eGpencilModifierType_Time = 16, eGpencilModifierType_Multiply = 17, + eGpencilModifierType_Vertexcolor = 18, NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -89,12 +90,18 @@ typedef struct NoiseGpencilModifierData { int flag; /** Factor of noise. */ float factor; + float factor_strength; + float factor_thickness; + float factor_uvs; + /** Noise Frequency scaling */ + float noise_scale; /** How many frames before recalculate randoms. */ int step; /** Custom index for passes. */ int layer_pass; /** Random seed */ int seed; + struct CurveMapping *curve_intensity; } NoiseGpencilModifierData; typedef enum eNoiseGpencil_Flag { @@ -103,7 +110,7 @@ typedef enum eNoiseGpencil_Flag { GP_NOISE_MOD_STRENGTH = (1 << 2), GP_NOISE_MOD_THICKNESS = (1 << 3), GP_NOISE_FULL_STROKE = (1 << 4), - GP_NOISE_MOVE_EXTREME = (1 << 5), + GP_NOISE_CUSTOM_CURVE = (1 << 5), GP_NOISE_INVERT_LAYER = (1 << 6), GP_NOISE_INVERT_PASS = (1 << 7), GP_NOISE_INVERT_VGROUP = (1 << 8), @@ -126,16 +133,23 @@ typedef struct SubdivGpencilModifierData { int level; /** Custom index for passes. */ int layer_pass; + /** Type of subdivision */ + short type; + char _pad[6]; } SubdivGpencilModifierData; typedef enum eSubdivGpencil_Flag { - GP_SUBDIV_SIMPLE = (1 << 0), GP_SUBDIV_INVERT_LAYER = (1 << 1), GP_SUBDIV_INVERT_PASS = (1 << 2), GP_SUBDIV_INVERT_LAYERPASS = (1 << 3), GP_SUBDIV_INVERT_MATERIAL = (1 << 4), } eSubdivGpencil_Flag; +typedef enum eSubdivGpencil_Type { + GP_SUBDIV_CATMULL = 0, + GP_SUBDIV_SIMPLE = 1, +} eSubdivGpencil_Type; + typedef struct ThickGpencilModifierData { GpencilModifierData modifier; /** Layer name. */ @@ -148,10 +162,13 @@ typedef struct ThickGpencilModifierData { int pass_index; /** Flags. */ int flag; - /** Thickness change. */ + /** Relative thickness factor. */ + float thickness_fac; + /** Absolute thickness overide. */ int thickness; /** Custom index for passes. */ int layer_pass; + char _pad[4]; struct CurveMapping *curve_thickness; } ThickGpencilModifierData; @@ -225,15 +242,17 @@ typedef struct TintGpencilModifierData { char _pad[7]; /** Custom index for passes. */ int layer_pass; + char _pad1[4]; + struct CurveMapping *curve_intensity; } TintGpencilModifierData; typedef enum eTintGpencil_Flag { - GP_TINT_CREATE_COLORS = (1 << 0), GP_TINT_INVERT_LAYER = (1 << 1), GP_TINT_INVERT_PASS = (1 << 2), GP_TINT_INVERT_LAYERPASS = (1 << 3), GP_TINT_INVERT_MATERIAL = (1 << 4), + GP_TINT_CUSTOM_CURVE = (1 << 5), } eTintGpencil_Flag; typedef struct ColorGpencilModifierData { @@ -253,15 +272,17 @@ typedef struct ColorGpencilModifierData { char _pad[3]; /** Custom index for passes. */ int layer_pass; + char _pad1[4]; + struct CurveMapping *curve_intensity; } ColorGpencilModifierData; typedef enum eColorGpencil_Flag { - GP_COLOR_CREATE_COLORS = (1 << 0), GP_COLOR_INVERT_LAYER = (1 << 1), GP_COLOR_INVERT_PASS = (1 << 2), GP_COLOR_INVERT_LAYERPASS = (1 << 3), GP_COLOR_INVERT_MATERIAL = (1 << 4), + GP_COLOR_CUSTOM_CURVE = (1 << 5), } eColorGpencil_Flag; typedef struct OpacityGpencilModifierData { @@ -280,21 +301,22 @@ typedef struct OpacityGpencilModifierData { float factor; /** Modify stroke, fill or both. */ char modify_color; - /** Mode of opacity, colors or strength */ - char opacity_mode; - char _pad[2]; + char _pad[3]; /** Custom index for passes. */ int layer_pass; + char _pad1[4]; + struct CurveMapping *curve_intensity; } OpacityGpencilModifierData; typedef enum eOpacityGpencil_Flag { GP_OPACITY_INVERT_LAYER = (1 << 0), GP_OPACITY_INVERT_PASS = (1 << 1), GP_OPACITY_INVERT_VGROUP = (1 << 2), - GP_OPACITY_CREATE_COLORS = (1 << 3), GP_OPACITY_INVERT_LAYERPASS = (1 << 4), GP_OPACITY_INVERT_MATERIAL = (1 << 5), + GP_OPACITY_CUSTOM_CURVE = (1 << 6), + GP_OPACITY_NORMALIZE = (1 << 7), } eOpacityGpencil_Flag; typedef struct ArrayGpencilModifierData { @@ -308,17 +330,15 @@ typedef struct ArrayGpencilModifierData { float offset[3]; /** Shift increment. */ float shift[3]; - /** Random size factor. */ - float rnd_size; - /** Random size factor. */ - float rnd_rot; - /** Rotation changes. */ - float rot[3]; - /** Scale changes. */ - float scale[3]; - /** (first element is the index) random values. */ - float rnd[20]; + /** Random Offset. */ + float rnd_offset[3]; + /** Random Rotation. */ + float rnd_rot[3]; + /** Random Scales. */ + float rnd_scale[3]; char _pad[4]; + /** (first element is the index) random values. */ + int seed; /** Custom index for passes. */ int pass_index; @@ -333,13 +353,13 @@ typedef struct ArrayGpencilModifierData { } ArrayGpencilModifierData; typedef enum eArrayGpencil_Flag { - GP_ARRAY_RANDOM_SIZE = (1 << 0), - GP_ARRAY_RANDOM_ROT = (1 << 1), GP_ARRAY_INVERT_LAYER = (1 << 2), GP_ARRAY_INVERT_PASS = (1 << 3), - GP_ARRAY_KEEP_ONTOP = (1 << 4), GP_ARRAY_INVERT_LAYERPASS = (1 << 5), GP_ARRAY_INVERT_MATERIAL = (1 << 6), + GP_ARRAY_USE_OFFSET = (1 << 7), + GP_ARRAY_USE_RELATIVE = (1 << 8), + GP_ARRAY_USE_OB_OFFSET = (1 << 9), } eArrayGpencil_Flag; typedef struct BuildGpencilModifierData { @@ -613,7 +633,9 @@ typedef struct SmoothGpencilModifierData { int step; /** Custom index for passes. */ int layer_pass; - char _pad[4]; + + char _pad1[4]; + struct CurveMapping *curve_intensity; } SmoothGpencilModifierData; typedef enum eSmoothGpencil_Flag { @@ -626,6 +648,7 @@ typedef enum eSmoothGpencil_Flag { GP_SMOOTH_MOD_UV = (1 << 6), GP_SMOOTH_INVERT_LAYERPASS = (1 << 7), GP_SMOOTH_INVERT_MATERIAL = (1 << 4), + GP_SMOOTH_CUSTOM_CURVE = (1 << 8), } eSmoothGpencil_Flag; typedef struct ArmatureGpencilModifierData { @@ -677,4 +700,41 @@ typedef enum eMultiplyGpencil_Flag { GP_MULTIPLY_ENABLE_FADING = (1 << 2), } eMultiplyGpencil_Flag; +typedef struct VertexcolorGpencilModifierData { + GpencilModifierData modifier; + + struct Object *object; + /** Layer name. */ + char layername[64]; + /** Material name. */ + char materialname[64]; + /** Optional vertexgroup name, MAX_VGROUP_NAME. */ + char vgname[64]; + /** Custom index for passes. */ + int pass_index; + /** Custom index for passes. */ + int layer_pass; + /** Flags. */ + int flag; + /** Mode. */ + int mode; + + float factor; + float radius; + + struct CurveMapping *curve_intensity; + + struct ColorBand *colorband; +} VertexcolorGpencilModifierData; + +typedef enum eVertexcolorGpencil_Flag { + GP_VERTEXCOL_INVERT_LAYER = (1 << 0), + GP_VERTEXCOL_INVERT_PASS = (1 << 1), + GP_VERTEXCOL_INVERT_VGROUP = (1 << 2), + GP_VERTEXCOL_UNIFORM_SPACE = (1 << 3), + GP_VERTEXCOL_INVERT_LAYERPASS = (1 << 4), + GP_VERTEXCOL_INVERT_MATERIAL = (1 << 5), + GP_VERTEXCOL_CUSTOM_CURVE = (1 << 6), +} eVertexcolorGpencil_Flag; + #endif /* __DNA_GPENCIL_MODIFIER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 8908b3c42d9..6059df6f886 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -36,6 +36,8 @@ struct MDeformVert; #define GP_DEFAULT_GRID_LINES 4 #define GP_MAX_INPUT_SAMPLES 10 +#define GP_MATERIAL_BUFFER_LEN 256 + /* ***************************************** */ /* GP Stroke Points */ @@ -78,9 +80,15 @@ typedef struct bGPDspoint { float uv_fac; /** Uv rotation for dot mode. */ float uv_rot; + /** Uv for fill mode */ + float uv_fill[2]; + + /** Vertex Color RGBA (A=mix factor). */ + float vert_color[4]; /** Runtime data */ char _pad2[4]; + bGPDspoint_Runtime runtime; } bGPDspoint; @@ -105,8 +113,6 @@ typedef enum eGPDspoint_Flag { typedef struct bGPDtriangle { /* indices for tessellated triangle used for GP Fill */ unsigned int verts[3]; - /* texture coordinates for verts */ - float uv[3][2]; } bGPDtriangle; /* ***************************************** */ @@ -166,18 +172,17 @@ typedef enum eGPDpalette_Flag { /* Runtime temp data for bGPDstroke */ typedef struct bGPDstroke_Runtime { - /** runtime final colors (result of original colors and modifiers) */ - float tmp_stroke_rgba[4]; - - /** runtime final fill colors (result of original colors and modifiers) */ - float tmp_fill_rgba[4]; - /** temporary layer name only used during copy/paste to put the stroke in the original layer */ char tmp_layerinfo[128]; /** Runtime falloff factor (only for transform). */ float multi_frame_falloff; - char _pad[4]; + + /** Vertex offset in the vbo where this stroke starts. */ + int stroke_start; + /** Triangle offset in the ibo where this fill starts. */ + int fill_start; + int _pad[1]; /** Original stroke (used to dereference evaluated data) */ struct bGPDstroke *gps_orig; @@ -217,15 +222,31 @@ typedef struct bGPDstroke { short caps[2]; /** gradient control along y for color */ - float gradient_f; + float hardeness; /** factor xy of shape for dots gradients */ - float gradient_s[2]; - char _pad_3[4]; + float aspect_ratio[2]; + + /** Factor of opacity for Fill color (used by opacity modifier). */ + float fill_opacity_fac; + + /** Min of the bound box used to speedup painting operators. */ + float boundbox_min[3]; + /** Max of the bound box used to speedup painting operators. */ + float boundbox_max[3]; + + /** UV rotation */ + float uv_rotation; + /** UV translation (X and Y axis) */ + float uv_translation[2]; + float uv_scale; /** Vertex weight data. */ struct MDeformVert *dvert; void *_pad3; + /** Vertex Color for Fill (one for all stroke, A=mix factor). */ + float vert_color_fill[4]; + bGPDstroke_Runtime runtime; } bGPDstroke; @@ -239,14 +260,13 @@ typedef enum eGPDstroke_Flag { GP_STROKE_2DIMAGE = (1 << 2), /* stroke is selected */ GP_STROKE_SELECT = (1 << 3), - /* Recalculate geometry data (triangulation, UVs, Bound Box,... - * (when true, force a new recalc) */ - GP_STROKE_RECALC_GEOMETRY = (1 << 4), /* Flag used to indicate that stroke is closed and draw edge between last and first point */ GP_STROKE_CYCLIC = (1 << 7), /* Flag used to indicate that stroke is used for fill close and must use * fill color for stroke and no fill area */ GP_STROKE_NOFILL = (1 << 8), + /* Tag for update geometry */ + GP_STROKE_TAG = (1 << 14), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15), } eGPDstroke_Flag; @@ -265,8 +285,13 @@ typedef enum eGPDstroke_Caps { /* Runtime temp data for bGPDframe */ typedef struct bGPDframe_Runtime { - /** Parent matrix for drawing. */ - float parent_obmat[4][4]; + /** Index of this frame in the listbase of frames. */ + int frameid; + /** Onion offset from active frame. 0 if not onion. INT_MAX to bypass frame. */ + int onion_id; + + /** Original frame (used to dereference evaluated data) */ + struct bGPDframe *gpf_orig; } bGPDframe_Runtime; /* Grease-Pencil Annotations - 'Frame' @@ -300,11 +325,31 @@ typedef enum eGPDframe_Flag { /* ***************************************** */ /* GP Layer */ +/* List of masking layers. */ +typedef struct bGPDlayer_Mask { + struct bGPDlayer_Mask *next, *prev; + char name[128]; + short flag; + /** Index for sorting. Only valid while sorting algorithm is running. */ + short sort_index; + char _pad[4]; +} bGPDlayer_Mask; + +/* bGPDlayer_Mask->flag */ +typedef enum ebGPDlayer_Mask_Flag { + /* Mask is hidden. */ + GP_MASK_HIDE = (1 << 0), + /* Mask is inverted. */ + GP_MASK_INVERT = (1 << 1), +} ebGPDlayer_Mask_Flag; + /* Runtime temp data for bGPDlayer */ typedef struct bGPDlayer_Runtime { /** Id for dynamic icon used to show annotation color preview for layer. */ int icon_id; char _pad[4]; + /** Original layer (used to dereference evaluated data) */ + struct bGPDlayer *gpl_orig; } bGPDlayer_Runtime; /* Grease-Pencil Annotations - 'Layer' */ @@ -355,7 +400,8 @@ typedef struct bGPDlayer { /** Blend modes. */ int blend_mode; - char _pad[4]; + /** Vertex Paint opacity by Layer. */ + float vertex_paint_opacity; /* annotation onion skin */ /** @@ -375,6 +421,12 @@ typedef struct bGPDlayer { float gcolor_next[3]; char _pad1[4]; + /** Mask list (bGPDlayer_Mask). */ + ListBase mask_layers; + /** Current Mask index (noted base 1). */ + int act_mask; + char _pad2[4]; + bGPDlayer_Runtime runtime; } bGPDlayer; @@ -388,6 +440,8 @@ typedef enum eGPDlayer_Flag { GP_LAYER_ACTIVE = (1 << 2), /* draw points of stroke for debugging purposes */ GP_LAYER_DRAWDEBUG = (1 << 3), + /* Flag used to display in Paint mode only layers with keyframe */ + GP_LAYER_SOLO_MODE = (1 << 4), /* for editing in Action Editor */ GP_LAYER_SELECT = (1 << 5), /* current frame for layer can't be changed */ @@ -396,12 +450,12 @@ typedef enum eGPDlayer_Flag { GP_LAYER_NO_XRAY = (1 << 7), /* "volumetric" strokes */ GP_LAYER_VOLUMETRIC = (1 << 10), + /* Use Scene lights */ + GP_LAYER_USE_LIGHTS = (1 << 11), /* Unlock color */ GP_LAYER_UNLOCK_COLOR = (1 << 12), /* Mask Layer */ - GP_LAYER_USE_MASK = (1 << 13), - /* Flag used to display in Paint mode only layers with keyframe */ - GP_LAYER_SOLO_MODE = (1 << 4), + GP_LAYER_USE_MASK = (1 << 13), /*TODO: DEPRECATED */ /* Ruler Layer */ GP_LAYER_IS_RULER = (1 << 14), } eGPDlayer_Flag; @@ -431,18 +485,15 @@ typedef struct bGPdata_Runtime { struct ARegion *ar; /** Stroke buffer. */ void *sbuffer; + /** Temp batches cleared after drawing. */ + struct GPUBatch *sbuffer_stroke_batch; + struct GPUBatch *sbuffer_fill_batch; + /** Temp stroke used for drawing. */ + struct bGPDstroke *sbuffer_gps; - /* GP Object drawing */ - /** Buffer stroke color. */ - float scolor[4]; - /** Buffer fill color. */ - float sfill[4]; - /** Settings for color. */ - short mode; - /** Buffer style for drawing strokes (used to select shader type). */ - short bstroke_style; - /** Buffer style for filling areas (used to select shader type). */ - short bfill_style; + char _pad[2]; + /** Material index of the stroke. */ + short matid; /* Stroke Buffer data (only used during paint-session) * - buffer must be initialized before use, but freed after @@ -450,16 +501,26 @@ typedef struct bGPdata_Runtime { */ /** Flags for stroke that cache represents. */ short sbuffer_sflag; + char _pad1[2]; /** Number of elements currently used in cache. */ int sbuffer_used; /** Number of total elements available in cache. */ int sbuffer_size; + /** Vertex Color applied to point (while drawing). */ + float vert_color[4]; + + /** Vertex Color applied to Fill (while drawing). */ + float vert_color_fill[4]; + /** Number of control-points for stroke. */ int tot_cp_points; - char _pad_[4]; + char _pad2[4]; /** Array of control-points for stroke. */ bGPDcontrolpoint *cp_points; + /** Brush pointer */ + Brush *sbuffer_brush; + struct GpencilBatchCache *gpencil_cache; } bGPdata_Runtime; /* grid configuration */ @@ -481,7 +542,7 @@ typedef struct bGPdata { struct AnimData *adt; /* Grease-Pencil data */ - /** BGPDlayers. */ + /** bGPDlayer. */ ListBase layers; /** Settings for this data-block. */ int flag; @@ -581,8 +642,6 @@ typedef enum eGPdata_Flag { /* Main flag to switch onion skinning on/off */ GP_DATA_SHOW_ONIONSKINS = (1 << 9), - /* Draw a green and red point to indicate start and end of the stroke */ - GP_DATA_SHOW_DIRECTION = (1 << 10), /* Batch drawing cache need to be recalculated */ GP_DATA_CACHE_IS_DIRTY = (1 << 11), @@ -600,15 +659,9 @@ typedef enum eGPdata_Flag { /* Allow edit several frames at the same time */ GP_DATA_STROKE_MULTIEDIT = (1 << 16), - /* Force fill recalc if use deformation modifiers. - * this is required if the stroke is deformed and the triangulation data is - * not valid. - */ - GP_DATA_STROKE_FORCE_RECALC = (1 << 17), - /* Special mode drawing polygons */ - GP_DATA_STROKE_POLYGON = (1 << 18), - /* Use adaptive UV scales */ - GP_DATA_UV_ADAPTIVE = (1 << 19), + /* Vertex Paint Mode - Toggle paint mode */ + GP_DATA_STROKE_VERTEXMODE = (1 << 18), + /* Autolock not active layers */ GP_DATA_AUTOLOCK_LAYERS = (1 << 20), /* Internal flag for python update */ @@ -654,28 +707,38 @@ typedef enum eGP_DrawMode { /* Check if 'multiedit sessions' is enabled */ #define GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) \ ((gpd) && \ - (gpd->flag & \ - (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE)) && \ + (gpd->flag & (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | \ + GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE)) && \ (gpd->flag & GP_DATA_STROKE_MULTIEDIT)) /* Macros to check grease pencil modes */ #define GPENCIL_ANY_MODE(gpd) \ - ((gpd) && (gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE | \ - GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) -#define GPENCIL_EDIT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) + ((gpd) && \ + (gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | \ + GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE))) +#define GPENCIL_EDIT_MODE(gpd) ((gpd) && ((gpd)->flag & GP_DATA_STROKE_EDITMODE)) #define GPENCIL_ANY_EDIT_MODE(gpd) \ ((gpd) && (gpd->flag & \ (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) #define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_PAINTMODE))) #define GPENCIL_SCULPT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_SCULPTMODE)) #define GPENCIL_WEIGHT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) +#define GPENCIL_VERTEX_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_VERTEXMODE))) #define GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd) \ ((gpd) && (gpd->flag & (GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) #define GPENCIL_NONE_EDIT_MODE(gpd) \ ((gpd) && ((gpd->flag & (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | \ - GP_DATA_STROKE_WEIGHTMODE)) == 0)) + GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE)) == 0)) #define GPENCIL_LAZY_MODE(brush, shift) \ (((brush) && ((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) && (shift == 0))) || \ (((brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && (shift == 1))) +#define GPENCIL_ANY_SCULPT_MASK(flag) \ + ((flag & (GP_SCULPT_MASK_SELECTMODE_POINT | GP_SCULPT_MASK_SELECTMODE_STROKE | \ + GP_SCULPT_MASK_SELECTMODE_SEGMENT))) + +#define GPENCIL_ANY_VERTEX_MASK(flag) \ + ((flag & (GP_VERTEX_MASK_SELECTMODE_POINT | GP_VERTEX_MASK_SELECTMODE_STROKE | \ + GP_VERTEX_MASK_SELECTMODE_SEGMENT))) + #endif /* __DNA_GPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 1d1ccef8846..0db7feffa99 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -72,15 +72,14 @@ typedef struct MaterialGPencilStyle { /** Factor used to define shader behavior (several uses). */ float mix_factor; /** Angle used for gradients orientation. */ - float gradient_angle; + float gradient_angle DNA_DEPRECATED; /** Radius for radial gradients. */ - float gradient_radius; - /** Cheesboard size. */ - float pattern_gridsize; + float gradient_radius DNA_DEPRECATED; + char _pad2[4]; /** Uv coordinates scale. */ - float gradient_scale[2]; + float gradient_scale[2] DNA_DEPRECATED; /** Factor to shift filling in 2d space. */ - float gradient_shift[2]; + float gradient_shift[2] DNA_DEPRECATED; /** Angle used for texture orientation. */ float texture_angle; /** Texture scale (separated of uv scale). */ @@ -107,35 +106,35 @@ typedef struct MaterialGPencilStyle { /* MaterialGPencilStyle->flag */ typedef enum eMaterialGPencilStyle_Flag { /* Fill Texture is a pattern */ - GP_STYLE_FILL_PATTERN = (1 << 0), + GP_MATERIAL_FILL_PATTERN = (1 << 0), /* don't display color */ - GP_STYLE_COLOR_HIDE = (1 << 1), + GP_MATERIAL_HIDE = (1 << 1), /* protected from further editing */ - GP_STYLE_COLOR_LOCKED = (1 << 2), + GP_MATERIAL_LOCKED = (1 << 2), /* do onion skinning */ - GP_STYLE_COLOR_ONIONSKIN = (1 << 3), + GP_MATERIAL_ONIONSKIN = (1 << 3), /* clamp texture */ - GP_STYLE_COLOR_TEX_CLAMP = (1 << 4), + GP_MATERIAL_TEX_CLAMP = (1 << 4), /* mix fill texture */ - GP_STYLE_FILL_TEX_MIX = (1 << 5), + GP_MATERIAL_FILL_TEX_MIX = (1 << 5), /* Flip fill colors */ - GP_STYLE_COLOR_FLIP_FILL = (1 << 6), + GP_MATERIAL_FLIP_FILL = (1 << 6), /* Stroke Texture is a pattern */ - GP_STYLE_STROKE_PATTERN = (1 << 7), + GP_MATERIAL_STROKE_PATTERN = (1 << 7), /* Stroke show main switch */ - GP_STYLE_STROKE_SHOW = (1 << 8), - /* Fill show main switch */ - GP_STYLE_FILL_SHOW = (1 << 9), + GP_MATERIAL_STROKE_SHOW = (1 << 8), + /* Fill show main switch */ + GP_MATERIAL_FILL_SHOW = (1 << 9), /* mix stroke texture */ - GP_STYLE_STROKE_TEX_MIX = (1 << 11), + GP_MATERIAL_STROKE_TEX_MIX = (1 << 11), /* disable stencil clipping (overlap) */ - GP_STYLE_DISABLE_STENCIL = (1 << 12), + GP_MATERIAL_DISABLE_STENCIL = (1 << 12), } eMaterialGPencilStyle_Flag; typedef enum eMaterialGPencilStyle_Mode { - GP_STYLE_MODE_LINE = 0, /* line */ - GP_STYLE_MODE_DOTS = 1, /* dots */ - GP_STYLE_MODE_BOX = 2, /* rectangles */ + GP_MATERIAL_MODE_LINE = 0, + GP_MATERIAL_MODE_DOT = 1, + GP_MATERIAL_MODE_SQUARE = 2, } eMaterialGPencilStyle_Mode; typedef struct Material { @@ -332,28 +331,28 @@ enum { /* Grease Pencil Stroke styles */ enum { - GP_STYLE_STROKE_STYLE_SOLID = 0, - GP_STYLE_STROKE_STYLE_TEXTURE, + GP_MATERIAL_STROKE_STYLE_SOLID = 0, + GP_MATERIAL_STROKE_STYLE_TEXTURE, }; /* Grease Pencil Fill styles */ enum { - GP_STYLE_FILL_STYLE_SOLID = 0, - GP_STYLE_FILL_STYLE_GRADIENT, - GP_STYLE_FILL_STYLE_CHECKER, - GP_STYLE_FILL_STYLE_TEXTURE, + GP_MATERIAL_FILL_STYLE_SOLID = 0, + GP_MATERIAL_FILL_STYLE_GRADIENT, + GP_MATERIAL_FILL_STYLE_CHECKER, /* DEPRECATED (only for convert old files) */ + GP_MATERIAL_FILL_STYLE_TEXTURE, }; /* Grease Pencil Gradient Types */ enum { - GP_STYLE_GRADIENT_LINEAR = 0, - GP_STYLE_GRADIENT_RADIAL, + GP_MATERIAL_GRADIENT_LINEAR = 0, + GP_MATERIAL_GRADIENT_RADIAL, }; /* Grease Pencil Follow Drawing Modes */ enum { - GP_STYLE_FOLLOW_PATH = 0, - GP_STYLE_FOLLOW_OBJ, - GP_STYLE_FOLLOW_FIXED, + GP_MATERIAL_FOLLOW_PATH = 0, + GP_MATERIAL_FOLLOW_OBJ, + GP_MATERIAL_FOLLOW_FIXED, }; #endif diff --git a/source/blender/makesdna/DNA_object_enums.h b/source/blender/makesdna/DNA_object_enums.h index c15f32564c4..f3e69161c85 100644 --- a/source/blender/makesdna/DNA_object_enums.h +++ b/source/blender/makesdna/DNA_object_enums.h @@ -37,6 +37,7 @@ typedef enum eObjectMode { OB_MODE_PAINT_GPENCIL = 1 << 8, OB_MODE_SCULPT_GPENCIL = 1 << 9, OB_MODE_WEIGHT_GPENCIL = 1 << 10, + OB_MODE_VERTEX_GPENCIL = 1 << 11, } eObjectMode; /** #Object.dt, #View3DShading.type */ @@ -54,7 +55,8 @@ typedef enum eDrawType { (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT) #define OB_MODE_ALL_PAINT_GPENCIL \ - (OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL) + (OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | \ + OB_MODE_VERTEX_GPENCIL) /** Any mode that uses Object.sculpt. */ #define OB_MODE_ALL_SCULPT (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) @@ -66,6 +68,6 @@ typedef enum eDrawType { #define OB_MODE_ALL_MODE_DATA \ (OB_MODE_EDIT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_SCULPT | OB_MODE_POSE | \ OB_MODE_PAINT_GPENCIL | OB_MODE_EDIT_GPENCIL | OB_MODE_SCULPT_GPENCIL | \ - OB_MODE_WEIGHT_GPENCIL) + OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL) #endif /* __DNA_OBJECT_ENUMS_H__ */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 4d15abc3c77..fca74c29909 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -166,6 +166,18 @@ typedef struct Object_Runtime { */ struct Mesh *mesh_deform_eval; + /** + * Original grease pencil bGPdata pointer, before object->data was changed to point + * to gpd_eval. + * Is assigned by dependency graph's copy-on-write evaluation. + */ + struct bGPdata *gpd_orig; + /** + * bGPdata structure created during object evaluation. + * It has all modifiers applied. + */ + struct bGPdata *gpd_eval; + /** * This is a mesh representation of corresponding object. * It created when Python calls `object.to_mesh()`. @@ -175,14 +187,6 @@ typedef struct Object_Runtime { /** Runtime evaluated curve-specific data, not stored in the file. */ struct CurveCache *curve_cache; - /** Runtime grease pencil drawing data */ - struct GpencilBatchCache *gpencil_cache; - /** Runtime grease pencil total layers used for evaluated data created by modifiers */ - int gpencil_tot_layers; - char _pad4[4]; - /** Runtime grease pencil evaluated data created by modifiers */ - struct bGPDframe *gpencil_evaluated_frames; - unsigned short local_collections_bits; short _pad2[3]; } Object_Runtime; @@ -542,6 +546,8 @@ enum { OB_DRAWTRANSP = 1 << 7, OB_DRAW_ALL_EDGES = 1 << 8, /* only for meshes currently */ OB_DRAW_NO_SHADOW_CAST = 1 << 9, + /* Enable lights for grease pencil. */ + OB_USE_GPENCIL_LIGHTS = 1 << 10, }; /* empty_drawtype: no flags */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 8617f122d55..3a88cd0a33b 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -999,8 +999,38 @@ typedef struct UvSculpt { /* grease pencil drawing brushes */ typedef struct GpPaint { Paint paint; + int flag; + /* Mode of paint (Materials or Vertex Color). */ + int mode; } GpPaint; +/* GpPaint.flag */ +enum { + GPPAINT_FLAG_USE_MATERIAL = 0, + GPPAINT_FLAG_USE_VERTEXCOLOR = 1, +}; + +/* Grease pencil vertex paint. */ +typedef struct GpVertexPaint { + Paint paint; + int flag; + char _pad[4]; +} GpVertexPaint; + +/* Grease pencil sculpt paint. */ +typedef struct GpSculptPaint { + Paint paint; + int flag; + char _pad[4]; +} GpSculptPaint; + +/* Grease pencil weight paint. */ +typedef struct GpWeightPaint { + Paint paint; + int flag; + char _pad[4]; +} GpWeightPaint; + /* ------------------------------------------- */ /* Vertex Paint */ @@ -1022,27 +1052,6 @@ enum { /* ------------------------------------------- */ /* GPencil Stroke Sculpting */ -/* GP_Sculpt_Settings.brushtype */ -typedef enum eGP_Sculpt_Types { - GP_SCULPT_TYPE_SMOOTH = 0, - GP_SCULPT_TYPE_THICKNESS = 1, - GP_SCULPT_TYPE_STRENGTH = 2, - GP_SCULPT_TYPE_GRAB = 3, - GP_SCULPT_TYPE_PUSH = 4, - GP_SCULPT_TYPE_TWIST = 5, - GP_SCULPT_TYPE_PINCH = 6, - GP_SCULPT_TYPE_RANDOMIZE = 7, - GP_SCULPT_TYPE_CLONE = 8, - GP_SCULPT_TYPE_SUBDIVIDE = 9, - GP_SCULPT_TYPE_SIMPLIFY = 10, - /* add any sculpt brush above this value */ - GP_SCULPT_TYPE_WEIGHT = 11, - /* add any weight paint brush below this value. Do no mix brushes */ - - /* !!! Update GP_Sculpt_Data brush[###]; below !!! */ - GP_SCULPT_TYPE_MAX, -} eGP_Sculpt_Types; - /* GP_Sculpt_Settings.lock_axis */ typedef enum eGP_Lockaxis_Types { GP_LOCKAXIS_VIEW = 0, @@ -1052,23 +1061,6 @@ typedef enum eGP_Lockaxis_Types { GP_LOCKAXIS_CURSOR = 4, } eGP_Lockaxis_Types; -/* Settings for a GPencil Stroke Sculpting Brush */ -typedef struct GP_Sculpt_Data { - /** Radius of brush. */ - short size; - /** EGP_Sculpt_Flag. */ - short flag; - /** Strength of effect. */ - float strength; - /** Cursor color for add. */ - float curcolor_add[3]; - /** Cursor color for sub. */ - float curcolor_sub[3]; - /** Target weight. */ - float weight; - char _pad[4]; -} GP_Sculpt_Data; - /* Settings for a GPencil Speed Guide */ typedef struct GP_Sculpt_Guide { char use_guide; @@ -1083,48 +1075,17 @@ typedef struct GP_Sculpt_Guide { struct Object *reference_object; } GP_Sculpt_Guide; -/* GP_Sculpt_Data.flag */ -typedef enum eGP_Sculpt_Flag { - /* invert the effect of the brush */ - GP_SCULPT_FLAG_INVERT = (1 << 0), - /* adjust strength using pen pressure */ - GP_SCULPT_FLAG_USE_PRESSURE = (1 << 1), - - /* strength of brush falls off with distance from cursor */ - GP_SCULPT_FLAG_USE_FALLOFF = (1 << 2), - - /* smooth brush affects pressure values as well */ - GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 3), - /* enable screen cursor */ - GP_SCULPT_FLAG_ENABLE_CURSOR = (1 << 4), - /* temporary invert action */ - GP_SCULPT_FLAG_TMP_INVERT = (1 << 5), - /* adjust radius using pen pressure */ - GP_SCULPT_FLAG_PRESSURE_RADIUS = (1 << 6), -} eGP_Sculpt_Flag; - /* GPencil Stroke Sculpting Settings */ typedef struct GP_Sculpt_Settings { - /** GP_SCULPT_TYPE_MAX. */ - GP_Sculpt_Data brush[12]; /** Runtime. */ void *paintcursor; - - /** #eGP_Sculpt_Types (sculpt). */ - int brushtype; /** #eGP_Sculpt_SettingsFlag. */ int flag; /** #eGP_Lockaxis_Types lock drawing to one axis. */ int lock_axis; /** Threshold for intersections */ float isect_threshold; - - /* weight paint is a submode of sculpt but use its own index. All weight paint - * brushes must be defined at the end of the brush array. - */ - /** #eGP_Sculpt_Types (weight paint). */ - int weighttype; - char _pad[4]; + char _pad_[4]; /** Multiframe edit falloff effect by frame. */ struct CurveMapping *cur_falloff; /** Curve used for primitive tools. */ @@ -1135,22 +1096,12 @@ typedef struct GP_Sculpt_Settings { /* GP_Sculpt_Settings.flag */ typedef enum eGP_Sculpt_SettingsFlag { - /* only affect selected points */ - GP_SCULPT_SETT_FLAG_DEPRECATED = (1 << 0), - /* apply brush to position */ - GP_SCULPT_SETT_FLAG_APPLY_POSITION = (1 << 1), - /* apply brush to strength */ - GP_SCULPT_SETT_FLAG_APPLY_STRENGTH = (1 << 2), - /* apply brush to thickness */ - GP_SCULPT_SETT_FLAG_APPLY_THICKNESS = (1 << 3), - /* apply brush to thickness */ - GP_SCULPT_SETT_FLAG_WEIGHT_MODE = (1 << 4), /* enable falloff for multiframe editing */ - GP_SCULPT_SETT_FLAG_FRAME_FALLOFF = (1 << 5), - /* apply brush to uv data */ - GP_SCULPT_SETT_FLAG_APPLY_UV = (1 << 6), + GP_SCULPT_SETT_FLAG_FRAME_FALLOFF = (1 << 0), /* apply primitive curve */ - GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE = (1 << 7), + GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE = (1 << 1), + /* Scale thickness. */ + GP_SCULPT_SETT_FLAG_SCALE_THICKNESS = (1 << 3), } eGP_Sculpt_SettingsFlag; /* GP_Sculpt_Settings.gpencil_selectmode_sculpt */ @@ -1163,6 +1114,16 @@ typedef enum eGP_Sculpt_SelectMaskFlag { GP_SCULPT_MASK_SELECTMODE_SEGMENT = (1 << 2), } eGP_Sculpt_SelectMaskFlag; +/* GP_Sculpt_Settings.gpencil_selectmode_vertex */ +typedef enum eGP_vertex_SelectMaskFlag { + /* only affect selected points */ + GP_VERTEX_MASK_SELECTMODE_POINT = (1 << 0), + /* only affect selected strokes */ + GP_VERTEX_MASK_SELECTMODE_STROKE = (1 << 1), + /* only affect selected segmenst */ + GP_VERTEX_MASK_SELECTMODE_SEGMENT = (1 << 2), +} eGP_Vertex_SelectMaskFlag; + /* Settings for GP Interpolation Operators */ typedef struct GP_Interpolate_Settings { /** #eGP_Interpolate_SettingsFlag. */ @@ -1395,6 +1356,12 @@ typedef struct ToolSettings { UvSculpt *uvsculpt; /** Gpencil paint. */ GpPaint *gp_paint; + /** Gpencil vertex paint. */ + GpVertexPaint *gp_vertexpaint; + /** Gpencil sculpt paint. */ + GpSculptPaint *gp_sculptpaint; + /** Gpencil weight paint. */ + GpWeightPaint *gp_weightpaint; /* Vertex group weight - used only for editmode, not weight * paint */ @@ -1508,8 +1475,11 @@ typedef struct ToolSettings { /** Subset selection filter in wpaint. */ char vgroupsubset; + /** Stroke selection mode for Vertex Paint. */ + char gpencil_selectmode_vertex; + /* UV painting */ - char _pad2[3]; + char _pad2[2]; char uv_sculpt_settings; char uv_relax_method; /* XXX: these sculpt_paint_* fields are deprecated, use the @@ -2289,14 +2259,12 @@ typedef enum eGPencil_SimplifyFlags { SIMPLIFY_GPENCIL_FILL = (1 << 2), /* Simplify modifier on viewport */ SIMPLIFY_GPENCIL_MODIFIER = (1 << 3), - /* Remove fill external line */ - SIMPLIFY_GPENCIL_REMOVE_FILL_LINE = (1 << 4), /* Simplify Shader FX */ SIMPLIFY_GPENCIL_FX = (1 << 5), - /* Simplify layer blending */ - SIMPLIFY_GPENCIL_BLEND = (1 << 6), /* Simplify layer tint */ SIMPLIFY_GPENCIL_TINT = (1 << 7), + /* Simplify Antialiasing */ + SIMPLIFY_GPENCIL_AA = (1 << 8), } eGPencil_SimplifyFlags; /* ToolSettings.gpencil_*_align - Stroke Placement mode flags */ diff --git a/source/blender/makesdna/DNA_shader_fx_types.h b/source/blender/makesdna/DNA_shader_fx_types.h index 6cda58a3279..e0931d8cac3 100644 --- a/source/blender/makesdna/DNA_shader_fx_types.h +++ b/source/blender/makesdna/DNA_shader_fx_types.h @@ -34,7 +34,7 @@ typedef enum ShaderFxType { eShaderFxType_None = 0, eShaderFxType_Blur = 1, eShaderFxType_Flip = 2, - eShaderFxType_Light = 3, + eShaderFxType_Light_deprecated = 3, /* DEPRECATED (replaced by scene lights) */ eShaderFxType_Pixel = 4, eShaderFxType_Swirl = 5, eShaderFxType_Wave = 6, @@ -81,15 +81,13 @@ typedef struct ShaderFxData_Runtime { typedef struct BlurShaderFxData { ShaderFxData shaderfx; - int radius[2]; + float radius[2]; /** Flags. */ int flag; /** Number of samples. */ int samples; - /** Circle of confusion. */ - float coc; - /** Not visible in rna. */ - int blur[2]; + /** Rotation of blur effect. */ + float rotation; char _pad[4]; ShaderFxData_Runtime runtime; @@ -136,14 +134,20 @@ typedef enum eFlipShaderFx_Flag { typedef struct GlowShaderFxData { ShaderFxData shaderfx; - float glow_color[3]; + float glow_color[4]; float select_color[3]; float threshold; /** Flags. */ int flag; int mode; - int blur[2]; + float blur[2]; int samples; + /** Rotation of effect. */ + float rotation; + /** Blend modes. */ + int blend_mode; + char _pad[4]; + ShaderFxData_Runtime runtime; } GlowShaderFxData; @@ -156,19 +160,6 @@ typedef enum eGlowShaderFx_Flag { FX_GLOW_USE_ALPHA = (1 << 0), } eGlowShaderFx_Flag; -typedef struct LightShaderFxData { - ShaderFxData shaderfx; - struct Object *object; - /** Flags. */ - int flag; - float energy; - float ambient; - /** Internal, not visible in rna. */ - float loc[4]; - char _pad[4]; - ShaderFxData_Runtime runtime; -} LightShaderFxData; - typedef struct PixelShaderFxData { ShaderFxData shaderfx; /** Last element used for shader only. */ @@ -179,10 +170,6 @@ typedef struct PixelShaderFxData { ShaderFxData_Runtime runtime; } PixelShaderFxData; -typedef enum ePixelShaderFx_Flag { - FX_PIXEL_USE_LINES = (1 << 0), -} ePixelShaderFx_Flag; - typedef struct RimShaderFxData { ShaderFxData shaderfx; int offset[2]; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index b3f82bc1269..6961a9e9c3e 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -853,8 +853,7 @@ typedef struct UserDef { short pie_menu_threshold; short opensubdiv_compute_type; - /** #eMultiSample_Type, amount of samples for Grease Pencil. */ - short gpencil_multisamples; + short _pad6; char factor_display_type; @@ -1228,7 +1227,7 @@ typedef enum eNdof_Flag { #define NDOF_PIXELS_PER_SECOND 600.0f -/** UserDef.ogl_multisamples and gpencil_multisamples */ +/** UserDef.ogl_multisamples */ typedef enum eMultiSample_Type { USER_MULTISAMPLE_NONE = 0, USER_MULTISAMPLE_2 = 2, diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 67ef1b35f9d..edc86a4b0ea 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -224,6 +224,9 @@ typedef struct View3DOverlay { float gpencil_grid_opacity; float gpencil_fade_layer; + /** Factor for mixing vertex paint with original color */ + float gpencil_vertex_paint_opacity; + char _pad4[4]; } View3DOverlay; typedef struct View3D_Runtime { @@ -415,13 +418,15 @@ enum { #define V3D_FLAG2_UNUSED_15 (1 << 15) /* cleared */ /** #View3D.gp_flag (short) */ -#define V3D_GP_SHOW_PAPER (1 << 0) /* Activate paper to cover all viewport */ -#define V3D_GP_SHOW_GRID (1 << 1) /* Activate paper grid */ +#define V3D_GP_FADE_OBJECTS (1 << 0) /* Fade all non GP objects */ +#define V3D_GP_SHOW_GRID (1 << 1) /* Activate paper grid */ #define V3D_GP_SHOW_EDIT_LINES (1 << 2) #define V3D_GP_SHOW_MULTIEDIT_LINES (1 << 3) #define V3D_GP_SHOW_ONION_SKIN (1 << 4) /* main switch at view level */ #define V3D_GP_FADE_NOACTIVE_LAYERS (1 << 5) /* fade layers not active */ #define V3D_GP_FADE_NOACTIVE_GPENCIL (1 << 6) /* Fade other GPencil objects */ +#define V3D_GP_SHOW_STROKE_DIRECTION (1 << 7) /* Show Strokes Directions */ +#define V3D_GP_SHOW_MATERIAL_NAME (1 << 8) /* Show Material names */ /** #View3DShading.flag */ enum { diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 1ae7ad6bc70..678baef3cef 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -110,3 +110,7 @@ DNA_STRUCT_RENAME_ELEM(bTheme, ttopbar, space_topbar) DNA_STRUCT_RENAME_ELEM(bTheme, tuserpref, space_preferences) DNA_STRUCT_RENAME_ELEM(bTheme, tv3d, space_view3d) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) +DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_f, hardeness) +DNA_STRUCT_RENAME_ELEM(bGPDstroke, gradient_s, aspect_ratio) +DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_f, hardeness) +DNA_STRUCT_RENAME_ELEM(BrushGpencilSettings, gradient_s, aspect_ratio) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 41858fffcf5..fa8cf6903ad 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -666,6 +666,7 @@ extern StructRNA RNA_UVWarpModifier; extern StructRNA RNA_UnitSettings; extern StructRNA RNA_UnknownType; extern StructRNA RNA_UserSolidLight; +extern StructRNA RNA_VertexcolorGpencilModifier; extern StructRNA RNA_VectorFont; extern StructRNA RNA_VertexGroup; extern StructRNA RNA_VertexGroupElement; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index c10436ae08e..fef98f9da4b 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -122,12 +122,13 @@ extern const EnumPropertyItem rna_enum_brush_uv_sculpt_tool_items[]; extern const EnumPropertyItem rna_enum_brush_vertex_tool_items[]; extern const EnumPropertyItem rna_enum_brush_weight_tool_items[]; extern const EnumPropertyItem rna_enum_brush_gpencil_types_items[]; +extern const EnumPropertyItem rna_enum_brush_gpencil_vertex_types_items[]; +extern const EnumPropertyItem rna_enum_brush_gpencil_sculpt_types_items[]; +extern const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[]; extern const EnumPropertyItem rna_enum_brush_image_tool_items[]; extern const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[]; extern const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[]; -extern const EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[]; -extern const EnumPropertyItem rna_enum_gpencil_weight_brush_items[]; extern const EnumPropertyItem rna_enum_uv_sculpt_tool_items[]; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index c2dbb2973a3..39216009e34 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -149,6 +149,73 @@ const EnumPropertyItem rna_enum_brush_gpencil_types_items[] = { ICON_PANEL_CLOSE, "Erase", "The brush is used for erasing strokes"}, + {GPAINT_TOOL_TINT, + "TINT", + ICON_BRUSH_TEXDRAW, + "Tint", + "The brush is of type used for tinting strokes"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_brush_gpencil_vertex_types_items[] = { + {GPVERTEX_TOOL_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""}, + {GPVERTEX_TOOL_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""}, + {GPVERTEX_TOOL_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""}, + {GPVERTEX_TOOL_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""}, + {GPVERTEX_TOOL_REPLACE, "REPLACE", ICON_BRUSH_BLUR, "Replace", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_brush_gpencil_sculpt_types_items[] = { + {GPSCULPT_TOOL_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", "Smooth stroke points"}, + {GPSCULPT_TOOL_THICKNESS, + "THICKNESS", + ICON_GPBRUSH_THICKNESS, + "Thickness", + "Adjust thickness of strokes"}, + {GPSCULPT_TOOL_STRENGTH, + "STRENGTH", + ICON_GPBRUSH_STRENGTH, + "Strength", + "Adjust color strength of strokes"}, + {GPSCULPT_TOOL_RANDOMIZE, + "RANDOMIZE", + ICON_GPBRUSH_RANDOMIZE, + "Randomize", + "Introduce jitter/randomness into strokes"}, + {GPSCULPT_TOOL_GRAB, + "GRAB", + ICON_GPBRUSH_GRAB, + "Grab", + "Translate the set of points initially within the brush circle"}, + {GPSCULPT_TOOL_PUSH, + "PUSH", + ICON_GPBRUSH_PUSH, + "Push", + "Move points out of the way, as if combing them"}, + {GPSCULPT_TOOL_TWIST, + "TWIST", + ICON_GPBRUSH_TWIST, + "Twist", + "Rotate points around the midpoint of the brush"}, + {GPSCULPT_TOOL_PINCH, + "PINCH", + ICON_GPBRUSH_PINCH, + "Pinch", + "Pull points towards the midpoint of the brush"}, + {GPSCULPT_TOOL_CLONE, + "CLONE", + ICON_GPBRUSH_CLONE, + "Clone", + "Paste copies of the strokes stored on the clipboard"}, + {0, NULL, 0, NULL, NULL}}; + +const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = { + {GPWEIGHT_TOOL_DRAW, + "WEIGHT", + ICON_GPBRUSH_WEIGHT, + "Weight", + "Weight Paint for Vertex Groups"}, {0, NULL, 0, NULL, NULL}, }; @@ -174,7 +241,7 @@ static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = { {GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"}, {0, NULL, 0, NULL, NULL}}; -static EnumPropertyItem rna_enum_gpencil_brush_icons_items[] = { +static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = { {GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""}, {GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""}, {GP_BRUSH_ICON_INK, "INK", ICON_GPBRUSH_INK, "Ink", ""}, @@ -189,6 +256,32 @@ static EnumPropertyItem rna_enum_gpencil_brush_icons_items[] = { {GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", ""}, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem rna_enum_gpencil_brush_sculpt_icons_items[] = { + {GP_BRUSH_ICON_GPBRUSH_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", ""}, + {GP_BRUSH_ICON_GPBRUSH_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", ""}, + {GP_BRUSH_ICON_GPBRUSH_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", ""}, + {GP_BRUSH_ICON_GPBRUSH_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", ""}, + {GP_BRUSH_ICON_GPBRUSH_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", ""}, + {GP_BRUSH_ICON_GPBRUSH_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", ""}, + {GP_BRUSH_ICON_GPBRUSH_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", ""}, + {GP_BRUSH_ICON_GPBRUSH_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", ""}, + {GP_BRUSH_ICON_GPBRUSH_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem rna_enum_gpencil_brush_weight_icons_items[] = { + {GP_BRUSH_ICON_GPBRUSH_WEIGHT, "DRAW", ICON_GPBRUSH_WEIGHT, "Draw", ""}, + {0, NULL, 0, NULL, NULL}, +}; +static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = { + {GP_BRUSH_ICON_VERTEX_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""}, + {GP_BRUSH_ICON_VERTEX_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""}, + {GP_BRUSH_ICON_VERTEX_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""}, + {GP_BRUSH_ICON_VERTEX_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""}, + {GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -1072,19 +1165,19 @@ static void rna_def_gpencil_options(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + /* modes */ + static EnumPropertyItem gppaint_mode_types_items[] = { + {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", "Vertex Color affects to Stroke only"}, + {GPPAINT_MODE_FILL, "FILL", 0, "Fill", "Vertex Color affects to Fill only"}, + {GPPAINT_MODE_BOTH, "BOTH", 0, "Both", "Vertex Color affects to Stroke and Fill"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "BrushGpencilSettings", NULL); RNA_def_struct_sdna(srna, "BrushGpencilSettings"); RNA_def_struct_path_func(srna, "rna_BrushGpencilSettings_path"); RNA_def_struct_ui_text(srna, "Grease Pencil Brush Settings", "Settings for grease pencil brush"); - /* Sensitivity factor for new strokes */ - prop = RNA_def_property(srna, "pen_sensitivity_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_sensitivity"); - RNA_def_property_range(prop, 0.1f, 3.0f); - RNA_def_property_ui_text(prop, "Sensitivity", "Pressure sensitivity factor for new strokes"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Strength factor for new strokes */ prop = RNA_def_property(srna, "pen_strength", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "draw_strength"); @@ -1121,15 +1214,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Randomnes factor for subdivision */ - prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "draw_random_sub"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text( - prop, "Random Subdivision", "Randomness factor for new strokes after subdivision"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Angle when brush is full size */ prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "draw_angle"); @@ -1171,27 +1255,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Thickness smoothing factor for new strokes */ - prop = RNA_def_property(srna, "pen_thick_smooth_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "thick_smoothfac"); - RNA_def_property_range(prop, 0.0, 2.0f); - RNA_def_property_ui_text(prop, - "Smooth Thickness", - "Amount of thickness smoothing to apply after finish newly created " - "strokes, to reduce jitter/noise"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Thickness iterations of the Smoothing factor */ - prop = RNA_def_property(srna, "pen_thick_smooth_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "thick_smoothlvl"); - RNA_def_property_range(prop, 1, 3); - RNA_def_property_ui_text(prop, - "Iterations Thickness", - "Number of times to smooth thickness for newly created strokes"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Subdivision level for new strokes */ prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "draw_subdivide"); @@ -1278,22 +1341,21 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* gradient control */ - prop = RNA_def_property(srna, "gradient_factor", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "gradient_f"); + prop = RNA_def_property(srna, "hardeness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "hardeness"); RNA_def_property_range(prop, 0.001f, 1.0f); RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, - "Border Opacity Factor", - "Amount of gradient for Dot and Box strokes (set to 1 for full solid)"); + RNA_def_property_ui_text( + prop, "Hardeness", "Amount of gradient for Dot and Box strokes (set to 1 for full solid)"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); /* gradient shape ratio */ - prop = RNA_def_property(srna, "gradient_shape", PROP_FLOAT, PROP_XYZ); - RNA_def_property_float_sdna(prop, NULL, "gradient_s"); + prop = RNA_def_property(srna, "aspect", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "aspect_ratio"); RNA_def_property_array(prop, 2); RNA_def_property_range(prop, 0.01f, 1.0f); RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, "Aspect Ratio", ""); + RNA_def_property_ui_text(prop, "Aspect", ""); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_NONE); @@ -1331,12 +1393,45 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); /* brush standard icon */ - prop = RNA_def_property(srna, "gp_icon", PROP_ENUM, PROP_NONE); + prop = RNA_def_property(srna, "gpencil_paint_icon", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "icon_id"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_paint_icons_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); + + prop = RNA_def_property(srna, "gpencil_sculpt_icon", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "icon_id"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_sculpt_icons_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); + + prop = RNA_def_property(srna, "gpencil_weight_icon", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "icon_id"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_icons_items); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_weight_icons_items); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); + prop = RNA_def_property(srna, "gpencil_vertex_icon", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "icon_id"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_vertex_icons_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); + + /* Mode type. */ + prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "vertex_mode"); + RNA_def_property_enum_items(prop, gppaint_mode_types_items); + RNA_def_property_ui_text(prop, "Mode Type", "Defines how vertex color affect to the strokes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + /* Vertex Color mix factor. */ + prop = RNA_def_property(srna, "vertex_color_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "vertex_factor"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Vertex Color Factor", "Factor used to mix vertex color to get final color"); + /* Flags */ prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); @@ -1369,12 +1464,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) "override while drawing"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - prop = RNA_def_property(srna, "use_cursor", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_ENABLE_CURSOR); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_ui_text(prop, "Enable Cursor", "Enable cursor on screen"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - prop = RNA_def_property(srna, "eraser_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "eraser_mode"); RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_eraser_modes_items); @@ -1395,6 +1484,47 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "use_edit_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_flag", GP_SCULPT_FLAG_SMOOTH_PRESSURE); + RNA_def_property_ui_text( + prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "sculpt_flag"); + RNA_def_property_enum_items(prop, prop_direction_items); + RNA_def_property_ui_text(prop, "Direction", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_edit_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_APPLY_POSITION); + RNA_def_property_ui_text(prop, "Affect Position", "The brush affects the position of the point"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_edit_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_APPLY_STRENGTH); + RNA_def_property_ui_text( + prop, "Affect Strength", "The brush affects the color strength of the point"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_edit_thickness", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_APPLY_THICKNESS); + RNA_def_property_ui_text( + prop, "Affect Thickness", "The brush affects the thickness of the point"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "use_edit_uv", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sculpt_mode_flag", GP_SCULPT_FLAGMODE_APPLY_UV); + RNA_def_property_ui_text(prop, "Affect UV", "The brush affects the UV rotation of the point"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* Material */ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); @@ -1713,6 +1843,25 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_enum_items(prop, rna_enum_brush_gpencil_types_items); RNA_def_property_ui_text(prop, "Grease Pencil Draw Tool", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "gpencil_vertex_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gpencil_vertex_tool"); + RNA_def_property_enum_items(prop, rna_enum_brush_gpencil_vertex_types_items); + RNA_def_property_ui_text(prop, "Grease Pencil Vertex Paint Tool", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "gpencil_sculpt_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gpencil_sculpt_tool"); + RNA_def_property_enum_items(prop, rna_enum_brush_gpencil_sculpt_types_items); + RNA_def_property_ui_text(prop, "Grease Pencil Sculpt Paint Tool", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "gpencil_weight_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gpencil_weight_tool"); + RNA_def_property_enum_items(prop, rna_enum_brush_gpencil_weight_types_items); + RNA_def_property_ui_text(prop, "Grease Pencil Weight Paint Tool", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + /** End per mode tool properties. */ prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); @@ -2443,7 +2592,12 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "use_paint_grease_pencil", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_PAINT_GPENCIL); - RNA_def_property_ui_text(prop, "Use Sculpt", "Use this brush in grease pencil drawing mode"); + RNA_def_property_ui_text(prop, "Use Paint", "Use this brush in grease pencil drawing mode"); + + prop = RNA_def_property(srna, "use_vertex_grease_pencil", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_VERTEX_GPENCIL); + RNA_def_property_ui_text( + prop, "Use Vertex", "Use this brush in grease pencil vertex color mode"); /* texture */ prop = RNA_def_property(srna, "texture_slot", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index 579dd92b703..c4c54aac2c6 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -52,6 +52,7 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { {CTX_MODE_EDIT_GPENCIL, "EDIT_GPENCIL", 0, "Grease Pencil Edit", ""}, {CTX_MODE_SCULPT_GPENCIL, "SCULPT_GPENCIL", 0, "Grease Pencil Sculpt", ""}, {CTX_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", 0, "Grease Pencil Weight Paint", ""}, + {CTX_MODE_VERTEX_GPENCIL, "VERTEX_GPENCIL", 0, "Grease Pencil Vertex Paint", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 17163095d4b..e828d9d3d95 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -141,6 +141,7 @@ static EnumPropertyItem rna_enum_gpencil_caps_modes_items[] = { # include "BKE_icons.h" # include "DEG_depsgraph.h" +# include "DEG_depsgraph_build.h" static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { @@ -148,6 +149,28 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } +static void rna_GPencil_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +{ + DEG_id_tag_update(ptr->owner_id, ID_RECALC_TRANSFORM); + DEG_relations_tag_update(bmain); + WM_main_add_notifier(NC_OBJECT | ND_PARENT, ptr->owner_id); + + DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + +static void rna_GPencil_uv_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + /* Force to recalc the UVs. */ + bGPDstroke *gps = (bGPDstroke *)ptr->data; + + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(gps); + + DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + static void rna_GPencil_autolock(Main *bmain, Scene *scene, PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->owner_id; @@ -167,25 +190,6 @@ static void rna_GPencil_editmode_update(Main *UNUSED(bmain), Scene *UNUSED(scene WM_main_add_notifier(NC_SCENE | ND_MODE | NC_MOVIECLIP, NULL); } -/* Recalc UVs and Fill for all strokes. */ -static void rna_GPencil_strokes_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) -{ - bGPdata *gpd = (bGPdata *)ptr->owner_id; - 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) { - BKE_gpencil_triangulate_stroke_fill(gpd, gps); - } - } - } - } - - /* Now do standard updates... */ - DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); -} - /* Poll Callback to filter GP Datablocks to only show those for Annotations */ bool rna_GPencil_datablocks_annotations_poll(PointerRNA *UNUSED(ptr), const PointerRNA value) { @@ -294,6 +298,42 @@ static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value) } } +static char *rna_GPencilLayerMask_path(PointerRNA *ptr) +{ + bGPdata *gpd = (bGPdata *)ptr->owner_id; + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + bGPDlayer_Mask *mask = (bGPDlayer_Mask *)ptr->data; + + char name_layer[sizeof(gpl->info) * 2]; + char name_mask[sizeof(mask->name) * 2]; + + BLI_strescape(name_layer, gpl->info, sizeof(name_layer)); + BLI_strescape(name_mask, mask->name, sizeof(name_mask)); + + return BLI_sprintfN("layers[\"%s\"].mask_layers[\"%s\"]", name_layer, name_mask); +} + +static int rna_GPencil_active_mask_index_get(PointerRNA *ptr) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + return gpl->act_mask - 1; +} + +static void rna_GPencil_active_mask_index_set(PointerRNA *ptr, int value) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + gpl->act_mask = value + 1; +} + +static void rna_GPencil_active_mask_index_range( + PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&gpl->mask_layers) - 1); +} + /* parent types enum */ static const EnumPropertyItem *rna_Object_parent_type_itemf(bContext *UNUSED(C), PointerRNA *ptr, @@ -383,7 +423,7 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, static int rna_GPencil_active_layer_index_get(PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->owner_id; - bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); return BLI_findindex(&gpd->layers, gpl); } @@ -393,7 +433,7 @@ static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value) bGPdata *gpd = (bGPdata *)ptr->owner_id; bGPDlayer *gpl = BLI_findlink(&gpd->layers, value); - BKE_gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_active_set(gpd, gpl); /* Now do standard updates... */ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); @@ -433,7 +473,8 @@ static const EnumPropertyItem *rna_GPencil_active_layer_itemf(bContext *C, item_tmp.name = gpl->info; item_tmp.value = i; - item_tmp.icon = BKE_icon_gplayer_color_ensure(gpl); + item_tmp.icon = (gpd->flag & GP_DATA_ANNOTATIONS) ? BKE_icon_gplayer_color_ensure(gpl) : + ICON_GREASEPENCIL; RNA_enum_item_add(&item, &totitem, &item_tmp); } @@ -460,6 +501,45 @@ static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value) /* now fix animation paths */ BKE_animdata_fix_paths_rename_all(&gpd->id, "layers", oldname, gpl->info); + + /* Fix mask layers. */ + LISTBASE_FOREACH (bGPDlayer *, gpl_, &gpd->layers) { + LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_->mask_layers) { + if (STREQ(mask->name, oldname)) { + BLI_strncpy(mask->name, gpl->info, sizeof(mask->name)); + } + } + } +} + +static void rna_GPencilLayer_mask_info_set(PointerRNA *ptr, const char *value) +{ + bGPdata *gpd = (bGPdata *)ptr->owner_id; + bGPDlayer_Mask *mask = ptr->data; + char oldname[128] = ""; + BLI_strncpy(oldname, mask->name, sizeof(oldname)); + + /* Really is changing the layer name. */ + bGPDlayer *gpl = BKE_gpencil_layer_named_get(gpd, oldname); + if (gpl) { + /* copy the new name into the name slot */ + BLI_strncpy_utf8(gpl->info, value, sizeof(gpl->info)); + + BLI_uniquename( + &gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "layers", oldname, gpl->info); + + /* Fix mask layers. */ + LISTBASE_FOREACH (bGPDlayer *, gpl_, &gpd->layers) { + LISTBASE_FOREACH (bGPDlayer_Mask *, mask_, &gpl_->mask_layers) { + if (STREQ(mask_->name, oldname)) { + BLI_strncpy(mask_->name, gpl->info, sizeof(mask_->name)); + } + } + } + } } static bGPDstroke *rna_GPencil_stroke_point_find_stroke(const bGPdata *gpd, @@ -558,7 +638,8 @@ static void rna_GPencil_stroke_point_add( stroke->totpoints += count; - stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(stroke); gpd->flag |= GP_DATA_PYTHON_UPDATED; DEG_id_tag_update(&gpd->id, @@ -619,7 +700,8 @@ static void rna_GPencil_stroke_point_pop(ID *id, MEM_freeN(pt_dvert); } - stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + /* Calc geometry data. */ + BKE_gpencil_stroke_geometry_update(stroke); gpd->flag |= GP_DATA_PYTHON_UPDATED; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -630,8 +712,9 @@ static void rna_GPencil_stroke_point_pop(ID *id, static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame) { bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - stroke->gradient_f = 1.0f; - ARRAY_SET_ITEMS(stroke->gradient_s, 1.0f, 1.0f); + stroke->hardeness = 1.0f; + ARRAY_SET_ITEMS(stroke->aspect_ratio, 1.0f, 1.0f); + stroke->uv_scale = 1.0f; BLI_addtail(&frame->strokes, stroke); return stroke; @@ -665,7 +748,7 @@ static void rna_GPencil_stroke_close(ID *id, return; } - BKE_gpencil_close_stroke(stroke); + BKE_gpencil_stroke_close(stroke); DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -703,14 +786,14 @@ static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer, { bGPDframe *frame; - if (BKE_gpencil_layer_find_frame(layer, frame_number)) { + if (BKE_gpencil_layer_frame_find(layer, frame_number)) { BKE_reportf(reports, RPT_ERROR, "Frame already exists on this frame number %d", frame_number); return NULL; } frame = BKE_gpencil_frame_addnew(layer, frame_number); if (active) { - layer->actframe = BKE_gpencil_layer_getframe(layer, frame_number, GP_GETFRAME_USE_PREV); + layer->actframe = BKE_gpencil_layer_frame_get(layer, frame_number, GP_GETFRAME_USE_PREV); } WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -725,7 +808,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin return; } - BKE_gpencil_layer_delframe(layer, frame); + BKE_gpencil_layer_frame_delete(layer, frame); RNA_POINTER_INVALIDATE(frame_ptr); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -735,7 +818,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) { bGPDframe *frame = BKE_gpencil_frame_duplicate(src); - while (BKE_gpencil_layer_find_frame(layer, frame->framenum)) { + while (BKE_gpencil_layer_frame_find(layer, frame->framenum)) { frame->framenum++; } @@ -791,6 +874,31 @@ static void rna_GPencil_layer_move(bGPdata *gpd, WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } +static void rna_GPencil_layer_mask_add(bGPDlayer *gpl, PointerRNA *layer_ptr) +{ + bGPDlayer *gpl_mask = layer_ptr->data; + + BKE_gpencil_layer_mask_add(gpl, gpl_mask->info); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static void rna_GPencil_layer_mask_remove(bGPDlayer *gpl, + ReportList *reports, + PointerRNA *mask_ptr) +{ + bGPDlayer_Mask *mask = mask_ptr->data; + if (BLI_findindex(&gpl->mask_layers, mask) == -1) { + BKE_report(reports, RPT_ERROR, "Mask not found in mask list"); + return; + } + + BKE_gpencil_layer_mask_remove(gpl, mask); + RNA_POINTER_INVALIDATE(mask_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + static void rna_GPencil_frame_clear(bGPDframe *frame) { BKE_gpencil_free_strokes(frame); @@ -879,6 +987,16 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set"); RNA_def_property_ui_text(prop, "Select", "Point is selected for viewport editing"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Vertex color. */ + prop = RNA_def_property(srna, "vertex_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "vert_color"); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Vertex Color", "Color used to mix with point color to get final color"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); } static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cprop) @@ -951,27 +1069,6 @@ static void rna_def_gpencil_triangle(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "verts[2]"); RNA_def_property_ui_text(prop, "v3", "Third triangle vertex index"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - /* texture coord for point v1 */ - prop = RNA_def_property(srna, "uv1", PROP_FLOAT, PROP_COORDS); - RNA_def_property_float_sdna(prop, NULL, "uv[0]"); - RNA_def_property_array(prop, 2); - RNA_def_property_ui_text(prop, "uv1", "First triangle vertex texture coordinates"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - /* texture coord for point v2 */ - prop = RNA_def_property(srna, "uv2", PROP_FLOAT, PROP_COORDS); - RNA_def_property_float_sdna(prop, NULL, "uv[1]"); - RNA_def_property_array(prop, 2); - RNA_def_property_ui_text(prop, "uv2", "Second triangle vertex texture coordinates"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - /* texture coord for point v3 */ - prop = RNA_def_property(srna, "uv3", PROP_FLOAT, PROP_COORDS); - RNA_def_property_float_sdna(prop, NULL, "uv[2]"); - RNA_def_property_array(prop, 2); - RNA_def_property_ui_text(prop, "uv3", "Third triangle vertex texture coordinates"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_gpencil_mvert_group(BlenderRNA *brna) @@ -1103,22 +1200,70 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* gradient control along y */ - prop = RNA_def_property(srna, "gradient_factor", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "gradient_f"); + prop = RNA_def_property(srna, "hardeness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "hardeness"); RNA_def_property_range(prop, 0.001f, 1.0f); RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text( - prop, "Border Opacity Factor", "Amount of gradient along section of stroke"); + RNA_def_property_ui_text(prop, "Hardeness", "Amount of gradient along section of stroke"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Stroke bound box */ + prop = RNA_def_property(srna, "bound_box_min", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "boundbox_min"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Boundbox Min", ""); + + prop = RNA_def_property(srna, "bound_box_max", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "boundbox_max"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Boundbox Max", ""); + /* gradient shape ratio */ - prop = RNA_def_property(srna, "gradient_shape", PROP_FLOAT, PROP_XYZ); - RNA_def_property_float_sdna(prop, NULL, "gradient_s"); + prop = RNA_def_property(srna, "aspect", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "aspect_ratio"); RNA_def_property_array(prop, 2); RNA_def_property_range(prop, 0.01f, 1.0f); RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_ui_text(prop, "Aspect Ratio", ""); + RNA_def_property_ui_text(prop, "Aspect", ""); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* UV translation. */ + prop = RNA_def_property(srna, "uv_translation", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "uv_translation"); + RNA_def_property_array(prop, 2); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_ui_text(prop, "UV Translation", "Translation of default UV postion"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_uv_update"); + + /* UV rotation. */ + prop = RNA_def_property(srna, "uv_rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "uv_rotation"); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_ui_text(prop, "UV Rotation", "Rotation of the UV"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_uv_update"); + + /* UV scale. */ + prop = RNA_def_property(srna, "uv_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "uv_scale"); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_range(prop, 0.01f, 100.0f); + RNA_def_property_ui_text(prop, "UV Scale", "Scale of the UV"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_uv_update"); + + /* Vertex Color for Fill. */ + prop = RNA_def_property(srna, "vertex_color_fill", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "vert_color_fill"); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Vertex Fill Color", "Color used to mix with fill color to get final color"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); } @@ -1239,6 +1384,74 @@ static void rna_def_gpencil_frames_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); } +static void rna_def_gpencil_layers_mask_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GreasePencilMaskLayers"); + srna = RNA_def_struct(brna, "GreasePencilMaskLayers", NULL); + RNA_def_struct_sdna(srna, "bGPDlayer"); + RNA_def_struct_ui_text( + srna, "Grease Pencil Mask Layers", "Collection of grease pencil masking layers"); + + prop = RNA_def_property(srna, "active_mask_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, + "rna_GPencil_active_mask_index_get", + "rna_GPencil_active_mask_index_set", + "rna_GPencil_active_mask_index_range"); + RNA_def_property_ui_text(prop, "Active Layer Mask Index", "Active index in layer mask array"); + + func = RNA_def_function(srna, "add", "rna_GPencil_layer_mask_add"); + RNA_def_function_ui_description(func, "Add a layer to mask list"); + parm = RNA_def_pointer(func, "layer", "GPencilLayer", "", "Layer to add as mask"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "remove", "rna_GPencil_layer_mask_remove"); + RNA_def_function_ui_description(func, "Remove a layer from mask list"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "mask", "GPencilLayerMask", "", "Mask to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); +} + +static void rna_def_gpencil_layer_mask(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilLayerMask", NULL); + RNA_def_struct_sdna(srna, "bGPDlayer_Mask"); + RNA_def_struct_ui_text(srna, "Grease Pencil Masking Layers", "List of Mask Layers"); + RNA_def_struct_path_func(srna, "rna_GPencilLayerMask_path"); + + /* Name */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Layer", "Mask layer name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilLayer_mask_info_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, NULL); + + /* Flags */ + prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MASK_HIDE); + RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); + RNA_def_property_ui_text(prop, "Hide", "Set mask Visibility"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "invert", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MASK_INVERT); + RNA_def_property_ui_icon(prop, ICON_CLIPUV_HLT, -1); + RNA_def_property_ui_text(prop, "Invert", "Invert mask"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); +} + static void rna_def_gpencil_layer(BlenderRNA *brna) { StructRNA *srna; @@ -1267,6 +1480,13 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Frames", "Sketches for this layer on different frames"); rna_def_gpencil_frames_api(brna, prop); + /* Mask Layers */ + prop = RNA_def_property(srna, "mask_layers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "mask_layers", NULL); + RNA_def_property_struct_type(prop, "GPencilLayerMask"); + RNA_def_property_ui_text(prop, "Masks", "List of Masking Layers"); + rna_def_gpencil_layers_mask_api(brna, prop); + /* Active Frame */ prop = RNA_def_property(srna, "active_frame", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "actframe"); @@ -1308,7 +1528,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Tint Color */ - prop = RNA_def_property(srna, "tint_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "tint_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "tintcolor"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -1316,12 +1536,20 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Tint factor */ - prop = RNA_def_property(srna, "tint_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "tint_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "tintcolor[3]"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Tint Factor", "Factor of tinting color"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Vertex Paint opacity factor */ + prop = RNA_def_property(srna, "vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "vertex_paint_opacity"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Vertex Paint Opacity", "Vertex Paint mix factor"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Line Thickness Change */ prop = RNA_def_property(srna, "line_change", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "line_change"); @@ -1434,11 +1662,17 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) prop, "Disallow Locked Materials Editing", "Avoids editing locked materials in the layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - prop = RNA_def_property(srna, "mask_layer", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_mask_layer", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_MASK); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Mask Layer", "Mask pixels from underlying layers drawing"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_lights", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_LIGHTS); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( - prop, "Mask Layer", "Remove any pixel outside underlying layers drawing"); + prop, "Use Lights", "Enable the use of lights on stroke and fill materials"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* solo mode: Only display frames with keyframe */ @@ -1454,15 +1688,6 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Ruler", "This is a special ruler layer"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - /* exposed as layers.active */ -# if 0 - prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_ACTIVE); - RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencilLayer_active_set"); - RNA_def_property_ui_text(prop, "Active", "Set active layer for editing"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); -# endif - prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SELECT); RNA_def_property_ui_text(prop, "Select", "Layer is selected for editing in the Dope Sheet"); @@ -1486,7 +1711,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Parent", "Parent Object"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_dependency_update"); /* parent type */ prop = RNA_def_property(srna, "parent_type", PROP_ENUM, PROP_NONE); @@ -1686,7 +1911,9 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "draw_mode"); RNA_def_property_enum_items(prop, rna_enum_gpencil_stroke_depth_order_items); RNA_def_property_ui_text( - prop, "Stroke Depth Order", "Defines how the strokes are ordered in 3D space"); + prop, + "Stroke Depth Order", + "Defines how the strokes are ordered in 3D space (for objects not displayed 'In Front')"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Flags */ @@ -1719,6 +1946,13 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_update( prop, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, "rna_GPencil_editmode_update"); + prop = RNA_def_property(srna, "is_stroke_vertex_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_VERTEXMODE); + RNA_def_property_ui_text(prop, "Stroke Vertex Paint Mode", "Grease Pencil vertex paint"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_update( + prop, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, "rna_GPencil_editmode_update"); + prop = RNA_def_property(srna, "use_onion_skinning", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_ONIONSKINS); RNA_def_property_boolean_default(prop, true); @@ -1727,14 +1961,6 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_update( prop, NC_SCREEN | NC_SCENE | ND_TOOLSETTINGS | ND_DATA | NC_GPENCIL, "rna_GPencil_update"); - prop = RNA_def_property(srna, "show_stroke_direction", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_DIRECTION); - RNA_def_property_ui_text(prop, - "Show Direction", - "Show stroke drawing direction with a bigger green dot (start) " - "and smaller red dot (end) points"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "stroke_thickness_space", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, stroke_thickness_items); @@ -1760,20 +1986,6 @@ static void rna_def_gpencil_data(BlenderRNA *brna) "(keyframes must be selected to be included)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "use_force_fill_recalc", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_FORCE_RECALC); - RNA_def_property_ui_text( - prop, - "Force Fill Update", - "Force recalc of fill data after use deformation modifiers (reduce FPS)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "use_adaptive_uv", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_UV_ADAPTIVE); - RNA_def_property_ui_text( - prop, "Adaptive UV", "Automatic UVs are calculated depending of the stroke size"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_strokes_update"); - prop = RNA_def_property(srna, "use_autolock_layers", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_AUTOLOCK_LAYERS); RNA_def_property_ui_text( @@ -1820,7 +2032,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Custom Ghost Colors", "Use custom colors for ghost frames"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "before_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "before_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "gcolor_prev"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -1829,7 +2041,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_update( prop, NC_SCREEN | NC_SCENE | ND_TOOLSETTINGS | ND_DATA | NC_GPENCIL, "rna_GPencil_update"); - prop = RNA_def_property(srna, "after_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "after_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "gcolor_next"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -1871,10 +2083,8 @@ static void rna_def_gpencil_data(BlenderRNA *brna) prop = RNA_def_property(srna, "use_onion_loop", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "onion_flag", GP_ONION_LOOP); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); - RNA_def_property_ui_text(prop, - "Loop", - "Display first onion keyframes using next frame color to show " - "indication of loop start frame"); + RNA_def_property_ui_text( + prop, "Show Start Frame", "Display onion keyframes for looping animations"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "onion_factor", PROP_FLOAT, PROP_NONE); @@ -1889,6 +2099,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "zdepth_offset"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3); + RNA_def_property_float_default(prop, 0.150f); RNA_def_property_ui_text(prop, "Surface Offset", "Offset amount when drawing in surface mode"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); @@ -1918,6 +2129,7 @@ void RNA_def_gpencil(BlenderRNA *brna) rna_def_gpencil_data(brna); rna_def_gpencil_layer(brna); + rna_def_gpencil_layer_mask(brna); rna_def_gpencil_frame(brna); rna_def_gpencil_stroke(brna); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 53fa3f7459d..0caa93940c8 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -23,6 +23,7 @@ #include #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_mesh_types.h" #include "DNA_gpencil_modifier_types.h" @@ -33,6 +34,7 @@ #include "MEM_guardedalloc.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLT_translation.h" @@ -127,6 +129,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { "Opacity", "Opacity of the strokes"}, {eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"}, + {eGpencilModifierType_Vertexcolor, + "GP_VERTEXCOLOR", + ICON_MOD_NORMALEDIT, + "Vertex Color", + "Apply color changes to Vertex Color"}, {0, NULL, 0, NULL, NULL}, }; @@ -138,16 +145,6 @@ static const EnumPropertyItem modifier_modify_color_items[] = { {0, NULL, 0, NULL, NULL}, }; -static const EnumPropertyItem modifier_opacity_mode_items[] = { - {GP_OPACITY_MODE_MATERIAL, - "MATERIAL", - 0, - "Material", - "Modify opacity using alpha channel of material"}, - {GP_OPACITY_MODE_STRENGTH, "STRENGTH", 0, "Strength", "Modify opacity using point strength"}, - {0, NULL, 0, NULL, NULL}, -}; - static const EnumPropertyItem modifier_gphook_falloff_items[] = { {eGPHook_Falloff_None, "NONE", 0, "No Falloff", ""}, {eGPHook_Falloff_Curve, "CURVE", 0, "Curve", ""}, @@ -168,6 +165,11 @@ static const EnumPropertyItem rna_enum_time_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem gpencil_subdivision_type_items[] = { + {GP_SUBDIV_CATMULL, "CATMULL_CLARK", 0, "Catmull-Clark", ""}, + {GP_SUBDIV_SIMPLE, "SIMPLE", 0, "Simple", ""}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -224,6 +226,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_ArmatureGpencilModifier; case eGpencilModifierType_Multiply: return &RNA_MultiplyGpencilModifier; + case eGpencilModifierType_Vertexcolor: + return &RNA_VertexcolorGpencilModifier; /* Default */ case eGpencilModifierType_None: case NUM_GREASEPENCIL_MODIFIER_TYPES: @@ -338,6 +342,17 @@ static void rna_HookGpencilModifier_object_set(PointerRNA *ptr, BKE_object_modifier_gpencil_hook_reset(ob, hmd); } +static void rna_VertexcolorGpencilModifier_object_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *UNUSED(reports)) +{ + VertexcolorGpencilModifierData *hmd = ptr->data; + Object *ob = (Object *)value.data; + + hmd->object = ob; + id_lib_extern((ID *)ob); +} + static void rna_TimeModifier_start_frame_set(PointerRNA *ptr, int value) { TimeGpencilModifierData *tmd = ptr->data; @@ -360,6 +375,42 @@ static void rna_TimeModifier_end_frame_set(PointerRNA *ptr, int value) } } +static void rna_GpencilOpacity_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + OpacityGpencilModifierData *md = (OpacityGpencilModifierData *)ptr->data; + + *min = 0.0f; + *softmin = 0.0f; + + *softmax = (md->flag & GP_OPACITY_NORMALIZE) ? 1.0f : 2.0f; + *max = *softmax; +} + +static void rna_GpencilOpacity_max_set(PointerRNA *ptr, float value) +{ + OpacityGpencilModifierData *md = (OpacityGpencilModifierData *)ptr->data; + + md->factor = value; + if (md->flag & GP_OPACITY_NORMALIZE) { + if (md->factor > 1.0f) { + md->factor = 1.0f; + } + } +} + +static void rna_GpencilModifier_opacity_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + OpacityGpencilModifierData *md = (OpacityGpencilModifierData *)ptr->data; + if (md->flag & GP_OPACITY_NORMALIZE) { + if (md->factor > 1.0f) { + md->factor = 1.0f; + } + } + + rna_GpencilModifier_update(bmain, scene, ptr); +} + #else static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) @@ -388,15 +439,41 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NoiseGpencilModifier_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "factor"); - RNA_def_property_range(prop, 0, 30.0); - RNA_def_property_ui_text(prop, "Factor", "Amount of noise to apply"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Offset Factor", "Amount of noise to apply"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor_strength", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "factor_strength"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Strength Factor", "Amount of noise to apply to opacity"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor_thickness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "factor_thickness"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Thickness Factor", "Amount of noise to apply to thickness"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor_uvs", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "factor_uvs"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "UV Factor", "Amount of noise to apply uv rotation"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "random", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_USE_RANDOM); - RNA_def_property_ui_text(prop, "Random", "Use random values"); + RNA_def_property_ui_text(prop, "Random", "Use random values over time"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED); @@ -409,33 +486,21 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) prop, "Affect Position", "The modifier affects the position of the point"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_edit_strength", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_MOD_STRENGTH); - RNA_def_property_ui_text( - prop, "Affect Strength", "The modifier affects the color strength of the point"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "use_edit_thickness", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_MOD_THICKNESS); - RNA_def_property_ui_text( - prop, "Affect Thickness", "The modifier affects the thickness of the point"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "use_edit_uv", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_MOD_UV); - RNA_def_property_ui_text( - prop, "Affect UV", "The modifier affects the UV rotation factor of the point"); + prop = RNA_def_property(srna, "noise_scale", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "noise_scale"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "Noise Scale", "Scale the noise frequency"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "full_stroke", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_FULL_STROKE); + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_CUSTOM_CURVE); RNA_def_property_ui_text( - prop, "Full Stroke", "The noise moves the stroke as a whole, not point by point"); + prop, "Custom Curve", "Use a custom curve to define noise effect along the strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "move_extreme", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_MOVE_EXTREME); - RNA_def_property_ui_text(prop, "Move Extremes", "The noise moves the stroke extreme points"); + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); @@ -509,9 +574,9 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna) RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SmoothGpencilModifier_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "factor"); - RNA_def_property_range(prop, 0, 2); + RNA_def_property_range(prop, 0, 1); RNA_def_property_ui_text(prop, "Factor", "Amount of smooth to apply"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -582,6 +647,17 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_INVERT_LAYERPASS); RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define smooth effect along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna) @@ -610,9 +686,10 @@ static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Level", "Number of subdivisions"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "simple", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SUBDIV_SIMPLE); - RNA_def_property_ui_text(prop, "Simple", "The modifier only add control points"); + prop = RNA_def_property(srna, "subdivision_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, gpencil_subdivision_type_items); + RNA_def_property_ui_text(prop, "Subdivision Type", "Select type of subdivision algorithm"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); @@ -663,7 +740,7 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) "ADAPTIVE", ICON_IPO_EASE_IN_OUT, "Adaptive", - "Use a RDP algorithm to simplify the stroke"}, + "Use a Ramer-Douglas-Peucker algorithm to simplify the stroke preserving main shape"}, {GP_SIMPLIFY_SAMPLE, "SAMPLE", ICON_IPO_EASE_IN_OUT, @@ -692,7 +769,7 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Material", "Material name"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "factor"); RNA_def_property_range(prop, 0, 100.0); RNA_def_property_ui_range(prop, 0, 100.0, 1.0f, 3); @@ -744,16 +821,18 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); /* Sample */ - prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "length"); - RNA_def_property_range(prop, 0, 10.0f); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3); RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - /* Distance */ - prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); + /* Merge */ + prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "distance"); - RNA_def_property_range(prop, 0, 100.0f); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3); RNA_def_property_ui_text(prop, "Distance", "Distance between points"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } @@ -787,7 +866,15 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "thickness"); RNA_def_property_range(prop, -100, 500); - RNA_def_property_ui_text(prop, "Thickness", "Factor of thickness change"); + RNA_def_property_ui_text(prop, "Thickness", "Absolute thickness to apply everywhere"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "thickness_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "thickness_fac"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); @@ -829,17 +916,18 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_CUSTOM_CURVE); - RNA_def_property_ui_text(prop, "Custom Curve", "Use a custom curve to define thickness changes"); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define thickness change along the strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "normalize_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_NORMALIZE); - RNA_def_property_ui_text(prop, "Normalize", "Normalize the full stroke to modifier thickness"); + RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "curve_thickness"); - RNA_def_property_ui_text(prop, "Curve", "Custom Thickness Curve"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } @@ -950,7 +1038,7 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Material", "Material name"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rgb"); RNA_def_property_array(prop, 3); @@ -959,15 +1047,10 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna) prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "factor"); - RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 3); + RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 2); RNA_def_property_ui_text(prop, "Factor", "Factor for mixing color"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "create_materials", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TINT_CREATE_COLORS); - RNA_def_property_ui_text(prop, "Create Materials", "When apply modifier, create new material"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -999,6 +1082,17 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TINT_INVERT_LAYERPASS); RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TINT_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define tint effect along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpenciltime(BlenderRNA *brna) @@ -1110,29 +1204,27 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna) prop = RNA_def_property(srna, "hue", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 3); + RNA_def_property_float_default(prop, 0.5); RNA_def_property_float_sdna(prop, NULL, "hsv[0]"); RNA_def_property_ui_text(prop, "Hue", "Color Hue"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "saturation", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_range(prop, 0.0, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 2.0, 0.1, 3); + RNA_def_property_float_default(prop, 1.0); RNA_def_property_float_sdna(prop, NULL, "hsv[1]"); RNA_def_property_ui_text(prop, "Saturation", "Color Saturation"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_range(prop, 0.0, FLT_MAX); RNA_def_property_ui_range(prop, 0.0, 2.0, 0.1, 3); + RNA_def_property_float_default(prop, 1.0); RNA_def_property_float_sdna(prop, NULL, "hsv[2]"); RNA_def_property_ui_text(prop, "Value", "Color Value"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "create_materials", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_COLOR_CREATE_COLORS); - RNA_def_property_ui_text(prop, "Create Materials", "When apply modifier, create new material"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -1164,6 +1256,17 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_COLOR_INVERT_LAYERPASS); RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_COLOR_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define color effect along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) @@ -1181,11 +1284,6 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Set what colors of the stroke are affected"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "opacity_mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, modifier_opacity_mode_items); - RNA_def_property_ui_text(prop, "Opacity Mode", "Set what mode used to define opacity"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); @@ -1204,13 +1302,10 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "factor"); - RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 3); - RNA_def_property_ui_text(prop, "Factor", "Factor of Opacity"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "create_materials", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_CREATE_COLORS); - RNA_def_property_ui_text(prop, "Create Materials", "When apply modifier, create new material"); + RNA_def_property_ui_range(prop, 0, 2.0, 0.1, 2); + RNA_def_property_float_funcs( + prop, NULL, "rna_GpencilOpacity_max_set", "rna_GpencilOpacity_range"); + RNA_def_property_ui_text(prop, "Opacity Factor", "Factor of Opacity"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); @@ -1249,9 +1344,25 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_INVERT_LAYERPASS); RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "normalize_opacity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_NORMALIZE); + RNA_def_property_ui_text(prop, "Uniform Opacity", "Replace the stroke opacity"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_opacity_update"); + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define opacity effect along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } -static void rna_def_modifier_gpencilinstance(BlenderRNA *brna) +static void rna_def_modifier_gpencilarray(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; @@ -1295,50 +1406,41 @@ static void rna_def_modifier_gpencilinstance(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); - prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_TRANSLATION); + prop = RNA_def_property(srna, "constant_offset", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_float_sdna(prop, NULL, "offset"); - RNA_def_property_ui_text(prop, "Offset", "Value for the distance between items"); + RNA_def_property_ui_text(prop, "Constant Offset", "Value for the distance between items"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "shift", PROP_FLOAT, PROP_TRANSLATION); + prop = RNA_def_property(srna, "relative_offset", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "shift"); - RNA_def_property_ui_text(prop, "Shift", "Shiftiness value"); - RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_EULER); - RNA_def_property_float_sdna(prop, NULL, "rot"); - RNA_def_property_ui_text(prop, "Rotation", "Value for changes in rotation"); + RNA_def_property_ui_text( + prop, + "Relative Offset", + "The size of the geometry will determine the distance between arrayed items"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ); - RNA_def_property_float_sdna(prop, NULL, "scale"); - RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale"); + prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_offset"); + RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location"); RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "random_rot", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_RANDOM_ROT); - RNA_def_property_ui_text(prop, "Random Rotation", "Use random factors for rotation"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - prop = RNA_def_property(srna, "rot_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER); RNA_def_property_float_sdna(prop, NULL, "rnd_rot"); - RNA_def_property_ui_text(prop, "Rotation Factor", "Random factor for rotation"); - RNA_def_property_range(prop, -10.0, 10.0); + RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "random_scale", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_RANDOM_SIZE); - RNA_def_property_ui_text(prop, "Random Scale", "Use random factors for scale"); + prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "rnd_scale"); + RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "scale_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "rnd_size"); - RNA_def_property_ui_text(prop, "Scale Factor", "Random factor for scale"); - RNA_def_property_range(prop, -10.0, 10.0); + prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Seed", "Random seed"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "replace_material", PROP_INT, PROP_NONE); @@ -1376,12 +1478,19 @@ static void rna_def_modifier_gpencilinstance(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "keep_on_top", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_KEEP_ONTOP); - RNA_def_property_ui_text( - prop, - "Keep on Top", - "Keep the original stroke in front of new instances (only affect by layer)"); + prop = RNA_def_property(srna, "use_constant_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_OFFSET); + RNA_def_property_ui_text(prop, "Offset", "Enable offset"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_object_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_OB_OFFSET); + RNA_def_property_ui_text(prop, "Object Offset", "Enable obejct offset"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_relative_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ARRAY_USE_RELATIVE); + RNA_def_property_ui_text(prop, "Shift", "Enable shift"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } @@ -1848,16 +1957,6 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna) prop, "Preserve Volume", "Deform rotation interpolation with quaternions"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); -# if 0 /* GPXX keep disabled now */ - prop = RNA_def_property(srna, "use_multi_modifier", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "multi", 0); - RNA_def_property_ui_text( - prop, - "Multi Modifier", - "Use same input as previous modifier, and mix results using overall vgroup"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); -# endif - prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "vgname"); RNA_def_property_ui_text( @@ -1930,28 +2029,33 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Angle Splitting", "Enable angle splitting"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "enable_fading", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_fade", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", GP_MULTIPLY_ENABLE_FADING); - RNA_def_property_ui_text(prop, "Enable Fading", "Enable fading"); + RNA_def_property_ui_text( + prop, "Enable Fade", "Fade the stroke thickness for each generated stroke"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_range(prop, 0, M_PI); + RNA_def_property_ui_range(prop, 0, M_PI, 10, 2); RNA_def_property_ui_text(prop, "Angle", "Split angle for segments"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "duplications", PROP_INT, PROP_NONE); - RNA_def_property_range(prop, 0, 10); - RNA_def_property_ui_text(prop, "Duplications", "How many copies of strokes be displayed"); + prop = RNA_def_property(srna, "duplicates", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "duplications"); + RNA_def_property_range(prop, 0, 999); + RNA_def_property_ui_range(prop, 1, 10, 1, 1); + RNA_def_property_ui_text(prop, "Duplicates", "How many copies of strokes be displayed"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0, M_PI); + prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 3); RNA_def_property_ui_text(prop, "Distance", "Distance of duplications"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE); - RNA_def_property_ui_range(prop, -1, 1, 0.1, 3); + RNA_def_property_ui_range(prop, -1, 1, 0.01, 3); RNA_def_property_ui_text(prop, "Offset", "Offset of duplicates. -1 to 1: inner to outer"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -1967,12 +2071,132 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Opacity", "Fade influence of stroke's opacity"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "fading_center", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "fading_center", PROP_FLOAT, PROP_FACTOR); RNA_def_property_range(prop, 0, 1); + RNA_def_property_float_default(prop, 0.5); RNA_def_property_ui_text(prop, "Center", "Fade center"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); } +static void rna_def_modifier_gpencilvertexcolor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* modes */ + static EnumPropertyItem vertexcol_mode_types_items[] = { + {GPPAINT_MODE_STROKE, "STROKE", 0, "Stroke", "Vertex Color affects to Stroke only"}, + {GPPAINT_MODE_FILL, "FILL", 0, "Fill", "Vertex Color affects to Fill only"}, + {GPPAINT_MODE_BOTH, "BOTH", 0, "Both", "Vertex Color affects to Stroke and Fill"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "VertexcolorGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Vertexcolor Modifier", "Vertex color modifier"); + RNA_def_struct_sdna(srna, "VertexcolorGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_NORMALEDIT); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Parent object to define the center of the effect"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_pointer_funcs( + prop, NULL, "rna_VertexcolorGpencilModifier_object_set", NULL, NULL); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "materialname"); + RNA_def_property_ui_text(prop, "Material", "Material name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_HookGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "pass_index"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse Vertex Group", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "factor"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Factor", "Factor of tinting"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 1, 3); + RNA_def_property_ui_text(prop, "Radius", "Defines the maximum distance of the effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Mode type. */ + prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, vertexcol_mode_types_items); + RNA_def_property_ui_text(prop, "Mode", "Defines how vertex color affect to the strokes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Color band */ + prop = RNA_def_property(srna, "colors", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "colorband"); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Colors", "Color ramp used to define tinting colors"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_custom_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_VERTEXCOL_CUSTOM_CURVE); + RNA_def_property_ui_text( + prop, "Custom Curve", "Use a custom curve to define vertex color effect along the strokes"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity"); + RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + void RNA_def_greasepencil_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -2038,7 +2262,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpenciltint(brna); rna_def_modifier_gpenciltime(brna); rna_def_modifier_gpencilcolor(brna); - rna_def_modifier_gpencilinstance(brna); + rna_def_modifier_gpencilarray(brna); rna_def_modifier_gpencilbuild(brna); rna_def_modifier_gpencilopacity(brna); rna_def_modifier_gpencillattice(brna); @@ -2046,6 +2270,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencilhook(brna); rna_def_modifier_gpencilarmature(brna); rna_def_modifier_gpencilmultiply(brna); + rna_def_modifier_gpencilvertexcolor(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index a29031900ac..f206e0061fc 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -63,6 +63,7 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = { # include "MEM_guardedalloc.h" +# include "DNA_gpencil_types.h" # include "DNA_node_types.h" # include "DNA_object_types.h" # include "DNA_screen_types.h" @@ -111,16 +112,16 @@ static void rna_Material_update_previews(Main *UNUSED(bmain), static void rna_MaterialGpencil_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Material *ma = (Material *)ptr->owner_id; - rna_Material_update(bmain, scene, ptr); - WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma); -} -static void rna_MaterialGpencil_nopreview_update(Main *bmain, Scene *scene, PointerRNA *ptr) -{ - Material *ma = (Material *)ptr->owner_id; + /* Need set all caches as dirty. */ + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = (bGPdata *)ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + } + } - rna_Material_update(bmain, scene, ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma); } @@ -411,42 +412,45 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) /* mode type styles */ static EnumPropertyItem gpcolordata_mode_types_items[] = { - {GP_STYLE_MODE_LINE, "LINE", 0, "Line", "Draw strokes using a continuous line"}, - {GP_STYLE_MODE_DOTS, "DOTS", 0, "Dots", "Draw strokes using separated dots"}, - {GP_STYLE_MODE_BOX, "BOX", 0, "Boxes", "Draw strokes using separated rectangle boxes"}, + {GP_MATERIAL_MODE_LINE, "LINE", 0, "Line", "Draw strokes using a continuous line"}, + {GP_MATERIAL_MODE_DOT, "DOTS", 0, "Dots", "Draw strokes using separated dots"}, + {GP_MATERIAL_MODE_SQUARE, "BOX", 0, "Squares", "Draw strokes using separated squares"}, {0, NULL, 0, NULL, NULL}, }; /* stroke styles */ static EnumPropertyItem stroke_style_items[] = { - {GP_STYLE_STROKE_STYLE_SOLID, "SOLID", 0, "Solid", "Draw strokes with solid color"}, - {GP_STYLE_STROKE_STYLE_TEXTURE, "TEXTURE", 0, "Texture", "Draw strokes using texture"}, + {GP_MATERIAL_STROKE_STYLE_SOLID, "SOLID", 0, "Solid", "Draw strokes with solid color"}, + {GP_MATERIAL_STROKE_STYLE_TEXTURE, "TEXTURE", 0, "Texture", "Draw strokes using texture"}, {0, NULL, 0, NULL, NULL}, }; /* fill styles */ static EnumPropertyItem fill_style_items[] = { - {GP_STYLE_FILL_STYLE_SOLID, "SOLID", 0, "Solid", "Fill area with solid color"}, - {GP_STYLE_FILL_STYLE_GRADIENT, "GRADIENT", 0, "Gradient", "Fill area with gradient color"}, - {GP_STYLE_FILL_STYLE_CHECKER, - "CHECKER", + {GP_MATERIAL_FILL_STYLE_SOLID, "SOLID", 0, "Solid", "Fill area with solid color"}, + {GP_MATERIAL_FILL_STYLE_GRADIENT, + "GRADIENT", 0, - "Checker Board", - "Fill area with checkerboard pattern"}, - {GP_STYLE_FILL_STYLE_TEXTURE, "TEXTURE", 0, "Texture", "Fill area with image texture"}, + "Gradient", + "Fill area with gradient color"}, + {GP_MATERIAL_FILL_STYLE_TEXTURE, "TEXTURE", 0, "Texture", "Fill area with image texture"}, {0, NULL, 0, NULL, NULL}, }; static EnumPropertyItem fill_gradient_items[] = { - {GP_STYLE_GRADIENT_LINEAR, "LINEAR", 0, "Linear", "Fill area with gradient color"}, - {GP_STYLE_GRADIENT_RADIAL, "RADIAL", 0, "Radial", "Fill area with radial gradient"}, + {GP_MATERIAL_GRADIENT_LINEAR, "LINEAR", 0, "Linear", "Fill area with gradient color"}, + {GP_MATERIAL_GRADIENT_RADIAL, "RADIAL", 0, "Radial", "Fill area with radial gradient"}, {0, NULL, 0, NULL, NULL}, }; static EnumPropertyItem alignment_draw_items[] = { - {GP_STYLE_FOLLOW_PATH, "PATH", 0, "Path", "Follow stroke drawing path and object rotation"}, - {GP_STYLE_FOLLOW_OBJ, "OBJECT", 0, "Object", "Follow object rotation only"}, - {GP_STYLE_FOLLOW_FIXED, + {GP_MATERIAL_FOLLOW_PATH, + "PATH", + 0, + "Path", + "Follow stroke drawing path and object rotation"}, + {GP_MATERIAL_FOLLOW_OBJ, "OBJECT", 0, "Object", "Follow object rotation only"}, + {GP_MATERIAL_FOLLOW_FIXED, "FIXED", 0, "Fixed", @@ -459,7 +463,7 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Grease Pencil Color", ""); RNA_def_struct_path_func(srna, "rna_GpencilColorData_path"); - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "stroke_rgba"); RNA_def_property_array(prop, 4); @@ -467,7 +471,7 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* Fill Drawing Color */ - prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "fill_rgba"); RNA_def_property_array(prop, 4); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -475,7 +479,7 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* Secondary Drawing Color */ - prop = RNA_def_property(srna, "mix_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "mix_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "mix_rgba"); RNA_def_property_array(prop, 4); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -483,51 +487,17 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* Mix factor */ - prop = RNA_def_property(srna, "mix_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "mix_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "mix_factor"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Mix", "Mix Adjustment Factor"); + RNA_def_property_ui_text(prop, "Mix", "Mix Factor"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* Stroke Mix factor */ - prop = RNA_def_property(srna, "mix_stroke_factor", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "mix_stroke_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "mix_stroke_factor"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Mix", "Mix Stroke Color"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - /* Scale factor for uv coordinates */ - prop = RNA_def_property(srna, "pattern_scale", PROP_FLOAT, PROP_COORDS); - RNA_def_property_float_sdna(prop, NULL, "gradient_scale"); - RNA_def_property_array(prop, 2); - RNA_def_property_ui_text(prop, "Scale", "Scale Factor for UV coordinates"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - /* Shift factor to move pattern filling in 2d space */ - prop = RNA_def_property(srna, "pattern_shift", PROP_FLOAT, PROP_COORDS); - RNA_def_property_float_sdna(prop, NULL, "gradient_shift"); - RNA_def_property_array(prop, 2); - RNA_def_property_ui_text(prop, "Shift", "Shift filling pattern in 2d space"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - /* Gradient angle */ - prop = RNA_def_property(srna, "pattern_angle", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, NULL, "gradient_angle"); - RNA_def_property_ui_text(prop, "Angle", "Pattern Orientation Angle"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - /* Gradient radius */ - prop = RNA_def_property(srna, "pattern_radius", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "gradient_radius"); - RNA_def_property_range(prop, 0.0001f, 10.0f); - RNA_def_property_ui_text(prop, "Radius", "Pattern Radius"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - /* Box size */ - prop = RNA_def_property(srna, "pattern_gridsize", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "pattern_gridsize"); - RNA_def_property_range(prop, 0.0001f, 10.0f); - RNA_def_property_ui_text(prop, "Size", "Box Size"); + RNA_def_property_ui_text(prop, "Mix", "Mix Stroke Factor"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* Texture angle */ @@ -566,68 +536,48 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_HIDE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_HIDE); RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1); RNA_def_property_ui_text(prop, "Hide", "Set color Visibility"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_nopreview_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_LOCKED); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_LOCKED); RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); RNA_def_property_ui_text( prop, "Locked", "Protect color from further editing and/or frame changes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_nopreview_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_ONIONSKIN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_ONIONSKIN); RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0); RNA_def_property_ui_text( prop, "Show in Ghosts", "Display strokes using this color when showing onion skins"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_nopreview_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); prop = RNA_def_property(srna, "texture_clamp", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_TEX_CLAMP); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_TEX_CLAMP); RNA_def_property_ui_text(prop, "Clamp", "Do not repeat texture and clamp to one instance only"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - prop = RNA_def_property(srna, "use_fill_texture_mix", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_FILL_TEX_MIX); - RNA_def_property_ui_text(prop, "Mix Texture", "Mix texture image with filling color"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - prop = RNA_def_property(srna, "use_stroke_texture_mix", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_STROKE_TEX_MIX); - RNA_def_property_ui_text(prop, "Mix Texture", "Mix texture image with stroke color"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - prop = RNA_def_property(srna, "flip", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_FLIP_FILL); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_FLIP_FILL); RNA_def_property_ui_text(prop, "Flip", "Flip filling colors"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - prop = RNA_def_property(srna, "use_stroke_pattern", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_STROKE_PATTERN); - RNA_def_property_ui_text(prop, "Pattern", "Use Stroke Texture as a pattern to apply color"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - - prop = RNA_def_property(srna, "use_fill_pattern", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_FILL_PATTERN); - RNA_def_property_ui_text(prop, "Pattern", "Use Fill Texture as a pattern to apply color"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); - prop = RNA_def_property(srna, "use_overlap_strokes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_DISABLE_STENCIL); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_DISABLE_STENCIL); RNA_def_property_ui_text( prop, "Self Overlap", "Disable stencil and overlap self intersections with alpha materials"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); prop = RNA_def_property(srna, "show_stroke", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_STROKE_SHOW); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_STROKE_SHOW); RNA_def_property_ui_text(prop, "Show Stroke", "Show stroke lines of this material"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); prop = RNA_def_property(srna, "show_fill", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_FILL_SHOW); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MATERIAL_FILL_SHOW); RNA_def_property_ui_text(prop, "Show Fill", "Show stroke fills of this material"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); @@ -637,13 +587,13 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_enum_items(prop, alignment_draw_items); RNA_def_property_ui_text( prop, "Alignment", "Defines how align Dots and Boxes with drawing path and object rotation"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_nopreview_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* pass index for future compositing and editing tools */ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "index"); RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Color Index\" pass"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_nopreview_update"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* mode type */ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 9980051c112..78f5cfb60b2 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -85,6 +85,11 @@ const EnumPropertyItem rna_enum_object_mode_items[] = { ICON_GREASEPENCIL, "Draw", "Paint Grease Pencil Strokes"}, + {OB_MODE_VERTEX_GPENCIL, + "VERTEX_GPENCIL", + ICON_VPAINT_HLT, + "Vertex Paint", + "Grease Pencil Vertex Paint Strokes"}, {OB_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", ICON_WPAINT_HLT, @@ -118,6 +123,11 @@ const EnumPropertyItem rna_enum_workspace_object_mode_items[] = { ICON_GREASEPENCIL, "Grease Pencil Draw", "Paint Grease Pencil Strokes"}, + {OB_MODE_VERTEX_GPENCIL, + "VERTEX_GPENCIL", + ICON_VPAINT_HLT, + "Grease Pencil Vertex Paint", + "Grease Pencil Vertex Paint Strokes"}, {OB_MODE_WEIGHT_GPENCIL, "WEIGHT_GPENCIL", ICON_WPAINT_HLT, @@ -343,6 +353,16 @@ static void rna_MaterialIndex_update(Main *UNUSED(bmain), Scene *UNUSED(scene), } } +static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->owner_id; + if (ob && ob->type == OB_GPENCIL) { + bGPdata *gpd = (bGPdata *)ob->data; + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); + } +} + static void rna_Object_matrix_local_get(PointerRNA *ptr, float values[16]) { Object *ob = (Object *)ptr->owner_id; @@ -3067,6 +3087,13 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Display All Edges", "Display all edges for mesh objects"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + prop = RNA_def_property(srna, "use_grease_pencil_lights", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_USE_GPENCIL_LIGHTS); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Use Lights", "Lights affect to grease pencil object"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_GPencil_update"); + prop = RNA_def_property(srna, "show_transparent", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAWTRANSP); RNA_def_property_ui_text( @@ -3076,7 +3103,7 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "show_in_front", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAWXRAY); RNA_def_property_ui_text(prop, "In Front", "Make the object draw in front of others"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_GPencil_update"); /* pose */ prop = RNA_def_property(srna, "pose_library", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index a427267926d..0ead133b3a8 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -733,14 +733,8 @@ static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value) } } -static void rna_Gpencil_selectmode_update(bContext *C, PointerRNA *ptr) +static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr)) { - ToolSettings *ts = (ToolSettings *)ptr->data; - /* If the mode is not Stroke, don't extend selection. */ - if ((ts->gpencil_selectmode_edit & GP_SELECTMODE_STROKE) == 0) { - return; - } - /* Extend selection to all points in all selected strokes. */ ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); @@ -762,9 +756,18 @@ static void rna_Gpencil_selectmode_update(bContext *C, PointerRNA *ptr) } } -static void rna_Gpencil_mask_point_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) +static void rna_Gpencil_selectmode_update(bContext *C, PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + /* If the mode is not Stroke, don't extend selection. */ + if ((ts->gpencil_selectmode_edit & GP_SELECTMODE_STROKE) == 0) { + return; + } + + rna_Gpencil_extend_selection(C, ptr); +} + +static void rna_Gpencil_mask_point_update(bContext *UNUSED(C), PointerRNA *ptr) { ToolSettings *ts = (ToolSettings *)ptr->data; @@ -772,19 +775,17 @@ static void rna_Gpencil_mask_point_update(Main *UNUSED(bmain), ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_SEGMENT; } -static void rna_Gpencil_mask_stroke_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) +static void rna_Gpencil_mask_stroke_update(bContext *C, PointerRNA *ptr) { ToolSettings *ts = (ToolSettings *)ptr->data; ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_POINT; ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_SEGMENT; + + rna_Gpencil_extend_selection(C, ptr); } -static void rna_Gpencil_mask_segment_update(Main *UNUSED(bmain), - Scene *UNUSED(scene), - PointerRNA *ptr) +static void rna_Gpencil_mask_segment_update(bContext *UNUSED(C), PointerRNA *ptr) { ToolSettings *ts = (ToolSettings *)ptr->data; @@ -792,6 +793,38 @@ static void rna_Gpencil_mask_segment_update(Main *UNUSED(bmain), ts->gpencil_selectmode_sculpt &= ~GP_SCULPT_MASK_SELECTMODE_STROKE; } +static void rna_Gpencil_vertex_mask_point_update(bContext *C, PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_STROKE; + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_SEGMENT; + + ED_gpencil_tag_scene_gpencil(CTX_data_scene(C)); +} + +static void rna_Gpencil_vertex_mask_stroke_update(bContext *C, PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_POINT; + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_SEGMENT; + + rna_Gpencil_extend_selection(C, ptr); + + ED_gpencil_tag_scene_gpencil(CTX_data_scene(C)); +} + +static void rna_Gpencil_vertex_mask_segment_update(bContext *C, PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *)ptr->data; + + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_POINT; + ts->gpencil_selectmode_vertex &= ~GP_VERTEX_MASK_SELECTMODE_STROKE; + + ED_gpencil_tag_scene_gpencil(CTX_data_scene(C)); +} + /* Read-only Iterator of all the scene objects. */ static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) @@ -2873,6 +2906,18 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "gp_paint"); RNA_def_property_ui_text(prop, "Grease Pencil Paint", ""); + prop = RNA_def_property(srna, "gpencil_vertex_paint", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_vertexpaint"); + RNA_def_property_ui_text(prop, "Grease Pencil Vertex Paint", ""); + + prop = RNA_def_property(srna, "gpencil_sculpt_paint", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_sculptpaint"); + RNA_def_property_ui_text(prop, "Grease Pencil Sculpt Paint", ""); + + prop = RNA_def_property(srna, "gpencil_weight_paint", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_weightpaint"); + RNA_def_property_ui_text(prop, "Grease Pencil Weight Paint", ""); + prop = RNA_def_property(srna, "particle_edit", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "particle"); RNA_def_property_ui_text(prop, "Particle Edit", ""); @@ -3190,6 +3235,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points"); RNA_def_property_ui_icon(prop, ICON_GP_SELECT_POINTS, 0); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_point_update"); prop = RNA_def_property(srna, "use_gpencil_select_mask_stroke", PROP_BOOLEAN, PROP_NONE); @@ -3198,6 +3244,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke"); RNA_def_property_ui_icon(prop, ICON_GP_SELECT_STROKES, 0); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_stroke_update"); prop = RNA_def_property(srna, "use_gpencil_select_mask_segment", PROP_BOOLEAN, PROP_NONE); @@ -3207,8 +3254,41 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop, "Selection Mask", "Only sculpt selected stroke points between other strokes"); RNA_def_property_ui_icon(prop, ICON_GP_SELECT_BETWEEN_STROKES, 0); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_mask_segment_update"); + /* Grease Pencil - Select mode Vertex Paint */ + prop = RNA_def_property(srna, "use_gpencil_vertex_select_mask_point", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_vertex", GP_VERTEX_MASK_SELECTMODE_POINT); + RNA_def_property_ui_text(prop, "Selection Mask", "Only paint selected stroke points"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_POINTS, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_vertex_mask_point_update"); + + prop = RNA_def_property(srna, "use_gpencil_vertex_select_mask_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_vertex", GP_VERTEX_MASK_SELECTMODE_STROKE); + RNA_def_property_ui_text(prop, "Selection Mask", "Only paint selected stroke"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_STROKES, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_vertex_mask_stroke_update"); + + prop = RNA_def_property(srna, "use_gpencil_vertex_select_mask_segment", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "gpencil_selectmode_vertex", GP_VERTEX_MASK_SELECTMODE_SEGMENT); + RNA_def_property_ui_text( + prop, "Selection Mask", "Only paint selected stroke points between other strokes"); + RNA_def_property_ui_icon(prop, ICON_GP_SELECT_BETWEEN_STROKES, 0); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Gpencil_vertex_mask_segment_update"); + /* Annotations - 2D Views Stroke Placement */ prop = RNA_def_property(srna, "annotation_stroke_placement_view2d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v2d_align"); @@ -6288,7 +6368,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop = RNA_def_property(srna, "simplify_gpencil_onplay", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_ON_PLAY); RNA_def_property_ui_text( - prop, "Simplify Playback", "Simplify Grease Pencil only during animation playback"); + prop, "Playback Only", "Simplify Grease Pencil only during animation playback"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "simplify_gpencil_antialiasing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_AA); + RNA_def_property_ui_text(prop, "Antialiasing", "Use Antialiasing to smooth stroke edges"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "simplify_gpencil_view_fill", PROP_BOOLEAN, PROP_NONE); @@ -6296,26 +6381,15 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Fill", "Display fill strokes in the viewport"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "simplify_gpencil_remove_lines", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna( - prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_REMOVE_FILL_LINE); - RNA_def_property_ui_text(prop, "Disable Lines", "Display external lines of fill strokes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "simplify_gpencil_view_modifier", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "simplify_gpencil_modifier", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna( prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_MODIFIER); - RNA_def_property_ui_text(prop, "Disable Modifiers", "Display modifiers in the viewport"); + RNA_def_property_ui_text(prop, "Modifiers", "Display modifiers"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "simplify_gpencil_shader_fx", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FX); - RNA_def_property_ui_text(prop, "Simplify Shaders", "Display Shader FX"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "simplify_gpencil_blend", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_BLEND); - RNA_def_property_ui_text(prop, "Layers Blending", "Display blend layers"); + RNA_def_property_ui_text(prop, "ShadersFX", "Display Shader FX"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "simplify_gpencil_tint", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 14cfe9d29ab..743c47f4f48 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -57,59 +57,6 @@ const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[] = { {0, NULL, 0, NULL, NULL}, }; -const EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { - {GP_SCULPT_TYPE_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", "Smooth stroke points"}, - {GP_SCULPT_TYPE_THICKNESS, - "THICKNESS", - ICON_GPBRUSH_THICKNESS, - "Thickness", - "Adjust thickness of strokes"}, - {GP_SCULPT_TYPE_STRENGTH, - "STRENGTH", - ICON_GPBRUSH_STRENGTH, - "Strength", - "Adjust color strength of strokes"}, - {GP_SCULPT_TYPE_RANDOMIZE, - "RANDOMIZE", - ICON_GPBRUSH_RANDOMIZE, - "Randomize", - "Introduce jitter/randomness into strokes"}, - {GP_SCULPT_TYPE_GRAB, - "GRAB", - ICON_GPBRUSH_GRAB, - "Grab", - "Translate the set of points initially within the brush circle"}, - {GP_SCULPT_TYPE_PUSH, - "PUSH", - ICON_GPBRUSH_PUSH, - "Push", - "Move points out of the way, as if combing them"}, - {GP_SCULPT_TYPE_TWIST, - "TWIST", - ICON_GPBRUSH_TWIST, - "Twist", - "Rotate points around the midpoint of the brush"}, - {GP_SCULPT_TYPE_PINCH, - "PINCH", - ICON_GPBRUSH_PINCH, - "Pinch", - "Pull points towards the midpoint of the brush"}, - {GP_SCULPT_TYPE_CLONE, - "CLONE", - ICON_GPBRUSH_CLONE, - "Clone", - "Paste copies of the strokes stored on the clipboard"}, - {0, NULL, 0, NULL, NULL}}; - -const EnumPropertyItem rna_enum_gpencil_weight_brush_items[] = { - {GP_SCULPT_TYPE_WEIGHT, - "WEIGHT", - ICON_GPBRUSH_WEIGHT, - "Weight", - "Weight Paint for Vertex Groups"}, - {0, NULL, 0, NULL, NULL}, -}; - #ifndef RNA_RUNTIME static const EnumPropertyItem rna_enum_gpencil_lock_axis_items[] = { {GP_LOCKAXIS_VIEW, @@ -135,6 +82,20 @@ static const EnumPropertyItem rna_enum_gpencil_lock_axis_items[] = { "Align strokes to current 3D cursor orientation"}, {0, NULL, 0, NULL, NULL}, }; + +static const EnumPropertyItem rna_enum_gpencil_paint_mode[] = { + {GPPAINT_FLAG_USE_MATERIAL, + "MATERIAL", + 0, + "Material", + "Paint using the active material base color"}, + {GPPAINT_FLAG_USE_VERTEXCOLOR, + "VERTEXCOLOR", + 0, + "Vertex Color", + "Paint the material with custom vertex color"}, + {0, NULL, 0, NULL, NULL}, +}; #endif const EnumPropertyItem rna_enum_symmetrize_direction_items[] = { @@ -384,6 +345,24 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) } mode = OB_MODE_PAINT_GPENCIL; } + else if (paint_contains_brush_slot(&ts->gp_vertexpaint->paint, tslot, &slot_index)) { + if (slot_index != brush->gpencil_vertex_tool) { + return false; + } + mode = OB_MODE_VERTEX_GPENCIL; + } + else if (paint_contains_brush_slot(&ts->gp_sculptpaint->paint, tslot, &slot_index)) { + if (slot_index != brush->gpencil_sculpt_tool) { + return false; + } + mode = OB_MODE_SCULPT_GPENCIL; + } + else if (paint_contains_brush_slot(&ts->gp_weightpaint->paint, tslot, &slot_index)) { + if (slot_index != brush->gpencil_weight_tool) { + return false; + } + mode = OB_MODE_WEIGHT_GPENCIL; + } return brush->ob_mode & mode; } @@ -454,6 +433,21 @@ static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.gpencil_paint"); } +static char *rna_GpVertexPaint_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gpencil_vertex_paint"); +} + +static char *rna_GpSculptPaint_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gpencil_sculpt_paint"); +} + +static char *rna_GpWeightPaint_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gpencil_weight_paint"); +} + static char *rna_ParticleBrush_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit.brush"); @@ -543,34 +537,11 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) return imapaint->missing_data == 0; } -static PointerRNA rna_GPencilSculptSettings_brush_get(PointerRNA *ptr) -{ - GP_Sculpt_Settings *gset = (GP_Sculpt_Settings *)ptr->data; - GP_Sculpt_Data *brush = NULL; - - if ((gset) && (gset->flag & GP_SCULPT_SETT_FLAG_WEIGHT_MODE)) { - if ((gset->weighttype >= GP_SCULPT_TYPE_WEIGHT) && (gset->weighttype < GP_SCULPT_TYPE_MAX)) { - brush = &gset->brush[gset->weighttype]; - } - } - else { - if ((gset->brushtype >= 0) && (gset->brushtype < GP_SCULPT_TYPE_WEIGHT)) { - brush = &gset->brush[gset->brushtype]; - } - } - return rna_pointer_inherit_refine(ptr, &RNA_GPencilSculptBrush, brush); -} - static char *rna_GPencilSculptSettings_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt"); } -static char *rna_GPencilSculptBrush_path(PointerRNA *UNUSED(ptr)) -{ - return BLI_strdup("tool_settings.gpencil_sculpt.brush"); -} - static char *rna_GPencilSculptGuide_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt.guide"); @@ -888,10 +859,46 @@ static void rna_def_uv_sculpt(BlenderRNA *brna) static void rna_def_gp_paint(BlenderRNA *brna) { StructRNA *srna; + PropertyRNA *prop; srna = RNA_def_struct(brna, "GpPaint", "Paint"); RNA_def_struct_path_func(srna, "rna_GpPaint_path"); RNA_def_struct_ui_text(srna, "Grease Pencil Paint", ""); + + /* Use vertex color (main swith). */ + prop = RNA_def_property(srna, "color_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_paint_mode); + RNA_def_property_ui_text(prop, "Mode", "Paint Mode"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); +} + +static void rna_def_gp_vertexpaint(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "GpVertexPaint", "Paint"); + RNA_def_struct_path_func(srna, "rna_GpVertexPaint_path"); + RNA_def_struct_ui_text(srna, "Grease Pencil Vertex Paint", ""); +} + +static void rna_def_gp_sculptpaint(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "GpSculptPaint", "Paint"); + RNA_def_struct_path_func(srna, "rna_GpSculptPaint_path"); + RNA_def_struct_ui_text(srna, "Grease Pencil Sculpt Paint", ""); +} + +static void rna_def_gp_weightpaint(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "GpWeightPaint", "Paint"); + RNA_def_struct_path_func(srna, "rna_GpWeightPaint_path"); + RNA_def_struct_ui_text(srna, "Grease Pencil Weight Paint", ""); } /* use for weight paint too */ @@ -1383,6 +1390,8 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + UNUSED_VARS(prop_direction_items); + StructRNA *srna; PropertyRNA *prop; @@ -1390,60 +1399,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) srna = RNA_def_struct(brna, "GPencilSculptSettings", NULL); RNA_def_struct_sdna(srna, "GP_Sculpt_Settings"); RNA_def_struct_path_func(srna, "rna_GPencilSculptSettings_path"); - RNA_def_struct_ui_text( - srna, "GPencil Sculpt Settings", "Properties for Grease Pencil stroke sculpting tool"); - - prop = RNA_def_property(srna, "sculpt_tool", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "brushtype"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_sculpt_brush_items); - RNA_def_property_ui_text(prop, "Tool", ""); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "weight_tool", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "weighttype"); - RNA_def_property_enum_items(prop, rna_enum_gpencil_weight_brush_items); - RNA_def_property_ui_text(prop, "Tool", "Tool for weight painting"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "GPencilSculptBrush"); - RNA_def_property_pointer_funcs(prop, "rna_GPencilSculptSettings_brush_get", NULL, NULL, NULL); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Brush", ""); + RNA_def_struct_ui_text(srna, + "GPencil Sculpt Settings", + "General properties for Grease Pencil stroke sculpting tools"); prop = RNA_def_property(srna, "guide", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "GPencilSculptGuide"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Guide", ""); - prop = RNA_def_property(srna, "use_edit_position", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_APPLY_POSITION); - RNA_def_property_ui_text(prop, "Affect Position", "The brush affects the position of the point"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_edit_strength", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_APPLY_STRENGTH); - RNA_def_property_ui_text( - prop, "Affect Strength", "The brush affects the color strength of the point"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_edit_thickness", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_APPLY_THICKNESS); - RNA_def_property_ui_text( - prop, "Affect Thickness", "The brush affects the thickness of the point"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_edit_uv", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_APPLY_UV); - RNA_def_property_ui_text(prop, "Affect UV", "The brush affects the UV rotation of the point"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "use_multiframe_falloff", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_FRAME_FALLOFF); RNA_def_property_ui_text( @@ -1459,6 +1423,13 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_scale_thickness", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_SCALE_THICKNESS); + RNA_def_property_ui_text( + prop, "Scale Stroke Thickness", "Scale the stroke thickness when transforming strokes"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + /* custom falloff curve */ prop = RNA_def_property(srna, "multiframe_falloff_curve", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "cur_falloff"); @@ -1491,97 +1462,6 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_float_default(prop, 0.1f); RNA_def_property_ui_text(prop, "Threshold", "Threshold for stroke intersections"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - - /* brush */ - srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL); - RNA_def_struct_sdna(srna, "GP_Sculpt_Data"); - RNA_def_struct_path_func(srna, "rna_GPencilSculptBrush_path"); - RNA_def_struct_ui_text(srna, "GPencil Sculpt Brush", "Stroke editing brush"); - - prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); - RNA_def_property_range(prop, 1, GP_MAX_BRUSH_PIXEL_RADIUS); - RNA_def_property_ui_range(prop, 1, 500, 10, 3); - RNA_def_property_ui_text(prop, "Radius", "Radius of the brush in pixels"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_range(prop, 0.001, 1.0); - RNA_def_property_ui_text(prop, "Strength", "Brush strength"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_range(prop, 0.0, 1.0); - RNA_def_property_ui_text(prop, - "Weight", - "Target weight (define a maximum range limit for the weight. Any value " - "above will be clamped)"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_pressure_strength", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_FLAG_USE_PRESSURE); - RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); - RNA_def_property_ui_text( - prop, "Strength Pressure", "Enable tablet pressure sensitivity for strength"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_pressure_radius", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_FLAG_PRESSURE_RADIUS); - RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); - RNA_def_property_ui_text( - prop, "Radius Pressure", "Enable tablet pressure sensitivity for radius"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_falloff", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_FLAG_USE_FALLOFF); - RNA_def_property_ui_text( - prop, "Use Falloff", "Strength of brush decays with distance from cursor"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "use_edit_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_FLAG_SMOOTH_PRESSURE); - RNA_def_property_ui_text( - prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); - RNA_def_property_enum_items(prop, prop_direction_items); - RNA_def_property_ui_text(prop, "Direction", ""); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - - /* Cursor Color */ - static float default_1[3] = {1.0f, 0.6f, 0.6f}; - static float default_2[3] = {0.6f, 0.6f, 1.0f}; - - prop = RNA_def_property(srna, "cursor_color_add", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "curcolor_add"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_array_default(prop, default_1); - RNA_def_property_ui_text(prop, "Cursor Add", "Color for the cursor for addition"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - - prop = RNA_def_property(srna, "cursor_color_sub", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "curcolor_sub"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_array_default(prop, default_2); - RNA_def_property_ui_text(prop, "Cursor Sub", "Color for the cursor for subtraction"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - - prop = RNA_def_property(srna, "use_cursor", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_FLAG_ENABLE_CURSOR); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_ui_text(prop, "Enable Cursor", "Enable cursor on screen"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } void RNA_def_sculpt_paint(BlenderRNA *brna) @@ -1594,6 +1474,9 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) rna_def_sculpt(brna); rna_def_uv_sculpt(brna); rna_def_gp_paint(brna); + rna_def_gp_vertexpaint(brna); + rna_def_gp_sculptpaint(brna); + rna_def_gp_weightpaint(brna); rna_def_vertex_paint(brna); rna_def_image_paint(brna); rna_def_particle_edit(brna); diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c index cd4e027ce7c..f7f68d535ec 100644 --- a/source/blender/makesrna/intern/rna_shader_fx.c +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -22,9 +22,10 @@ #include #include -#include "DNA_shader_fx_types.h" +#include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_shader_fx_types.h" #include "MEM_guardedalloc.h" @@ -52,7 +53,6 @@ const EnumPropertyItem rna_enum_object_shaderfx_type_items[] = { "Apply different tint effects"}, {eShaderFxType_Flip, "FX_FLIP", ICON_SHADERFX, "Flip", "Flip image"}, {eShaderFxType_Glow, "FX_GLOW", ICON_SHADERFX, "Glow", "Create a glow effect"}, - {eShaderFxType_Light, "FX_LIGHT", ICON_SHADERFX, "Light", "Simulate illumination"}, {eShaderFxType_Pixel, "FX_PIXEL", ICON_SHADERFX, "Pixelate", "Pixelate image"}, {eShaderFxType_Rim, "FX_RIM", ICON_SHADERFX, "Rim", "Add a rim to the image"}, {eShaderFxType_Shadow, "FX_SHADOW", ICON_SHADERFX, "Shadow", "Create a shadow effect"}, @@ -87,6 +87,14 @@ static const EnumPropertyItem rna_enum_shaderfx_colorize_modes_items[] = { {eShaderFxColorizeMode_Custom, "CUSTOM", 0, "Custom", ""}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem rna_enum_glow_blend_modes_items[] = { + {eGplBlendMode_Regular, "REGULAR", 0, "Regular", ""}, + {eGplBlendMode_Add, "ADD", 0, "Add", ""}, + {eGplBlendMode_Subtract, "SUBTRACT", 0, "Subtract", ""}, + {eGplBlendMode_Multiply, "MULTIPLY", 0, "Multiply", ""}, + {eGplBlendMode_Divide, "DIVIDE", 0, "Divide", ""}, + {0, NULL, 0, NULL, NULL}}; + #ifdef RNA_RUNTIME # include "BKE_shader_fx.h" @@ -117,11 +125,10 @@ static StructRNA *rna_ShaderFx_refine(struct PointerRNA *ptr) return &RNA_ShaderFxFlip; case eShaderFxType_Glow: return &RNA_ShaderFxGlow; - case eShaderFxType_Light: - return &RNA_ShaderFxLight; /* Default */ case eShaderFxType_None: case NUM_SHADER_FX_TYPES: + default: return &RNA_ShaderFx; } @@ -192,7 +199,6 @@ static void shaderfx_object_set(Object *self, Object **ob_p, int type, PointerRN shaderfx_object_set((Object *)ptr->owner_id, &tmd->_prop, _obtype, value); \ } -RNA_FX_OBJECT_SET(Light, object, OB_EMPTY); RNA_FX_OBJECT_SET(Shadow, object, OB_EMPTY); RNA_FX_OBJECT_SET(Swirl, object, OB_EMPTY); @@ -210,10 +216,10 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna) RNA_def_struct_sdna(srna, "BlurShaderFxData"); RNA_def_struct_ui_icon(srna, ICON_SHADERFX); - prop = RNA_def_property(srna, "factor", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "radius"); - RNA_def_property_range(prop, 0, SHRT_MAX); - RNA_def_property_ui_text(prop, "Factor", "Factor of Blur"); + prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Size", "Factor of Blur"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); @@ -224,19 +230,15 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "coc", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "coc"); - RNA_def_property_range(prop, 0.001f, 1.0f); - RNA_def_property_float_default(prop, 0.025f); - RNA_def_property_ui_text(prop, "Precision", "Define circle of confusion for depth of field"); + prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "rotation"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_dof_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_BLUR_DOF_MODE); - RNA_def_property_ui_text(prop, - "Lock Focal Plane", - "Blur using focal plane distance as factor to simulate depth of field " - "effect (only in camera view)"); + RNA_def_property_ui_text(prop, "Use as Depth Of Field", "Blur using camera depth of field"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); } @@ -256,14 +258,14 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Factor", "Mix factor"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "low_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "low_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "low_color"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Low Color", "First color used for effect"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "high_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "high_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "high_color"); RNA_def_property_array(prop, 4); @@ -334,17 +336,12 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Size", "Pixel size"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rgba"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Color", "Color used for lines"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - - prop = RNA_def_property(srna, "use_lines", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_PIXEL_USE_LINES); - RNA_def_property_ui_text(prop, "Lines", "Display lines between pixels"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); } static void rna_def_shader_fx_rim(BlenderRNA *brna) @@ -363,14 +360,14 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Offset", "Offset of the rim"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "rim_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "rim_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rim_rgb"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Rim Color", "Color used for Rim"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "mask_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "mask_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "mask_rgb"); RNA_def_property_array(prop, 3); @@ -433,7 +430,7 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Scale", "Offset of the shadow"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "shadow_rgba"); RNA_def_property_array(prop, 4); @@ -507,14 +504,21 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_struct_sdna(srna, "GlowShaderFxData"); RNA_def_struct_ui_icon(srna, ICON_SHADERFX); - prop = RNA_def_property(srna, "glow_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "glow_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "glow_color"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Glow Color", "Color used for generated glow"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "select_color", PROP_FLOAT, PROP_COLOR_GAMMA); + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "glow_color[3]"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Opacity", "Effect Opacity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "select_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "select_color"); RNA_def_property_array(prop, 3); @@ -534,13 +538,11 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Threshold", "Limit to select color for glow effect"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - /* Use blur fields to make compatible with blur filter, - * but only makes public first array element. */ - prop = RNA_def_property(srna, "radius", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "blur[0]"); - RNA_def_property_range(prop, 0, SHRT_MAX); - RNA_def_property_ui_text( - prop, "Radius", "Number of pixels for blurring glow (set to 0 to disable)"); + /* Use blur fields to make compatible with blur filter */ + prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "blur"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Size", "Size of th effect"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); @@ -551,9 +553,23 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - prop = RNA_def_property(srna, "use_alpha_mode", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_glow_under", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_GLOW_USE_ALPHA); - RNA_def_property_ui_text(prop, "Use Alpha", "Glow only areas with alpha"); + RNA_def_property_ui_text( + prop, "Glow Under", "Glow only areas with alpha (not supported with Regular blend mode)"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "rotation"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + /* blend mode */ + prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "blend_mode"); + RNA_def_property_enum_items(prop, rna_enum_glow_blend_modes_items); + RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); } @@ -614,38 +630,6 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); } -static void rna_def_shader_fx_light(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "ShaderFxLight", "ShaderFx"); - RNA_def_struct_ui_text(srna, "Light Effect", "Light effect"); - RNA_def_struct_sdna(srna, "LightShaderFxData"); - RNA_def_struct_ui_icon(srna, ICON_SHADERFX); - - prop = RNA_def_property(srna, "energy", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "energy"); - RNA_def_property_range(prop, 0, FLT_MAX); - RNA_def_property_ui_range(prop, 1, FLT_MAX, 1, 2); - RNA_def_property_ui_text(prop, "Energy", "Strength of light source"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - - prop = RNA_def_property(srna, "ambient", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "ambient"); - RNA_def_property_range(prop, 0, FLT_MAX); - RNA_def_property_ui_range(prop, 0, FLT_MAX, 1, 2); - RNA_def_property_ui_text(prop, "Ambient", "Strength of ambient light source"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); - - prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_ui_text(prop, "Object", "Object to determine light source location"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_LightShaderFx_object_set", NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update"); -} - void RNA_def_shader_fx(BlenderRNA *brna) { StructRNA *srna; @@ -712,7 +696,6 @@ void RNA_def_shader_fx(BlenderRNA *brna) rna_def_shader_fx_glow(brna); rna_def_shader_fx_swirl(brna); rna_def_shader_fx_flip(brna); - rna_def_shader_fx_light(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8123d02714a..02dd625c7e3 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1013,6 +1013,9 @@ static void rna_3DViewShading_type_update(Main *bmain, Scene *scene, PointerRNA } } + /* Update Gpencil. */ + rna_GPencil_update(bmain, scene, ptr); + bScreen *screen = (bScreen *)ptr->owner_id; for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -3289,7 +3292,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_View3DShading_color_type_itemf"); RNA_def_property_ui_text(prop, "Color", "Color Type"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); prop = RNA_def_property(srna, "wireframe_color_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "wire_color_type"); @@ -3782,8 +3785,8 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "use_gpencil_paper", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_SHOW_PAPER); + prop = RNA_def_property(srna, "use_gpencil_fade_objects", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_FADE_OBJECTS); RNA_def_property_ui_text( prop, "Fade Objects", @@ -3801,12 +3804,26 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop, "Fade Layers", "Toggle fading of Grease Pencil layers except the active one"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); - prop = RNA_def_property(srna, "use_gpencil_fade_objects", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_gpencil_fade_gp_objects", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_FADE_NOACTIVE_GPENCIL); RNA_def_property_ui_text( prop, "Fade Grease Pencil Objects", "Fade Grease Pencil Objects, except the active one"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + prop = RNA_def_property(srna, "use_gpencil_show_directions", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_SHOW_STROKE_DIRECTION); + RNA_def_property_ui_text(prop, + "Stroke Direction", + "Show stroke drawing direction with a bigger green dot (start) " + "and smaller red dot (end) points"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_gpencil_show_material_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_SHOW_MATERIAL_NAME); + RNA_def_property_ui_text( + prop, "Stroke Material Name", "Show material name assigned to each stroke"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + prop = RNA_def_property(srna, "gpencil_grid_opacity", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_grid_opacity"); RNA_def_property_range(prop, 0.1f, 1.0f); @@ -3814,7 +3831,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* Paper opacity factor */ - prop = RNA_def_property(srna, "gpencil_paper_opacity", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "gpencil_fade_objects", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_paper_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Fade factor"); @@ -3832,12 +3849,12 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) /* show edit lines */ prop = RNA_def_property(srna, "use_gpencil_edit_lines", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_SHOW_EDIT_LINES); - RNA_def_property_ui_text(prop, "Show Edit Lines", "Show edit lines when editing strokes"); + RNA_def_property_ui_text(prop, "Show Edit Lines", "Show Edit Lines when editing strokes"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); prop = RNA_def_property(srna, "use_gpencil_multiedit_line_only", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gp_flag", V3D_GP_SHOW_MULTIEDIT_LINES); - RNA_def_property_ui_text(prop, "Lines Only", "Only show edit lines for additional frames"); + RNA_def_property_ui_text(prop, "Lines Only", "Show Edit Lines only in multiframe"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); /* main grease pencil onion switch */ @@ -3848,12 +3865,20 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); /* vertex opacity */ - prop = RNA_def_property(srna, "vertex_opacity", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "vertex_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "vertex_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Vertex Opacity", "Opacity for edit vertices"); RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); + + /* Vertex Paint opacity factor */ + prop = RNA_def_property(srna, "gpencil_vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_vertex_paint_opacity"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); } static void rna_def_space_view3d(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 527df695f5b..8b112031ada 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -152,8 +152,10 @@ static void rna_uiItemR_with_popover(uiLayout *layout, RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); return; } - if (RNA_property_type(prop) != PROP_ENUM) { - RNA_warning("property is not an enum: %s.%s", RNA_struct_identifier(ptr->type), propname); + if ((RNA_property_type(prop) != PROP_ENUM) && + !ELEM(RNA_property_subtype(prop), PROP_COLOR, PROP_COLOR_GAMMA)) { + RNA_warning( + "property is not an enum or color: %s.%s", RNA_struct_identifier(ptr->type), propname); return; } int flag = 0; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 566305ad1c7..7250f8a2d59 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5082,15 +5082,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem multi_sample_levels[] = { - {USER_MULTISAMPLE_NONE, "NONE", 0, "No MultiSample", "Do not use OpenGL MultiSample"}, - {USER_MULTISAMPLE_2, "2", 0, "MultiSample: 2", "Use 2x OpenGL MultiSample"}, - {USER_MULTISAMPLE_4, "4", 0, "MultiSample: 4", "Use 4x OpenGL MultiSample"}, - {USER_MULTISAMPLE_8, "8", 0, "MultiSample: 8", "Use 8x OpenGL MultiSample"}, - {USER_MULTISAMPLE_16, "16", 0, "MultiSample: 16", "Use 16x OpenGL MultiSample"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem image_draw_methods[] = { {IMAGE_DRAW_METHOD_AUTO, "AUTO", @@ -5175,16 +5166,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) "Enable Edit-Mode edge smoothing, reducing aliasing, requires restart"); RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); - /* grease pencil anti-aliasing */ - prop = RNA_def_property(srna, "gpencil_multi_sample", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_multisamples"); - RNA_def_property_enum_items(prop, multi_sample_levels); - RNA_def_property_ui_text( - prop, - "Gpencil MultiSample", - "Enable Grease Pencil OpenGL multi-sampling, only for systems that support it"); - RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); - prop = RNA_def_property(srna, "use_region_overlap", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP); RNA_def_property_ui_text( diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt index 6d918763996..9835c5c0588 100644 --- a/source/blender/shader_fx/CMakeLists.txt +++ b/source/blender/shader_fx/CMakeLists.txt @@ -45,7 +45,6 @@ set(SRC intern/FX_shader_colorize.c intern/FX_shader_flip.c intern/FX_shader_glow.c - intern/FX_shader_light.c intern/FX_shader_pixel.c intern/FX_shader_rim.c intern/FX_shader_shadow.c diff --git a/source/blender/shader_fx/FX_shader_types.h b/source/blender/shader_fx/FX_shader_types.h index 7a87c77f313..f338f9bcc2a 100644 --- a/source/blender/shader_fx/FX_shader_types.h +++ b/source/blender/shader_fx/FX_shader_types.h @@ -30,7 +30,6 @@ extern ShaderFxTypeInfo shaderfx_Type_Blur; extern ShaderFxTypeInfo shaderfx_Type_Colorize; extern ShaderFxTypeInfo shaderfx_Type_Flip; extern ShaderFxTypeInfo shaderfx_Type_Glow; -extern ShaderFxTypeInfo shaderfx_Type_Light; extern ShaderFxTypeInfo shaderfx_Type_Pixel; extern ShaderFxTypeInfo shaderfx_Type_Rim; extern ShaderFxTypeInfo shaderfx_Type_Shadow; diff --git a/source/blender/shader_fx/intern/FX_shader_blur.c b/source/blender/shader_fx/intern/FX_shader_blur.c index 72d2a61dafd..e98205897aa 100644 --- a/source/blender/shader_fx/intern/FX_shader_blur.c +++ b/source/blender/shader_fx/intern/FX_shader_blur.c @@ -23,6 +23,7 @@ #include +#include "BLI_math.h" #include "BLI_utildefines.h" #include "FX_shader_types.h" @@ -30,9 +31,9 @@ static void initData(ShaderFxData *fx) { BlurShaderFxData *gpfx = (BlurShaderFxData *)fx; - ARRAY_SET_ITEMS(gpfx->radius, 1, 1); - gpfx->samples = 4; - gpfx->coc = 0.025f; + copy_v2_fl(gpfx->radius, 50.0f); + gpfx->samples = 8; + gpfx->rotation = 0.0f; } static void copyData(const ShaderFxData *md, ShaderFxData *target) @@ -45,7 +46,7 @@ ShaderFxTypeInfo shaderfx_Type_Blur = { /* structName */ "BlurShaderFxData", /* structSize */ sizeof(BlurShaderFxData), /* type */ eShaderFxType_GpencilType, - /* flags */ eShaderFxTypeFlag_Single, + /* flags */ 0, /* copyData */ copyData, diff --git a/source/blender/shader_fx/intern/FX_shader_flip.c b/source/blender/shader_fx/intern/FX_shader_flip.c index 804b194ed68..41ca903ee08 100644 --- a/source/blender/shader_fx/intern/FX_shader_flip.c +++ b/source/blender/shader_fx/intern/FX_shader_flip.c @@ -47,7 +47,7 @@ ShaderFxTypeInfo shaderfx_Type_Flip = { /* structName */ "FlipShaderFxData", /* structSize */ sizeof(FlipShaderFxData), /* type */ eShaderFxType_GpencilType, - /* flags */ eShaderFxTypeFlag_Single, + /* flags */ 0, /* copyData */ copyData, diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c index 1b1e4dc9d94..8924a1ab8b9 100644 --- a/source/blender/shader_fx/intern/FX_shader_glow.c +++ b/source/blender/shader_fx/intern/FX_shader_glow.c @@ -27,6 +27,7 @@ #include "DNA_object_types.h" #include "DNA_gpencil_types.h" +#include "BLI_math.h" #include "BLI_utildefines.h" #include "BKE_modifier.h" @@ -37,12 +38,11 @@ static void initData(ShaderFxData *md) { GlowShaderFxData *gpfx = (GlowShaderFxData *)md; - ARRAY_SET_ITEMS(gpfx->glow_color, 0.75f, 1.0f, 1.0f); + ARRAY_SET_ITEMS(gpfx->glow_color, 0.75f, 1.0f, 1.0f, 1.0f); ARRAY_SET_ITEMS(gpfx->select_color, 0.0f, 0.0f, 0.0f); + copy_v2_fl(gpfx->blur, 50.0f); gpfx->threshold = 0.1f; - - ARRAY_SET_ITEMS(gpfx->blur, 50, 0); - gpfx->samples = 16; + gpfx->samples = 8; } static void copyData(const ShaderFxData *md, ShaderFxData *target) diff --git a/source/blender/shader_fx/intern/FX_shader_pixel.c b/source/blender/shader_fx/intern/FX_shader_pixel.c index e0ea111d121..f39649bba07 100644 --- a/source/blender/shader_fx/intern/FX_shader_pixel.c +++ b/source/blender/shader_fx/intern/FX_shader_pixel.c @@ -44,7 +44,7 @@ ShaderFxTypeInfo shaderfx_Type_Pixel = { /* structName */ "PixelShaderFxData", /* structSize */ sizeof(PixelShaderFxData), /* type */ eShaderFxType_GpencilType, - /* flags */ eShaderFxTypeFlag_Single, + /* flags */ 0, /* copyData */ copyData, diff --git a/source/blender/shader_fx/intern/FX_shader_rim.c b/source/blender/shader_fx/intern/FX_shader_rim.c index d16210918fc..a81d2ff2a09 100644 --- a/source/blender/shader_fx/intern/FX_shader_rim.c +++ b/source/blender/shader_fx/intern/FX_shader_rim.c @@ -35,7 +35,7 @@ static void initData(ShaderFxData *fx) ARRAY_SET_ITEMS(gpfx->offset, 50, -100); ARRAY_SET_ITEMS(gpfx->rim_rgb, 1.0f, 1.0f, 0.5f); ARRAY_SET_ITEMS(gpfx->mask_rgb, 0.0f, 0.0f, 0.0f); - gpfx->mode = eShaderFxRimMode_Multiply; + gpfx->mode = eShaderFxRimMode_Overlay; ARRAY_SET_ITEMS(gpfx->blur, 0, 0); gpfx->samples = 2; diff --git a/source/blender/shader_fx/intern/FX_shader_shadow.c b/source/blender/shader_fx/intern/FX_shader_shadow.c index 0ee45a0bd51..aa9da5ae9f1 100644 --- a/source/blender/shader_fx/intern/FX_shader_shadow.c +++ b/source/blender/shader_fx/intern/FX_shader_shadow.c @@ -44,7 +44,7 @@ static void initData(ShaderFxData *md) gpfx->rotation = 0.0f; ARRAY_SET_ITEMS(gpfx->offset, 15, 20); ARRAY_SET_ITEMS(gpfx->scale, 1.0f, 1.0f); - ARRAY_SET_ITEMS(gpfx->shadow_rgba, 0.54f, 0.62f, 1.0f, 0.9f); + ARRAY_SET_ITEMS(gpfx->shadow_rgba, 0.0f, 0.0f, 0.0f, 0.8f); gpfx->amplitude = 10.0f; gpfx->period = 20.0f; diff --git a/source/blender/shader_fx/intern/FX_shader_util.c b/source/blender/shader_fx/intern/FX_shader_util.c index 908a2b249b8..c2dcae04b85 100644 --- a/source/blender/shader_fx/intern/FX_shader_util.c +++ b/source/blender/shader_fx/intern/FX_shader_util.c @@ -39,7 +39,6 @@ void shaderfx_type_init(ShaderFxTypeInfo *types[]) INIT_FX_TYPE(Colorize); INIT_FX_TYPE(Flip); INIT_FX_TYPE(Glow); - INIT_FX_TYPE(Light); INIT_FX_TYPE(Pixel); INIT_FX_TYPE(Rim); INIT_FX_TYPE(Shadow); diff --git a/source/blender/shader_fx/intern/FX_shader_wave.c b/source/blender/shader_fx/intern/FX_shader_wave.c index 334024bbd3f..35d2e515f76 100644 --- a/source/blender/shader_fx/intern/FX_shader_wave.c +++ b/source/blender/shader_fx/intern/FX_shader_wave.c @@ -50,7 +50,7 @@ ShaderFxTypeInfo shaderfx_Type_Wave = { /* structName */ "WaveShaderFxData", /* structSize */ sizeof(WaveShaderFxData), /* type */ eShaderFxType_GpencilType, - /* flags */ eShaderFxTypeFlag_Single, + /* flags */ 0, /* copyData */ copyData, diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index 6e494280820..307ad444ffd 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -170,6 +170,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) case CTX_MODE_WEIGHT_GPENCIL: km_id = "Grease Pencil Stroke Weight Mode"; break; + case CTX_MODE_VERTEX_GPENCIL: + km_id = "Grease Pencil Stroke Vertex Mode"; + break; } } else if (sl->spacetype == SPACE_IMAGE) { diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index e335800636c..e721fd21b88 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -172,37 +172,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre if (tref_rt->data_block[0]) { Main *bmain = CTX_data_main(C); - if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_SCULPT_GPENCIL)) { - const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items; - const int i = RNA_enum_from_identifier(items, tref_rt->data_block); - if (i != -1) { - const int value = items[i].value; - wmWindowManager *wm = bmain->wm.first; - for (wmWindow *win = wm->windows.first; win; win = win->next) { - if (workspace == WM_window_get_active_workspace(win)) { - Scene *scene = WM_window_get_active_scene(win); - ToolSettings *ts = scene->toolsettings; - ts->gp_sculpt.brushtype = value; - } - } - } - } - else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_WEIGHT_GPENCIL)) { - const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items; - const int i = RNA_enum_from_identifier(items, tref_rt->data_block); - if (i != -1) { - const int value = items[i].value; - wmWindowManager *wm = bmain->wm.first; - for (wmWindow *win = wm->windows.first; win; win = win->next) { - if (workspace == WM_window_get_active_workspace(win)) { - Scene *scene = WM_window_get_active_scene(win); - ToolSettings *ts = scene->toolsettings; - ts->gp_sculpt.weighttype = value; - } - } - } - } - else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) { + if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) { const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items; const int i = RNA_enum_from_identifier(items, tref_rt->data_block); if (i != -1) { @@ -414,29 +384,7 @@ void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToo if (ob == NULL) { /* pass */ } - else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_SCULPT_GPENCIL)) { - if (ob->mode & OB_MODE_SCULPT_GPENCIL) { - const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items; - const int i = RNA_enum_from_value(items, ts->gp_sculpt.brushtype); - const EnumPropertyItem *item = &items[i]; - if (!STREQ(tref_rt->data_block, item->identifier)) { - STRNCPY(tref_rt->data_block, item->identifier); - SNPRINTF(tref->idname, "builtin_brush.%s", item->name); - } - } - } - else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_WEIGHT_GPENCIL)) { - if (ob->mode & OB_MODE_WEIGHT_GPENCIL) { - const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items; - const int i = RNA_enum_from_value(items, ts->gp_sculpt.weighttype); - const EnumPropertyItem *item = &items[i]; - if (!STREQ(tref_rt->data_block, item->identifier)) { - STRNCPY(tref_rt->data_block, item->identifier); - SNPRINTF(tref->idname, "builtin_brush.%s", item->name); - } - } - } - else if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) { + if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE)) { if (ob->mode & OB_MODE_PARTICLE_EDIT) { const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items; const int i = RNA_enum_from_value(items, ts->particle.brushtype); @@ -735,6 +683,8 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) return "builtin_brush.Push"; case CTX_MODE_WEIGHT_GPENCIL: return "builtin_brush.Weight"; + case CTX_MODE_VERTEX_GPENCIL: + return "builtin_brush.Draw"; /* end temporary hack. */ case CTX_MODE_PARTICLE: -- cgit v1.2.3