From 66da2f537ae80ce2b31d1eaf34ad8c03d858938d Mon Sep 17 00:00:00 2001 From: Antonioya Date: Tue, 31 Jul 2018 10:22:19 +0200 Subject: [PATCH] New Grease Pencil object for 2D animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton. --- build_files/cmake/macros.cmake | 5 +- intern/cycles/blender/addon/ui.py | 5 +- .../datafiles/brushicons/gp_brush_block.png | Bin 0 -> 6088 bytes .../datafiles/brushicons/gp_brush_clone.png | Bin 0 -> 2586 bytes .../brushicons/gp_brush_erase_hard.png | Bin 0 -> 6107 bytes .../brushicons/gp_brush_erase_soft.png | Bin 0 -> 6252 bytes .../brushicons/gp_brush_erase_stroke.png | Bin 0 -> 5955 bytes .../datafiles/brushicons/gp_brush_fill.png | Bin 0 -> 5707 bytes .../datafiles/brushicons/gp_brush_grab.png | Bin 0 -> 2741 bytes release/datafiles/brushicons/gp_brush_ink.png | Bin 0 -> 4034 bytes .../brushicons/gp_brush_inknoise.png | Bin 0 -> 5591 bytes .../datafiles/brushicons/gp_brush_marker.png | Bin 0 -> 5996 bytes release/datafiles/brushicons/gp_brush_pen.png | Bin 0 -> 4920 bytes .../datafiles/brushicons/gp_brush_pencil.png | Bin 0 -> 3965 bytes .../datafiles/brushicons/gp_brush_pinch.png | Bin 0 -> 5432 bytes .../datafiles/brushicons/gp_brush_push.png | Bin 0 -> 2914 bytes .../brushicons/gp_brush_randomize.png | Bin 0 -> 5726 bytes .../datafiles/brushicons/gp_brush_smooth.png | Bin 0 -> 3105 bytes .../brushicons/gp_brush_strength.png | Bin 0 -> 3229 bytes .../brushicons/gp_brush_thickness.png | Bin 0 -> 5066 bytes .../datafiles/brushicons/gp_brush_twist.png | Bin 0 -> 4776 bytes .../datafiles/brushicons/gp_brush_weight.png | Bin 0 -> 2460 bytes .../icons/brush.gpencil.draw.eraser_hard.dat | Bin 0 -> 1736 bytes .../icons/brush.gpencil.draw.eraser_soft.dat | Bin 0 -> 1880 bytes .../brush.gpencil.draw.eraser_stroke.dat | Bin 0 -> 2456 bytes .../icons/brush.gpencil.draw_block.dat | Bin 0 -> 2006 bytes .../icons/brush.gpencil.draw_fill.dat | Bin 0 -> 6326 bytes .../icons/brush.gpencil.draw_ink.dat | Bin 0 -> 2240 bytes .../icons/brush.gpencil.draw_marker.dat | Bin 0 -> 2690 bytes .../icons/brush.gpencil.draw_noise.dat | Bin 0 -> 3086 bytes .../icons/brush.gpencil.draw_pen.dat | Bin 0 -> 3158 bytes .../icons/brush.gpencil.draw_pencil.dat | Bin 0 -> 2780 bytes release/datafiles/icons/ops.gpencil.draw.dat | Bin 0 -> 2492 bytes .../icons/ops.gpencil.draw.eraser.dat | Bin 0 -> 2384 bytes .../datafiles/icons/ops.gpencil.draw.line.dat | Bin 0 -> 1664 bytes .../datafiles/icons/ops.gpencil.draw.poly.dat | Bin 0 -> 1736 bytes .../datafiles/icons/ops.gpencil.edit_bend.dat | Bin 0 -> 2330 bytes .../icons/ops.gpencil.edit_mirror.dat | Bin 0 -> 620 bytes .../icons/ops.gpencil.edit_shear.dat | Bin 0 -> 332 bytes .../icons/ops.gpencil.edit_to_sphere.dat | Bin 0 -> 1790 bytes .../icons/ops.gpencil.sculpt_clone.dat | Bin 0 -> 4328 bytes .../icons/ops.gpencil.sculpt_grab.dat | Bin 0 -> 1664 bytes .../icons/ops.gpencil.sculpt_pinch.dat | Bin 0 -> 3482 bytes .../icons/ops.gpencil.sculpt_push.dat | Bin 0 -> 1664 bytes .../icons/ops.gpencil.sculpt_randomize.dat | Bin 0 -> 6254 bytes .../icons/ops.gpencil.sculpt_smooth.dat | Bin 0 -> 3788 bytes .../icons/ops.gpencil.sculpt_strength.dat | Bin 0 -> 5228 bytes .../icons/ops.gpencil.sculpt_thickness.dat | Bin 0 -> 1700 bytes .../icons/ops.gpencil.sculpt_twist.dat | Bin 0 -> 2942 bytes .../icons/ops.gpencil.sculpt_weight.dat | Bin 0 -> 1736 bytes release/datafiles/preview_grease_pencil.blend | Bin 0 -> 947876 bytes .../datafiles/userdef/userdef_default_theme.c | 10 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../modules/bpy_extras/keyconfig_utils.py | 6 + .../scripts/startup/bl_operators/presets.py | 37 + release/scripts/startup/bl_ui/__init__.py | 3 + .../startup/bl_ui/properties_data_gpencil.py | 402 +++ .../startup/bl_ui/properties_data_modifier.py | 445 ++- .../startup/bl_ui/properties_data_shaderfx.py | 134 + .../bl_ui/properties_grease_pencil_common.py | 799 ++---- .../startup/bl_ui/properties_material.py | 7 +- .../bl_ui/properties_material_gpencil.py | 322 +++ .../scripts/startup/bl_ui/properties_scene.py | 35 +- release/scripts/startup/bl_ui/space_clip.py | 45 +- release/scripts/startup/bl_ui/space_image.py | 55 +- release/scripts/startup/bl_ui/space_node.py | 56 +- .../scripts/startup/bl_ui/space_sequencer.py | 10 - .../startup/bl_ui/space_toolsystem_toolbar.py | 564 +++- release/scripts/startup/bl_ui/space_topbar.py | 11 + .../scripts/startup/bl_ui/space_userpref.py | 11 +- release/scripts/startup/bl_ui/space_view3d.py | 326 ++- .../startup/bl_ui/space_view3d_toolbar.py | 415 ++- source/blender/CMakeLists.txt | 4 + source/blender/blenkernel/BKE_brush.h | 8 +- source/blender/blenkernel/BKE_context.h | 12 +- source/blender/blenkernel/BKE_gpencil.h | 99 +- .../blender/blenkernel/BKE_gpencil_modifier.h | 256 ++ source/blender/blenkernel/BKE_icons.h | 7 + source/blender/blenkernel/BKE_lattice.h | 1 - source/blender/blenkernel/BKE_material.h | 4 + source/blender/blenkernel/BKE_object.h | 12 + source/blender/blenkernel/BKE_paint.h | 3 +- source/blender/blenkernel/BKE_shader_fx.h | 180 ++ source/blender/blenkernel/CMakeLists.txt | 6 + source/blender/blenkernel/intern/anim_sys.c | 7 + source/blender/blenkernel/intern/brush.c | 380 ++- source/blender/blenkernel/intern/colortools.c | 19 + source/blender/blenkernel/intern/context.c | 20 +- source/blender/blenkernel/intern/deform.c | 4 +- source/blender/blenkernel/intern/gpencil.c | 1553 ++++++----- .../blenkernel/intern/gpencil_modifier.c | 679 +++++ source/blender/blenkernel/intern/icons.c | 44 +- source/blender/blenkernel/intern/library.c | 1 + .../blender/blenkernel/intern/library_query.c | 27 +- source/blender/blenkernel/intern/material.c | 76 +- source/blender/blenkernel/intern/object.c | 194 +- .../blender/blenkernel/intern/object_deform.c | 16 +- .../blender/blenkernel/intern/object_update.c | 4 + source/blender/blenkernel/intern/paint.c | 13 +- source/blender/blenkernel/intern/scene.c | 81 +- source/blender/blenkernel/intern/shader_fx.c | 245 ++ source/blender/blenlib/BLI_math_vector.h | 3 + source/blender/blenlib/BLI_rand.h | 3 + source/blender/blenlib/intern/listbase.c | 3 + .../blenlib/intern/math_vector_inline.c | 13 + source/blender/blenlib/intern/rand.c | 12 + source/blender/blenloader/intern/readfile.c | 246 +- .../blenloader/intern/versioning_260.c | 4 +- .../blenloader/intern/versioning_270.c | 85 +- .../blenloader/intern/versioning_280.c | 199 +- .../blenloader/intern/versioning_defaults.c | 50 +- source/blender/blenloader/intern/writefile.c | 105 +- source/blender/collada/SceneExporter.cpp | 2 + .../intern/builder/deg_builder_nodes.cc | 34 +- .../builder/deg_builder_nodes_view_layer.cc | 4 - .../intern/builder/deg_builder_relations.cc | 124 +- .../deg_builder_relations_view_layer.cc | 4 - .../blender/depsgraph/intern/depsgraph_tag.cc | 9 +- source/blender/draw/CMakeLists.txt | 34 + source/blender/draw/DRW_engine.h | 3 + .../engines/gpencil/gpencil_cache_utils.c | 296 ++ .../engines/gpencil/gpencil_draw_cache_impl.c | 739 +++++ .../draw/engines/gpencil/gpencil_draw_utils.c | 1336 +++++++++ .../draw/engines/gpencil/gpencil_engine.c | 794 ++++++ .../draw/engines/gpencil/gpencil_engine.h | 355 +++ .../draw/engines/gpencil/gpencil_render.c | 353 +++ .../draw/engines/gpencil/gpencil_shader_fx.c | 848 ++++++ .../shaders/fx/gpencil_fx_blur_frag.glsl | 60 + .../shaders/fx/gpencil_fx_colorize_frag.glsl | 86 + .../shaders/fx/gpencil_fx_flip_frag.glsl | 37 + .../shaders/fx/gpencil_fx_light_frag.glsl | 70 + .../shaders/fx/gpencil_fx_pixel_frag.glsl | 50 + .../fx/gpencil_fx_rim_prepare_frag.glsl | 64 + .../fx/gpencil_fx_rim_resolve_frag.glsl | 101 + .../shaders/fx/gpencil_fx_swirl_frag.glsl | 70 + .../shaders/fx/gpencil_fx_wave_frag.glsl | 40 + .../shaders/gpencil_background_frag.glsl | 12 + .../shaders/gpencil_edit_point_frag.glsl | 17 + .../shaders/gpencil_edit_point_geom.glsl | 48 + .../shaders/gpencil_edit_point_vert.glsl | 15 + .../gpencil/shaders/gpencil_fill_frag.glsl | 140 + .../gpencil/shaders/gpencil_fill_vert.glsl | 14 + .../gpencil/shaders/gpencil_paper_frag.glsl | 9 + .../gpencil/shaders/gpencil_point_frag.glsl | 49 + .../gpencil/shaders/gpencil_point_geom.glsl | 82 + .../gpencil/shaders/gpencil_point_vert.glsl | 37 + .../shaders/gpencil_simple_mix_frag.glsl | 15 + .../gpencil/shaders/gpencil_stroke_frag.glsl | 46 + .../gpencil/shaders/gpencil_stroke_geom.glsl | 208 ++ .../gpencil/shaders/gpencil_stroke_vert.glsl | 37 + .../shaders/gpencil_zdepth_mix_frag.glsl | 45 + source/blender/draw/intern/DRW_render.h | 1 + source/blender/draw/intern/draw_cache.c | 60 +- source/blender/draw/intern/draw_cache.h | 3 + source/blender/draw/intern/draw_cache_impl.h | 4 + source/blender/draw/intern/draw_manager.c | 145 +- source/blender/draw/modes/draw_mode_engines.h | 1 + source/blender/draw/modes/object_mode.c | 18 + .../editors/animation/anim_channels_defines.c | 15 +- .../editors/animation/anim_channels_edit.c | 3 +- source/blender/editors/animation/anim_deps.c | 10 + source/blender/editors/animation/anim_draw.c | 4 +- .../blender/editors/animation/anim_filter.c | 13 +- .../editors/animation/keyframes_draw.c | 9 +- .../blender/editors/datafiles/CMakeLists.txt | 52 + source/blender/editors/gpencil/CMakeLists.txt | 6 + .../blender/editors/gpencil/annotate_draw.c | 1065 ++++++++ .../blender/editors/gpencil/annotate_paint.c | 2382 +++++++++++++++++ source/blender/editors/gpencil/drawgpencil.c | 1019 +++---- .../editors/gpencil/editaction_gpencil.c | 2 + .../editors/gpencil/gpencil_add_monkey.c | 1567 +++++++++++ .../blender/editors/gpencil/gpencil_brush.c | 697 +++-- .../blender/editors/gpencil/gpencil_convert.c | 70 +- source/blender/editors/gpencil/gpencil_data.c | 2027 ++++++++------ source/blender/editors/gpencil/gpencil_edit.c | 1639 ++++++++++-- source/blender/editors/gpencil/gpencil_fill.c | 1246 +++++++++ .../blender/editors/gpencil/gpencil_intern.h | 271 +- .../editors/gpencil/gpencil_interpolate.c | 71 +- source/blender/editors/gpencil/gpencil_old.c | 219 ++ source/blender/editors/gpencil/gpencil_ops.c | 561 +++- .../blender/editors/gpencil/gpencil_paint.c | 1341 ++++++---- .../editors/gpencil/gpencil_primitive.c | 712 +++++ .../blender/editors/gpencil/gpencil_select.c | 293 +- source/blender/editors/gpencil/gpencil_undo.c | 6 + .../blender/editors/gpencil/gpencil_utils.c | 1296 ++++++--- source/blender/editors/include/ED_anim_api.h | 5 + source/blender/editors/include/ED_datafiles.h | 63 + source/blender/editors/include/ED_gpencil.h | 192 +- .../editors/include/ED_keyframes_draw.h | 5 +- source/blender/editors/include/ED_object.h | 35 + source/blender/editors/include/UI_icons.h | 24 + source/blender/editors/include/UI_interface.h | 9 +- .../editors/interface/interface_icons.c | 100 +- .../editors/interface/interface_layout.c | 8 + .../editors/interface/interface_templates.c | 308 ++- source/blender/editors/interface/resources.c | 20 +- source/blender/editors/object/CMakeLists.txt | 4 + source/blender/editors/object/object_add.c | 131 +- source/blender/editors/object/object_edit.c | 42 +- .../editors/object/object_gpencil_modifier.c | 637 +++++ source/blender/editors/object/object_intern.h | 15 + source/blender/editors/object/object_modes.c | 18 +- .../blender/editors/object/object_modifier.c | 238 +- source/blender/editors/object/object_ops.c | 15 + .../blender/editors/object/object_relations.c | 23 +- source/blender/editors/object/object_select.c | 5 +- .../blender/editors/object/object_shader_fx.c | 469 ++++ .../blender/editors/object/object_transform.c | 103 +- source/blender/editors/render/render_opengl.c | 94 +- .../blender/editors/render/render_preview.c | 38 +- .../blender/editors/render/render_shading.c | 8 +- source/blender/editors/screen/area.c | 28 +- .../blender/editors/screen/screen_context.c | 84 +- source/blender/editors/screen/screen_ops.c | 7 +- .../blender/editors/sculpt_paint/paint_ops.c | 46 +- .../editors/space_action/action_select.c | 69 +- .../editors/space_buttons/buttons_context.c | 44 +- .../editors/space_buttons/buttons_texture.c | 17 + .../editors/space_buttons/space_buttons.c | 41 +- .../blender/editors/space_clip/clip_buttons.c | 2 +- .../blender/editors/space_clip/space_clip.c | 6 +- .../editors/space_image/image_buttons.c | 2 +- .../blender/editors/space_info/info_stats.c | 32 + .../blender/editors/space_nla/nla_buttons.c | 3 +- .../blender/editors/space_nla/nla_channels.c | 1 + source/blender/editors/space_node/drawnode.c | 30 +- .../editors/space_outliner/outliner_draw.c | 415 +-- .../editors/space_outliner/outliner_select.c | 30 + .../editors/space_outliner/outliner_tree.c | 2 - .../editors/space_topbar/space_topbar.c | 4 + .../blender/editors/space_view3d/drawobject.c | 1 + .../editors/space_view3d/space_view3d.c | 13 +- .../editors/space_view3d/view3d_draw.c | 2 +- .../editors/space_view3d/view3d_draw_legacy.c | 2 +- .../editors/space_view3d/view3d_edit.c | 8 +- .../editors/space_view3d/view3d_gizmo_ruler.c | 33 +- .../editors/space_view3d/view3d_ruler.c | 33 +- .../editors/space_view3d/view3d_select.c | 24 + source/blender/editors/transform/transform.c | 69 +- .../editors/transform/transform_conversions.c | 338 +-- .../editors/transform/transform_generics.c | 22 +- .../editors/transform/transform_gizmo_3d.c | 9 +- .../editors/transform/transform_snap_object.c | 7 +- source/blender/editors/undo/ed_undo.c | 27 + .../blender/gpencil_modifiers/CMakeLists.txt | 70 + .../MOD_gpencil_modifiertypes.h | 53 + .../intern/MOD_gpencil_util.c | 142 + .../intern/MOD_gpencil_util.h | 47 + .../intern/MOD_gpencilbuild.c | 558 ++++ .../intern/MOD_gpencilcolor.c | 178 ++ .../intern/MOD_gpencilhook.c | 355 +++ .../intern/MOD_gpencilinstance.c | 360 +++ .../intern/MOD_gpencillattice.c | 213 ++ .../intern/MOD_gpencilmirror.c | 239 ++ .../intern/MOD_gpencilnoise.c | 285 ++ .../intern/MOD_gpenciloffset.c | 143 + .../intern/MOD_gpencilopacity.c | 171 ++ .../intern/MOD_gpencilsimplify.c | 123 + .../intern/MOD_gpencilsmooth.c | 152 ++ .../intern/MOD_gpencilsubdiv.c | 193 ++ .../intern/MOD_gpencilthick.c | 171 ++ .../intern/MOD_gpenciltint.c | 186 ++ source/blender/gpu/CMakeLists.txt | 8 + source/blender/gpu/GPU_shader.h | 5 +- source/blender/gpu/intern/gpu_shader.c | 14 + .../shaders/gpu_shader_gpencil_fill_frag.glsl | 166 ++ .../shaders/gpu_shader_gpencil_fill_vert.glsl | 11 + .../gpu_shader_gpencil_stroke_frag.glsl | 20 + .../gpu_shader_gpencil_stroke_geom.glsl | 196 ++ .../gpu_shader_gpencil_stroke_vert.glsl | 33 + source/blender/makesdna/DNA_ID.h | 2 +- source/blender/makesdna/DNA_brush_types.h | 109 +- source/blender/makesdna/DNA_color_types.h | 1 + .../makesdna/DNA_gpencil_modifier_types.h | 404 +++ source/blender/makesdna/DNA_gpencil_types.h | 317 ++- source/blender/makesdna/DNA_material_types.h | 76 + source/blender/makesdna/DNA_object_enums.h | 5 +- source/blender/makesdna/DNA_object_types.h | 19 +- source/blender/makesdna/DNA_scene_types.h | 105 +- source/blender/makesdna/DNA_shader_fx_types.h | 196 ++ source/blender/makesdna/DNA_space_types.h | 3 +- source/blender/makesdna/DNA_userdef_types.h | 6 +- source/blender/makesdna/DNA_view3d_types.h | 29 +- source/blender/makesdna/intern/makesdna.c | 4 + source/blender/makesrna/RNA_access.h | 28 +- source/blender/makesrna/RNA_enum_types.h | 4 + source/blender/makesrna/intern/CMakeLists.txt | 2 + source/blender/makesrna/intern/makesrna.c | 2 + source/blender/makesrna/intern/rna_brush.c | 441 +++ source/blender/makesrna/intern/rna_context.c | 4 + source/blender/makesrna/intern/rna_gpencil.c | 1007 +++---- .../makesrna/intern/rna_gpencil_modifier.c | 1314 +++++++++ source/blender/makesrna/intern/rna_internal.h | 7 + source/blender/makesrna/intern/rna_main_api.c | 13 + source/blender/makesrna/intern/rna_material.c | 337 +++ .../blender/makesrna/intern/rna_movieclip.c | 1 + source/blender/makesrna/intern/rna_nodetree.c | 1 + source/blender/makesrna/intern/rna_object.c | 212 +- source/blender/makesrna/intern/rna_palette.c | 4 +- source/blender/makesrna/intern/rna_scene.c | 427 +-- .../makesrna/intern/rna_sculpt_paint.c | 149 +- .../blender/makesrna/intern/rna_shader_fx.c | 538 ++++ source/blender/makesrna/intern/rna_space.c | 151 +- source/blender/makesrna/intern/rna_tracking.c | 1 + source/blender/makesrna/intern/rna_ui_api.c | 26 + source/blender/makesrna/intern/rna_userdef.c | 8 + .../render/intern/source/external_engine.c | 3 + source/blender/shader_fx/CMakeLists.txt | 64 + source/blender/shader_fx/FX_shader_types.h | 47 + .../blender/shader_fx/intern/FX_shader_blur.c | 66 + .../shader_fx/intern/FX_shader_colorize.c | 69 + .../blender/shader_fx/intern/FX_shader_flip.c | 69 + .../shader_fx/intern/FX_shader_light.c | 104 + .../shader_fx/intern/FX_shader_pixel.c | 66 + .../blender/shader_fx/intern/FX_shader_rim.c | 70 + .../shader_fx/intern/FX_shader_swirl.c | 103 + .../blender/shader_fx/intern/FX_shader_util.c | 56 + .../blender/shader_fx/intern/FX_shader_util.h | 36 + .../blender/shader_fx/intern/FX_shader_wave.c | 71 + .../windowmanager/intern/wm_operators.c | 2 +- source/creator/creator.c | 4 + 322 files changed, 39743 insertions(+), 6692 deletions(-) create mode 100644 release/datafiles/brushicons/gp_brush_block.png create mode 100644 release/datafiles/brushicons/gp_brush_clone.png create mode 100644 release/datafiles/brushicons/gp_brush_erase_hard.png create mode 100644 release/datafiles/brushicons/gp_brush_erase_soft.png create mode 100644 release/datafiles/brushicons/gp_brush_erase_stroke.png create mode 100644 release/datafiles/brushicons/gp_brush_fill.png create mode 100644 release/datafiles/brushicons/gp_brush_grab.png create mode 100644 release/datafiles/brushicons/gp_brush_ink.png create mode 100644 release/datafiles/brushicons/gp_brush_inknoise.png create mode 100644 release/datafiles/brushicons/gp_brush_marker.png create mode 100644 release/datafiles/brushicons/gp_brush_pen.png create mode 100644 release/datafiles/brushicons/gp_brush_pencil.png create mode 100644 release/datafiles/brushicons/gp_brush_pinch.png create mode 100644 release/datafiles/brushicons/gp_brush_push.png create mode 100644 release/datafiles/brushicons/gp_brush_randomize.png create mode 100644 release/datafiles/brushicons/gp_brush_smooth.png create mode 100644 release/datafiles/brushicons/gp_brush_strength.png create mode 100644 release/datafiles/brushicons/gp_brush_thickness.png create mode 100644 release/datafiles/brushicons/gp_brush_twist.png create mode 100644 release/datafiles/brushicons/gp_brush_weight.png create mode 100644 release/datafiles/icons/brush.gpencil.draw.eraser_hard.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw.eraser_soft.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw.eraser_stroke.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_block.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_fill.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_ink.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_marker.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_noise.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_pen.dat create mode 100644 release/datafiles/icons/brush.gpencil.draw_pencil.dat create mode 100644 release/datafiles/icons/ops.gpencil.draw.dat create mode 100644 release/datafiles/icons/ops.gpencil.draw.eraser.dat create mode 100644 release/datafiles/icons/ops.gpencil.draw.line.dat create mode 100644 release/datafiles/icons/ops.gpencil.draw.poly.dat create mode 100644 release/datafiles/icons/ops.gpencil.edit_bend.dat create mode 100644 release/datafiles/icons/ops.gpencil.edit_mirror.dat create mode 100644 release/datafiles/icons/ops.gpencil.edit_shear.dat create mode 100644 release/datafiles/icons/ops.gpencil.edit_to_sphere.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_clone.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_grab.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_pinch.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_push.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_randomize.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_smooth.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_strength.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_thickness.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_twist.dat create mode 100644 release/datafiles/icons/ops.gpencil.sculpt_weight.dat create mode 100644 release/datafiles/preview_grease_pencil.blend create mode 100644 release/scripts/startup/bl_ui/properties_data_gpencil.py create mode 100644 release/scripts/startup/bl_ui/properties_data_shaderfx.py create mode 100644 release/scripts/startup/bl_ui/properties_material_gpencil.py create mode 100644 source/blender/blenkernel/BKE_gpencil_modifier.h create mode 100644 source/blender/blenkernel/BKE_shader_fx.h create mode 100644 source/blender/blenkernel/intern/gpencil_modifier.c create mode 100644 source/blender/blenkernel/intern/shader_fx.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_cache_utils.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_draw_utils.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_engine.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_engine.h create mode 100644 source/blender/draw/engines/gpencil/gpencil_render.c create mode 100644 source/blender/draw/engines/gpencil/gpencil_shader_fx.c create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl create mode 100644 source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl create mode 100644 source/blender/editors/gpencil/annotate_draw.c create mode 100644 source/blender/editors/gpencil/annotate_paint.c create mode 100644 source/blender/editors/gpencil/gpencil_add_monkey.c create mode 100644 source/blender/editors/gpencil/gpencil_fill.c create mode 100644 source/blender/editors/gpencil/gpencil_old.c create mode 100644 source/blender/editors/gpencil/gpencil_primitive.c create mode 100644 source/blender/editors/object/object_gpencil_modifier.c create mode 100644 source/blender/editors/object/object_shader_fx.c create mode 100644 source/blender/gpencil_modifiers/CMakeLists.txt create mode 100644 source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c create mode 100644 source/blender/gpu/shaders/gpu_shader_gpencil_fill_frag.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_gpencil_fill_vert.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_gpencil_stroke_frag.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl create mode 100644 source/blender/gpu/shaders/gpu_shader_gpencil_stroke_vert.glsl create mode 100644 source/blender/makesdna/DNA_gpencil_modifier_types.h create mode 100644 source/blender/makesdna/DNA_shader_fx_types.h create mode 100644 source/blender/makesrna/intern/rna_gpencil_modifier.c create mode 100644 source/blender/makesrna/intern/rna_shader_fx.c create mode 100644 source/blender/shader_fx/CMakeLists.txt create mode 100644 source/blender/shader_fx/FX_shader_types.h create mode 100644 source/blender/shader_fx/intern/FX_shader_blur.c create mode 100644 source/blender/shader_fx/intern/FX_shader_colorize.c create mode 100644 source/blender/shader_fx/intern/FX_shader_flip.c create mode 100644 source/blender/shader_fx/intern/FX_shader_light.c create mode 100644 source/blender/shader_fx/intern/FX_shader_pixel.c create mode 100644 source/blender/shader_fx/intern/FX_shader_rim.c create mode 100644 source/blender/shader_fx/intern/FX_shader_swirl.c create mode 100644 source/blender/shader_fx/intern/FX_shader_util.c create mode 100644 source/blender/shader_fx/intern/FX_shader_util.h create mode 100644 source/blender/shader_fx/intern/FX_shader_wave.c diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index ae265654724..65f962d2dd9 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -598,12 +598,12 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_editor_util bf_editor_uvedit bf_editor_curve - bf_editor_gpencil bf_editor_interface bf_editor_gizmo_library bf_editor_mesh bf_editor_metaball bf_editor_object + bf_editor_gpencil bf_editor_lattice bf_editor_armature bf_editor_physics @@ -626,12 +626,15 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_freestyle bf_ikplugin bf_modifiers + bf_gpencil_modifiers bf_alembic bf_bmesh bf_gpu bf_draw bf_blenloader bf_blenkernel + bf_shader_fx + bf_gpencil_modifiers bf_physics bf_nodes bf_rna diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index a1941ce6176..737f7416486 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -895,7 +895,10 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): @classmethod def poll(cls, context): - return (context.material or context.object) and CyclesButtonsPanel.poll(context) + if context.active_object and context.active_object.type == 'GPENCIL': + return False + else: + return (context.material or context.object) and CyclesButtonsPanel.poll(context) def draw(self, context): layout = self.layout diff --git a/release/datafiles/brushicons/gp_brush_block.png b/release/datafiles/brushicons/gp_brush_block.png new file mode 100644 index 0000000000000000000000000000000000000000..2db3964e573409725d85b35a703f92504167bab2 GIT binary patch literal 6088 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 C+NenY literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_erase_hard.png b/release/datafiles/brushicons/gp_brush_erase_hard.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac528406781688374b211b244bfc0c802a16ba6 GIT binary patch literal 6107 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_erase_soft.png b/release/datafiles/brushicons/gp_brush_erase_soft.png new file mode 100644 index 0000000000000000000000000000000000000000..416923004ddefe96b9a5e1fb1589e65aff06101b GIT binary patch literal 6252 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 QHq}9@ikirBdDEc(16(upN&o-= literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_erase_stroke.png b/release/datafiles/brushicons/gp_brush_erase_stroke.png new file mode 100644 index 0000000000000000000000000000000000000000..cd6d21532cf2b089b9b7b59942711cef4d8a9e67 GIT binary patch literal 5955 zcmXw72|QF?*uP|%tV5F~OOz2J#8|SAeX=K{tYwd3#@J<-@Y*I(c0v?+?fVkhlR_c; zmh6=+%kUlV_kH&_Gk1Q^J@?*opZh%jAI z1G}^CT4o*)M1St|O97>(BSDash}Bi6ojY@$RZw6I7kmG|kosLB#*^siMu3XmebfRW z1|kTl*?Bx9IJpy@+;GsjvzK{6obEKP>gMiCaB^@YLdDDd|GT)8$3qtzFq8n*v{uH0FzhsJYD2&|;T)h= zjvtXAM*F|#dlDYnK|3d5tN%Yt)y>t_4o8G`KPI<;7|rRhJ5EFwJE(ADkRO8Bn6PS? zJ3i^_7On>OjSrNTCQ?w+(jin@!6>-p!2?0{!qCa;JNFOgwBH8ezrCz3_8ckto`E;X z$QT>gkX`+>@z=(|!OPr&t12%4)8Cq^6`fUQYASlFdjhUS*G2h~65y=a*p1bbb}Y{A z8C(TcA~!xQ=B=mqsi|%$Woa*cNq%Vpn#4)p4g4oImORgW^7mIe^JdfDR4j+|mGC^I zty%J&r~Xlo-17RcyO^d!<$_$BQXedW|*X7y}1~CI8K)u5uSo&#EyOy`>}> zk-6Te&1<61Ra8|~_4M=}J$fXDq=LG|6HsTn4Fj$J$!Ke9GkUGJOsrQr*&jgb@=x`Y zpCj)7S!|DFwrURW{rbjuZ^A==P|6@;f92~p3et^2+{3cFQf^ZdQ&WnPl6-yQ5XAlb z+h6@IFRlv`Mx6c-qlu)02oEe`)6uIPGf6wuL z4UWA0I!lC^iNHwL0OIyge+s*|4ejtY`Skmm6HLZbJxO#XG^E49m#RUteEmkNvS3bkbm1 znpH~Qr}a5sJ7vm$Q=w+u!q~XV`RkkESRYW z1XkTg2LoA4zQ;QwR-(efIyM!3>IPy+dC!&M6sy2xqU=biRaT|0bJ%U?VFYSN4R+c5 zvvU=AC@yw(_KO#1eHFc8?>8|A!&NHA)^_|jI5@o5ze{G-iY3QnPfbmEPd^@UuKM=v zn>fR>QEQB*MAiXGR@D4mNeLfzaFIYw8Io50YL2!>_?qzA&!5=>D_|N9W>Pz@!t|Ik zXU@2_kaySC);d8Ax{PKIEe~!t@Vs1&J(qZ!+tlpNojVp5larGQH<#Z_j_#?AcbUg) zs9LdeF34<@29e)R;`C(+MTw6?3knLFj}KPen)mG&d!OI0cF7UwwH1Gqw=Tsmfj2l+Zl~>xx3PW6lesjI zesk;e7L}FTvyt529MKRI4(}^Bc`M1!&;Of4yD|dtBe_s=E!f%F*)erveLaJT3aaKh z@_x&g42spN*+nYN%4ar$x+5YHH=K2qhL(2PcZtg^d3ANQueTSsk0PO8>d0n%+T0uX z^y#Vs*B*C6yjriVS?BXVC4n8zRVK#988-lRi4<;(bJch2uW>HcjONj#`pdk(XkU_ zb@TviL}JpCuNh7GwS_24#>+4p0YqwAnw$j5oWlpcdidj%F6;sm@&_?=^?PU+U z;_|$M{k7m~INZDT+3x;+e_ zwKO2d85c&&45~bDy0n;uczm=ErafdWCcphzRVwYe=R;VI#;dY17d}2d8JHGl9qVN5 z0iWXc_1%?~m5>mM_^vQbVYBnQGZH*1vL)4}rJ@Kr@+@_T(Yro!oETzW3yR%38T>Z)Mp{9n0hNh;P;Zwg$i}L`R zuY|)@Lag2G?F)*Eikh1hGZI&m)6DOz=D{M{Ei5eh`}?KRTn~5GvEwN6@$W=b4*-|* zLM97~i-K_yUl@y4bFK+eAy&e?5LvJ89ui;)B9Y=tARZ8KFsOr{o!s(%mq=) zBY?7wVo0yG(Xwm`{_iXs|5}!o78Vv37u%Y7fvztA6}~`}Jv=<*zj`(J+SL!d7t~-^ zp2CNhiLS1$BoujBKj_-EYrWat+I)^KE^E^#$9oG43-z7=RaQvV*jsd3zl5nU2n6De zvGGk#rqqIYEyFB5!{LLS)vm5CO-)T|Ol!*Rg0-@EWf;q=SFaMaY)_8K#>U17b-T~+ z`B;dcnv#=~UunjJQnYCB;&XkbVty;1T{h!gd3kw#y{wXwl|nl2c~Ip7(e;GI*JEQB zgCEC}x}SDXz%G+eleoQ=uW$F($5TRT#l)Er>V|8ptNyDcHFR`z)o#-`G`mqvmcBV& zXEt=qMe3yvuI3Po2gq1U!GyTZrj5m7gMwPd6)t(mNOy^ZCkSKV(7jhqZf+^4<-L}Z z$9O!xbMoUvXlGZ~Y5SCp+&49?@ml|$=Q@Z;NoDVF)*=yQGnnOnDLXj{svG{j9-RxO zdQ~CwOCpOQ!p`h=L`0ddpbAQzk%{S3e?NJ{yXAgMAo+o&h@?2m(aURhxX8#V;CDp( ztj|KCCaK!2xRw-ZH&$O?e{kSeT>It?;58A;dbETbhXfa&OgNk}oJU0q9^TW_1IB=a zU3dImlo<6v&5#p`{5w8m@1v9{*;N0cvYAFod(to{*L?SWB7HV8&Bd&k1lAiM6WsPzfE4S;2K93!@ z9rG=7NL0y=CD3BIrdpRU(G=>s>VFz4foR zrOmP)KXja&oCYrnc9Ux7{O2NA73%8hV%9CL zu<_r>T(S@uu{!>9A*SQc_K?1Vp}F;?Z~yKaAk;~yE<8iKqh#t`aa9^f8N$DTL&}K@ zpbL>toSdES^>*iX&IvmP1RTXmPw`+01q&^3!oX$XB#?JY zO#CIBlOGx~hodmW2J^s{6CfqNe*L;E>i)X(3hd7znLFUu`_|T0&{%+Vo>liG*A6qm zmk>n&pMl`vaAm5?p$0TFK$Tp5whZpC_gaE{Ec%pq_RoHR zThk7kiepzC*H*rN@)eN|XHry5SDNXse{pCe`o?smJ_S5e!Qac-d8OlmRdI2#xiEDR zxOI*}VM|L(ZfwxD8mg%d2dZiQ*{6n77Pv!=9eW>#kdbrVP5DI{B(l7)F}Xf0VWg5 zGst3kMHD}Gn}JqEiD5lBfP`WJx z?Z%sb49HDu=TW1&LLhh`GekI<;{S;Qv;gWcMLWx*KT=(b;WB!fz^X^{&Z43E-+$ry zL2N=Ip2@uIY#LX}Q9Xo@Cu;L5%J6W(kvxa7jg1Xu9`|1}@9kJkD@V*4lTF@U7Zoji z^XB$d%yJCMx%5W+S39VjLQ&Ur2_5AsB(7=zHFX~q(^jG<+ z8=-Z_U8K?_b`Hs~%Yc;o%1PGxhzoj`AL7-ouwY3^0nCs%6})p!V>)Sb^!Gg2N*n+5 z0+28FIl(4MhFZ624T$OZ`tEOUSB|fBeP*FzRzEwGuLPU{RLT#;$H%{R zWb3+TUMYn1i0a1X&{+I#rMIh5`rPpii}#{T|?PZ&8a z`7R`OY<#{;H(1USt5NZwBU%e3E+z(NpaE3+o5}0ngxWdw86W|#14_PjZO{>Yt6GdT z>H|7|A&o)>_>k0sCy`I|GUWxagAxPs754q9E<|~YJfSjx1?%HA%*@Q(N`V2kws}CI z6A0p5_z+)o;m9hWK^zWn-@FWFCjaNC2*t%2v_uFjIu9P4#Vz;k>m>Q3!~j}KO2qEG z*cmt|ja#zomG{=)7{6^M?~Z3?W`6ndWp3`lQPt{>-!~OpxC-!&0Q$q=Uf$kW03v;SkZG)4c;UXei12-xwhM+vVezx>G6=e;+@~XnHo}zI zPWRv)yqa;9H8o0yYZVRNo0i{hjs)DL!XO#i<$txF+1c57{P-~_X)rYide(!Wi>SO; zG8j2LcU#HtB2#30j={$rWi=iWN-(;&r~f99T(7eN>+$oQdZ)a7c6~j z+Mh6*t?xXLO{2gd@0CNXku{i&(YSv=2!(k*|pzH^T&Ugiin5+CK{dAKkC8E$Y@q#B0n&=y3;!q zqK5A&BcM3r)Y{+}Di&-4S~so#hiJ>uA5i-sx8n`oZq;mw673{d+vjCsRaF(RJuhFp z_F@9VJE3c-;m-=XCU#JC4H!UJxc-kkTCG4C~S!d z2VF!w=gmLu%u4bnM_c*|J9ny+MVJq zxfJ-<28e}&?d4CO^y0f7){H;beFE$o!>_5EaWSw^<)J$`9L~z>gg-0b0h&F_f1_?+ z>io1lFmepZb{y>NKoZtBGysd_=jZp)-owx^5rvhZZ0+$epYgRMp%h**>(DA5{NzpP z^PFi7QSjfLz~gOzKZT%Bp5DGbAU;%cz>1f#Gt zVP#EC^W?p0Q9(gp(2;Jc*AyRv#RdpUE!eDE%G6Dh>i_f!0B<}v+sMcW5PWA86Hgyn zS6^3GQd-)oo(SyA@)u2R^2?|6568~7b@%m+H~Ll#EzA4wt}PF}wh+1S={9e;nxVYk zwgB5mY2eOVb%k{=4oKpV%mbJw?+T=hQ2!csq)b9#;16P?fH%5~!m?f_E)V9{Oa~q= z^gLr|cbRT(dh=#wdwJ;k_3M(7l3=-Kg$wJL*GWk=t0j$O=)6&m_}Jf{A@BPqj9C%M z!O<>x{`~otk#TJH)h0jhx*ja>si|=E_LC3l=wsn)rnm>!U;3|rnTv4uIAc4 z`y&o}9}9#;#NMBo{JpI6j;RGssks8F1;2?7Wv1N$${m?13aE$Yj0B9>W;S_#kp)C= zSh2@yydKIK4O=WqtfS69V~7AQ=^>}cJcS+^-J3zFZpa?{#0Cj8d}z@kp8z!#rd3C< zcy@k$L@+$T1{wA~m^&#pXitwNiG6fV`6JD5c+LkCx|}hxL1V&s{w9XU1}Ij;TqL9{ zfVh2*i!S0SKmXSp-l=;fmWYnD=;yT;Tkd3*WeZ#*ub-uhS{c`tZJYaud7!YHnW%*V z*;G)gf-s>x42+B*qon!TM=&uq6A6i=vf_i;HE#ePaLg*gi0mQc_a-y;tn6 zocGS`*UgeVIMrqRz#e!l3X$>y(^6ii1?(e#DZWd>>P$>WTe+}Q>(S9sdOBxXfAV%@ zD_ca~vjyVg1QZrHGF**+K)}S(R%~pnuY6{4_qU?%qV6S$i3^-CNa9u1SAHnkhmireig#@2V|km93W&-)F9Ihh(JH&#bV{pB+g z6IoiOv;G$LWkgJ#o(7%)Y&2oBpJ=0V47yi$XecR4N8}U~fJJJb{k6S~KBo9CM<3I1 z+tRWb*uCiJXvDcmIW}5qN{U97fu*eomCG&DN)i(17--0WC$4c7lI)D1e6Nzfwq`kH z|J>TA1QZ3x%eq%4Zp*L9CsJ5MNa$cJbZB{^#?5MCbIVriGl4cbrVZ$V;o&5|IB_Yd z59J|M*rbQUdv7Riex_iHvr%T!%8>EYW}ECTlQfh|h)N!iUWhnJ4@siS+h zlAI?P5f@q~mGKn+_vbe*yB5Ad6TN&TEo5$h&Rg8zC`;vP$lM8nL=#;zA9Q$RIxm8u bpE;oz*yaA6bwZ2*JqKac_0-;}Scm=(0L?X= literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_fill.png b/release/datafiles/brushicons/gp_brush_fill.png new file mode 100644 index 0000000000000000000000000000000000000000..9dac633139ca77fc44e69f09afff1563b123c7a5 GIT binary patch literal 5707 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_ink.png b/release/datafiles/brushicons/gp_brush_ink.png new file mode 100644 index 0000000000000000000000000000000000000000..410a77f61179c214d3ccca13ee9fe34be03623b5 GIT binary patch literal 4034 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 Kr78uh$o~PGbecW@ literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_inknoise.png b/release/datafiles/brushicons/gp_brush_inknoise.png new file mode 100644 index 0000000000000000000000000000000000000000..5356f697e018fb216398f97c747e52b4907dc648 GIT binary patch literal 5591 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_marker.png b/release/datafiles/brushicons/gp_brush_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a62b78ca78f65e65625fe710f2683e73854964 GIT binary patch literal 5996 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 BN=*O& literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_pencil.png b/release/datafiles/brushicons/gp_brush_pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..2d1fbdfd91657431d7e47a5ecc65b5f37977ad3d GIT binary patch literal 3965 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 EUzIjl`2YX_ literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_push.png b/release/datafiles/brushicons/gp_brush_push.png new file mode 100644 index 0000000000000000000000000000000000000000..542764309cc32106661b938a756cb78db615d9e9 GIT binary patch literal 2914 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_randomize.png b/release/datafiles/brushicons/gp_brush_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..0dd1a131d86bd26dfb9c4eaab91a34aa99e8cdbd GIT binary patch literal 5726 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 c-yZ^bME^(=UnH$?^~K1^KnaX0|3Bh0ynT? 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_strength.png b/release/datafiles/brushicons/gp_brush_strength.png new file mode 100644 index 0000000000000000000000000000000000000000..a0513119f2963a755671e09b71cd646617a46fe3 GIT binary patch literal 3229 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 C%I#SI literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_thickness.png b/release/datafiles/brushicons/gp_brush_thickness.png new file mode 100644 index 0000000000000000000000000000000000000000..6026716f026ab6e4a532ac49ef21f682b2fd2089 GIT binary patch literal 5066 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 BlO6y7 literal 0 HcmV?d00001 diff --git a/release/datafiles/brushicons/gp_brush_twist.png b/release/datafiles/brushicons/gp_brush_twist.png new file mode 100644 index 0000000000000000000000000000000000000000..84b9a90e9d685816363d8241844129522f56a391 GIT binary patch literal 4776 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/brush.gpencil.draw.eraser_hard.dat b/release/datafiles/icons/brush.gpencil.draw.eraser_hard.dat new file mode 100644 index 0000000000000000000000000000000000000000..1e909ca8ac93d53e8182f72524b52fed959fc8e9 GIT binary patch literal 1736 zcmd^+-Amh07{>iGR1n94iZbbkYcB*piraKxD!Ndiv!q&`v1!vPb+k5xVqK}wPF*e7 zv&Oa6C}L0|5}4iHKxahoLJ$N&5Y2yKmJ{2tja>}A+Y0abJ-nRv?aA|4I(o`vxvXqu zki|G_o2$>!JDxB{P06kWdeH;u6qfADpzU+UNXL7-#?sjTg!AReZErb&I1zU?$WUv6 z=}yWJHL(Qm*l|(@TyNKCXX^dNNCTl6(2eQFTJbhl)gh3x_0)g8o^590epDKK92Mp5a5W92bg_G!+oU99P%}62q&v4+@8c z!@VOx+&lVwEQm^-2%>PZdn$;#r~H{9@@HG;g1B|Qc_E0K7weZmf&|j~!W|N=zsC8Z#*YkCRSu@A}=4l(8@8* zBl$xUB8x>0dr+&@sgvKdnB<7QPGP&s)eeK4?=Z^Uwt#GJf2p$9o0DI^U6o6_Ey`}V zzf`af^(%!?L&6!TA@8GqpDFFR51|KtQ?uH;dysM6V|4}+`d9Mtld)Fy6>__!Ue%Ca zOd3@~etmYQArGEw)js4m#c$|8hD| PFfY8VOU+F6u9(?(ewV5? literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/brush.gpencil.draw.eraser_soft.dat b/release/datafiles/icons/brush.gpencil.draw.eraser_soft.dat new file mode 100644 index 0000000000000000000000000000000000000000..7242f76a0f9062edded16d26c01a87b3ddc75117 GIT binary patch literal 1880 zcmb7DZA%+j6!y1ht3`~8#+MolVPjH_L`%fHbQ;YhHPJ>U#t3f5Qd(D|Yp_Ido1pYV z(Iu&d+7NWrX%W%Xly>`}P$;4?NfH?_?|rXId{&gd&RNt$~F~R zWXyth(It37%6{3@#ZKqVR{lDFebh0+H4o}|5cG9coxy6F)`XPe1RamW3FhjjHBpte zuhOIJ>dEW5;6AtNUUIHDvQ6g=g_Z?xqJ7a7Qr<4^mvuTh1{2(D9d$GhYWsA&U=yrK zM&y5?Cy%jPO&n9{DR7mv$wi)uhDq;~GuolBC>nHzTvlY-Qf$s|ENK|wMjcbmLd$tW zeq+wnY?F*BaOJW^_I1~aL#*gobS*jOyB~EV+6C*fd95j7n5&O#W2yK?e3RwXVf_#n zP={GQnC1epR4nE5$2WX_mcQ03MkM2#@y*B2W2wi_=U>E9;&~;;yqbL-OU=HHyoseE zZ)V=cQZsKKY{gO!w(f5S)AzS0-v`r^??WGg>ClIXkHPfB$MJM9J)RyDpTRWZ)ZuSJ z>U-*YH52M^&4gx59oCFpx~mRfx~mhp=mPp7@VY#}@|Opi?y!8*9pf#QH{QC^$MRSD ztT$NRdZYDQmKRSi%d@=>@#*NjCO)}G6iP72qe)q@RH0Dc)`rw8ge2qbWaKjF$ zp)ZWSL;sqZ8i_yEpMIkV?Vusu0T4R?7xbX}rSX{v{lORc-~wHs338w(c0jd69=_ua zwecM@BcAGvJLr%8RDbYCZNy?8>;QETiydUnOZCs>_-{UX?dI%jy(7PFZ%Z|HcXy>asC9I7B+UUlK0cOw;F-7@ z)B(caQ~Mg9azW1LeBAxlp?xsl|2^Lg4oNvc=tT9J_#q_e2b}paBKg3dmX;+SxV80* wzbN@=2QCL4%RT5S=cp~n3yX78Drl3n&;DZ392ocn#8IrrXkzVDKq zy)~j(Q$vi*c!SJVAj!Q7I71e`=YD2-o6GuZy$YXvG!;m3J2P#(mb37>VLfH=8oY#u z7^!Cz<4Q*5Ra3fQg-_;DdF#gHql$64U&(0E?p^kGv@gwNre6hKPo)Ac*&s9HB^`72 zP=A^d+*#`F&4T-Ipvj>pTAg_+@7^CY+q%qFyVXu&q{C=8nawsmp~H-J^^I1izUOe@ zz;%SG_S|o8W~m&Rr{1_i{d0DoW=XZI-^Y%OAF#8IQ^R??Nn=x6wM9$OSTgr0T@9p) zRQVcx8bax8=uz6#xQtMCv{<#)=JWQF;SW@GVm#~kfE}BPmV#kfzoa5Y2+xdn3w533 zoS`<}!e^$_+%{M1y)vZmrC=hijhFkioNic6fhu~6@YFMPV~TN=SLRVu3g49>nWt{7 z)+-;C`|BAcll5ao3p3UGyV#@8hnj}5|=p}Z0dG7F6(G9X{_4w_ESU2P%;;d zCrHsF2t-enPtM4BlCxpkxkBX5pV5I!0 zxKfIgS4y#uk#g+g;#s7;cor?AbIU)W6^~;Nu@!6ukKu7V*7*RBcRuKj;_>dNErQ2w z5yt}bcs)e(*F$}?G~YMtx<~V_djofAe&Ful9hx7!A66PK0}3 z;BkMh_XSKg5oYJv`H3*iCxqx`Y%Nmu=c1dFlapd~FbpF?OifKmd6s2Gzu*5ks%xPJ z0{&O${|{c>Lp6t9p~p(UmAjM4q<9%Zyd+;m*J`YOCy=l9rMeEh`6rL6YyK-<-2?E- z`>ecc=%f0s%kV~^$IJ5dvrj~l`$cqdt768g73VcyiHpq*qHw_>2V4r9PV{x);_S7n z2r&ttcD9JCovq@xo}f5{5fb)&|3svIcq&%TCehh&ekC9e{5yoe0bvhtz%Su{-%~l~ zOAs{pH?g4oR0kN=)k}K-LBEQQKev7@=?b{fA(uGduee6yfT^xe9B{W=E9C)yLwbY$ vfY}QU_*Z^SssVo;1_MNOP)#EU3m5<;y&^W literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/brush.gpencil.draw_block.dat b/release/datafiles/icons/brush.gpencil.draw_block.dat new file mode 100644 index 0000000000000000000000000000000000000000..7a7402ef673a93f53d40936df786810921e54e7e GIT binary patch literal 2006 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/brush.gpencil.draw_ink.dat b/release/datafiles/icons/brush.gpencil.draw_ink.dat new file mode 100644 index 0000000000000000000000000000000000000000..3c654712783a3f1b5e1ff7d0f98c23c52ec0e72d GIT binary patch literal 2240 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 CyPZ$~ literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/brush.gpencil.draw_pen.dat b/release/datafiles/icons/brush.gpencil.draw_pen.dat new file mode 100644 index 0000000000000000000000000000000000000000..cb6fb77924ab76b452f913a36bdf483a96116f72 GIT binary patch literal 3158 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 literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.draw.eraser.dat b/release/datafiles/icons/ops.gpencil.draw.eraser.dat new file mode 100644 index 0000000000000000000000000000000000000000..323d8c23245f035fd3abb7d3b0e0ff18c27ee478 GIT binary patch literal 2384 zcmeH_+fUne6vuh1v`LfpG-)qOOVw5t5|?2RAt59LD8?PsR3URIv2!CNjbninkSYyS zucDcdhZd0w-eJ?+6eCyTrO0ZiJ%WPN<> zpMU54&iCtMYUwJI<+8F3+kt1xH`qMVD6}5-ov_Ews5@?5;^Sx*06Lyq*(lJJl(NpvUvu5A&y%5gKr{sCJ&^oRkB^iyQhSN^j z7A#RWr{#&k<|d+3+tN&H?o~0G2`0=;**0uh_P{gP%o8Jxhkg0p6E^Bzu*9ud_OS2R zdER|WzHM2xufh3ndL^xiTUYHXLcaISoip<&Ij+a^pL9&oYxWJ>mha5`=FTZ8w2m~6 z>lrx3Q8Q%=GYb|@>!{(0CgN6grN_xfAvtUPfXX3 zs{O&lTzYnMu`pkl&5P;a{-cbT4&|nI1F29>OiiwbvjMnAGm#xBIkO$j1QRpcQX-h3 zY~fWYK{|s8DPi=qyQHLnH+ljo13$SgrgTCeC8pGV?J#9CvTgTCr`~F0+g(i#h#n0@ z1K;4*xO76Dx52F)uJzS{20=BdE6fM%KvctpugFHaQ4N<7#lLxCVEfSH(3sG%nPMtF^xBAr%J~tFovpm-|)R z<^C&uD(*^OIiuprnX6_Mch!8&q~flbZrnw0ZqfvGldk9{sEY2&E`qA;s=7^3Rkv#> zf~uiDhdV-0YJdG`^^ll;l&K#LCc>+8;2rdxpWa<8Iyt=?+!xa#xEM%nFWk&=_^20j2D8hA0K9o7kAN=&Q$l#O&6 zS<>0=(p%eIMo*K&=rQmbmx0#_4Q`#F9j^0ght>XCU+gHBkL-kUONGV4TspRgZpIJL z&)88kgK=g%vJ=arqtA|)3cv8aN}vKDfH6P-Mu5+NPl0lv2WSB(;5vY7nDYT}3HT7u zmS}~z1cZQj0P}DSb3Xw-2C%LV=m!Q$m>{x19e}-HJ=}}+@f_?4_hR0+z++$nU|xA1 zV14Wn-yZ^F`_6&iA;wD39Q4b0!E&F#wyzyA1*te~@u z$c>*eBH;!NUPp)W-bVJ4NxKHYscpE$=j!~e`H%qq+$)=_2@Bi8y0SGswk literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.draw.line.dat b/release/datafiles/icons/ops.gpencil.draw.line.dat new file mode 100644 index 0000000000000000000000000000000000000000..238db63807aa4c5e1362443bf5adad9db1e87999 GIT binary patch literal 1664 zcmeH_U2ED<9LD`5qqP(n^#wIo@xqH`rGr$*WY(8BGgy!z8|lqU0Lr z)zo*7!EcWI-GVhQggL$Nl$-RI<1Gd*FDZ00)1>tZX{Nmug?v6Zb9YWc>f+Y&T0Hot+k`;1}z z%!p^?p_ws-Mm(mFF>DErOeg;ughDGV_4ot+Nw|9r|yV-Dglq+I@tV zVktz?$yoz7zawG^S_6&<88tIxltdH>-WbL;-i`H1KGBYMNuG=*n`G1x5$aCFP^eQ} zwvw(=9L0*|SU=uP4ddNJTNotT$!4aPYzp;UN2sT(Vk=$ER`QK(MJyL;qi(&+614fp zX#+jM>St7%k1@|tx0%6i3R!}B7bZG3Jx)i;mk>$!TX?L_YY!%&rXSohs7d+q6>56A zd<->x>%N7WoZ;8d;eQE9-;3)tZSOW*`{r%B*3)gePuzy#_xp&^5%gvn5OaBy1izSl zID+BQXZWPJqweEg@LcN&zem3+!l3j?)&1vh)HAd@`CG2t^S|yl?@42Z{bs&g@sBER zoI}hV^Z%d8ZRkmDs=36O#JR#;RE8*%nzNwMxI;|%}FE6OU$Cq75ZD0Y0-PvC+Z AumAu6 literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.draw.poly.dat b/release/datafiles/icons/ops.gpencil.draw.poly.dat new file mode 100644 index 0000000000000000000000000000000000000000..8351e48fec1cc7ced203d97b5eb0d60afbc447b8 GIT binary patch literal 1736 zcmeH_U2ED<9L9S&_9?8TNU1NVxr%~=WraejW8JJqof#Np$VR-`E(&7N1)VOdnYAmf zbfvT@VtqsF%QW_dt}UgsG~2GXyM2LOpPl2Dt{b?m;1gq3h9Kv^k6DUhjUE21RGqO@|t7jj$K3PI0{d;w#} zX{}#Fj*(hBL1WGdFF~8!W|xgIxe3M+G*UL$>b(XJ9@6>rUX0cGa28`{yT&liM%oyQ zv$1-#iHEpqtZrktswGG;iL#9$7`~7y&UWMdxxGl6>&5%gX0pTeV)b+jdX;aaTZwX} z#y3*MY$a10b{IT*ugR?;%`UB9J)@dd-PL<_KGn3&r}Y~=BU47oNR5&vw;<2$#ryF- z*Arx}m+S~~y2Up{iEm_TqLl2UTj-pqnyuuvL`jr3`{E9Kc42>aV_%du_SX+YY5idB zP?XjVSC2($^?2n3u~|M93s1*kQXl&WI3LbfvOP}>b2lY5lZkmIy^WlE+lEK2K zS5N@zHPlTgE7T1rR7ZW{cOT6`LUT|JjiEVcowYJK0TlgU0zi@c`3q3=X!#f@`r3X8 z6xsaGfyMn4;I1c^HD&BNT>0j8x^kxLbQix3{BQRmqa)x<)&SyiFDCwC_U;Jy^B=*( z+|FPeodvxsXF}`9XF1>zJ`8l{@r%J8%ANczSMK>=_pA4$FhhPdU-roR0dJH;#2xYf vpUG`79p4<}5@izQ3UL`Qgqc*F&L3Xp6Xh6X_)lJ8R$)HTK1xQBqrH9tJJvxt literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.edit_bend.dat b/release/datafiles/icons/ops.gpencil.edit_bend.dat new file mode 100644 index 0000000000000000000000000000000000000000..32f7b2e96318ab7074b1d1d35d1ff64cf260e0e1 GIT binary patch literal 2330 zcmeIuT}vWS0LJmYiZ=(Xm;)xB0*R(&qFOO(?G2%V66_{$y6D1+2!jeLRt2H#dpBsYCh44mcO%nb5 zVwUCUf-~!!QKk)ViMviqNuj(iA4v!It71f0kLRQEp6Jnr-v)GnlI%AqGkQCf@h%ku z`kefb!D8;pNN8y_`5af1@=@-J$K3Ut%vNTUtWzAxjBbG)xCwW`A>4uUAP56+1#H3~ zAD! zw835TI4za;pX2h;{pw>_jKuTD#i;DjCSHc$1gc1+Go#|j?bK2+s0H+gjGD)yk$64R z($8@tp(b-zSmg1X9MIWH)>$CZTVR(+?}8YSo(C&LdIf9|X%6IwbPhZa=@>X6(kFl; z(&kC)A8EIhNZXcChzhr`w&kW^CvXyJCm1Bs6p^(P=T;)!3Ob2&W0#Gw5@$P+_5qei zvtXV`4}x(bO`M6eOYSdsKlK(|GNTOW!*8yS{_=p{Q*lH)Qs@+g98qhG$@UiLyN(o- zb?|#)RNhwb!u_hS{unOrO9x3IEgcuVrDH>9v ZxNukcg3NEoN_EN zcic$BD~~dA4Fwf2Lr*z>f4cqC@aO;eyS#DN>>m(Kxr+b* literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.edit_shear.dat b/release/datafiles/icons/ops.gpencil.edit_shear.dat new file mode 100644 index 0000000000000000000000000000000000000000..e6b51f988f87ed6af9ef8d822524b1c40a209727 GIT binary patch literal 332 zcmWG@_GkG2pMgO`Ypdc~tqYf(@A_Q6aCxiZ-MboE&Ua-LHMC?Dw<=z^eD|*N-3yo3 z0##{f`2bZl207TbJ63GkakS`g@LAi(wrBUqEHhpvqbDP7EH3l#?CHm+&jz0jer)@A skIWw9WyX3kddA|$56?b)eERW!8iR-#7ypM%zW~O_>?adV{?ifz0I#X%)&Kwi literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat b/release/datafiles/icons/ops.gpencil.edit_to_sphere.dat new file mode 100644 index 0000000000000000000000000000000000000000..bf1181cd5006dfe82adcdd392adda6ab758488b0 GIT binary patch literal 1790 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 W6pV(zXb6mk00kisI_>;_7zP03K090h literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.sculpt_pinch.dat b/release/datafiles/icons/ops.gpencil.sculpt_pinch.dat new file mode 100644 index 0000000000000000000000000000000000000000..cb2b43f5597c8362c19667f20b6f576afc0b4b2c GIT binary patch literal 3482 zcmeH{ZBH6m6vzDv8ckZX229YJh+0#P)~Mt@-Xo}nTn;~0;b=lV$o1M7!CQIue^Y-ynd%+!ae7hm{QyI zjtRToJvXH`OHjzJpHidHHGZ>pwGOMShz_%w7d#i`OFDpmBFZh+~9MFA=U%9`#&fVWJB7%7R4!4GKg6VJkM+;)K;yUzWo zl|5GkoWF8pMW#`+2~_HEsyFrPaG3aJ!HoHa|NK2HA%Y*4;D6xHyh{9;Z`i90KKCkp z!>R6s;LX#7AS?;hR;pJ%KY(2)DttWD|XxDW6 zA`xigBHJ#u^&a27JtkxD>Yj#V$-p?8{#4nyp{1#h645g+a*a|DShV(1UbUX5<;yseBC|xxZ|mSNdU^&;cXrE3rsxV2%0fqV|8Asj-apXNJ zuBKA*NK&VKBx#b>DJinz5vr@LCc;XLS`jw9!48le)L z?51D^Gr2aBCEc>kPBQoEwjal<@?nxO+ZZK9sau6q6D_up?>4(ljM=?H znV1#IMw31p?eJLr4v)*f7IOJFg3btbXJjL|x#Ns%?od%z6uT=*MSbzDxG#Qi7Cf=h zRzK-Oe==IHIx@ejj^W7*0d;KlNELH<)gfLGv z*F|@8y={lCw*78Z`s)%((9*R+%P*5mA{k^-HYkK*RETHIlkQpbq8Sn|npf?Rc-4-H z5rKU~jEUbL-gIN)O*cYyL*hvXEi&1)qF&>`%u}LU2;*tc1>z`W8f9=Q78F#c7`0x^ zNHz@2GI_V!wMup|BNp(xz&6SP+qvQDotsvhs|zewZXAEU}M8r6kqvQG!L~@}VSMK3oj0B!j4|s(^|+L(<$C!}0VYAJmgSqUB3$2ceHJ;MR+7-I-fEi|yj{|V_nH>7 ee7^JT>-R4|eom%mN?=N0O5i^T(7E2^pZ)-fQf6NO literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.sculpt_randomize.dat b/release/datafiles/icons/ops.gpencil.sculpt_randomize.dat new file mode 100644 index 0000000000000000000000000000000000000000..35042936757178afae7b2af03430981f9380aaeb GIT binary patch literal 6254 zcmeHIYg1au7R|5F(Rkw|b0y;=Aij>MD1i__F=}B(6ERXcBdO?M1=LU|L=#a=5M?0Z z19N0TmIE@o*3sVh|M{c~s@&e4HPU+kI2_>-`0=tJm7S_wGk6y8HaXsJQU$ z+l31%hBQw_k0U3N6SJRo_DdCy`=x_jeNP6v)QY|*YK5k^?}?`OkAc2{KL+&Al^VzZ zL9B0}Tl};W*7rT>>1`Kvi#z50(w<($W6^_l(eKi3vEp&3T>soKqBh)b7CmSb8%8Ge zTH}Jz@^W#(^3pc5xUjfjo3Wa0GftD$>@;o7uUR157q{lUX4kUUj9FaETl22vH4A3( zTes|f>z>2A0ol8;=LoKPH|&c})0)LJv0yaLp$5~0aV|8!_iBH3@0EWph+@-j6q|IT z>r>lfZghJLn_QoAOn6?bPkCN!k2xl6dd~}+-Zt#k`sc9eAZi<4dA6zEdlf|YXJN*z z#ip^zAiDC*IcHA3b!R&uJj+{$WhZB+#ias`i0D`V2R zWz0$rr-D;a$>CO#DTP!Ly^u<$6|&2ioC-Quz;6`P2n(r&Y!aP_kSQR`rqk%$wA{2@ z0yQl+9bkGc5g|}3c(qkkcNzu1HPyA4drjiOGc8@E(84G zT4H)UDKQQj(F> zoGS&XIcd4c$R$cbKB&oqtE6Or6~Wp7saFcpa(~WB%}GW`bOM!#^w-FFgDg2uR;6Of zsvcFSn2##jxzJZJRm^s7dnv$D2}i}0aKx;KH-KH2a6l_oF(3mhZZA|Z+6y1$!-$Fj zX2=KUpCRX|n1d`?PFJ?NxGP&hR2OS0y&1|>X`U=cMeohfQZz*hVsD0~NJ~+s>JVkB z>Z%UWA*!otf|{UB(IMKDQGlZ<;{+WtPM8AOsjIW8V+FIR^Vf_;^Vb$hD0Pvvl=(lK zx;j>%Ltx%mqy}Sj$g{isHTp_5|JhxA<@ZX6rV;_aM#yWd5jG0yT7GM~-+WJWPXs+- zN23_ts(YgQ&7Jb!rSQ%PJ46qZ8vXM@o&LGnFsRcS)ds_eac&ev4Wp>mIH`v`sb5?$ z&Mg>S%a~=$9$XDO@g2}v+MI?VW%I%cf!s9zU%vLLI18V>_>bN-+}KCV8j>2kMT42F@ESf zbD!g<{*POy{wRoi_I<=ZZ-2zk@lPJepFCf@Uv@rkgXa6Z4S4&D_v6+l&pD_)-}<-} zb)9ZTe5addZaDAh=ArN8-HGjJ<-`_U2kXB*c^6qae0^+=EFGJpZzD_lvtd(cK5ROC z9hyIRceD~&impdpVdt3}?f~vD?1cXnO0{I#$X1$->?Au%mNG4wOElNOrW(EtZY_Kx zHk7V`^(PxjD?saj1Kc4;&$4n^3A}+f9oBAh&s6=(#^I)yjo{WbP1S1}jCUt$jCVB+ z`btB!ns2C{s8REW%Jh}}HG{07vi=%Pk-8Y3I~84)GgAW3%1p^3$yD-&W&%5~(w1m& z7jI}wnU0e0W&><1fxCnZ5ir@!cHP`9cHML`c8lHY0Num+v&_Q@FaivK^Z?h#4KRG% zKg$A)18$HTz8T~OxWOC28vzhMxfLxxoNM zWHSpda56B&n~Nc9*S)!`G*9+smM43a<|J=sIZEosTFipCEoQM@Xcyb1o2_o?X4g*J z4&=6-Zd@LahvWgpo;=hAdg490q<)M&)M6Ie1#jVqRyWA(OM+edvJaA=?1MBc2};8q z2a=#9DBctAff+)QV2*$;AqvxZIRZjm{g~iwwXqxjo^RXf+NmF_HnN9)Vx{vgSJ5Tx zp`vcOq^O%HBnk<2aKz;*m`P&bf%&@NFSU!o?^;tP@}@da_n#k>Ss66F##4!nzr z*>PFm>zW;hBxYaCiz9;<60`&&_}-Eeh!-<~2QZPCL`=&3FJ}Uel#~f)x|ru|#eVg- zI$Ish*sqS}6@jZ3YjD+W-&qk@`Sw~Xf90;dc12*XT?6I<-u9i{I-I{-7vkf5SnFu^ zD?F0FIy@3*tG^YpzxC_H(a46;S`5XPm{B`1!=5O@t1Y8Ge*uI1Fv4%*G zU-4JRx7aVHKum#{0x<<*3d9tMDG*a2ra(-Am;x~cVhY3*_`eF=HoX59Ywy4P2m7(^ ALjV8( literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat b/release/datafiles/icons/ops.gpencil.sculpt_smooth.dat new file mode 100644 index 0000000000000000000000000000000000000000..3a132ed4049ea558e4f07d38fd5bcdbfa1d1e555 GIT binary patch literal 3788 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 Qx_@P{ur3)9D zp;V|@$aw3hRYZzlX#~f%)Ph6t1!g;2C)cMagZFaI?{Gfux%~ZG9?fE*(P+&&6G~-i zf#DtB`h5RukMHw6)%Q3MtH+klxkC(dO=+*tv0 zXPr0!Q$(+^GIfmsqD*DhMO3tkXb~+~PB#*GmQLWAbwO1xfeRGxh|^g*PM>aM>0Bte zliyA5@D7226c|yR5v>JF5iL`0`>`*|9{ZAPly%!}3`LSSMH);xJ%(dO3@35i2#FgJ z%LgN>np!b_w5`#oWsOEGFoUUig9$g1I3NJC{F_yvl*l8e#Cu1r@O`KiZUnj>$f3Ux zxbZc;H@?Q_rnl+sdSGSX9r_2}U%rR^f%jp5=!bJkU6sUkc;J|6>0DCnO$q literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.sculpt_twist.dat b/release/datafiles/icons/ops.gpencil.sculpt_twist.dat new file mode 100644 index 0000000000000000000000000000000000000000..4ce958cb7ec533e780f59bf6948453e638dce4b9 GIT binary patch literal 2942 zcmeIwZA&6+7zgk^%(4sHA_PiP>?U~99#$Pvj!qMrQ?br$uA`Rb!#IiCINLg@8N1RP zO(R@2lL~wwib{ln?ZNH13vKiw2sX~A=*+#};&2@JU%#7qcmMB8_XphZ_}I}wx|}mk zcj0`kwefYWmHhj!Z$L0h$A$Asqi|k2J}n;A4hl!LN~2o2Oq^xvsk6*!v7TxMn_NT4 zWug(iur7yS5{RN;i_VPczXo6 zJ(BFr_3?wbzFk>X>4!KdB~c zdpac6Jx4?mrBwP7mP&`j{W4&`tkWVIlVC!W5YolHpdjW2)oBsCfMSZimfrJ4~6)trXeZ z(ndPXfBBT;zpNCOHh@bT`A9aAjAs++bTXcgB;)CDf!bs;^d^&!WPY1rQbg9iYR}pW z)S5ejeV&S2K2N2HxW$JtruR5wieQU4a1kfT8S|WV#_Yp9(Cf*~RmG$f{2 zN_ej!u?p&!ofZ!Y^^`>U5{8gYiv$zDy|C414+){Ww_?}H=pnIZ=;9zo8ZYa%NA`#1 zIR&p?PxkVIF@@0Emb2s+Hqtu~*kp4|`BPH3ds?G6pr|PyNry8G>_SMeT~iForifL0 z+!D7e;t`C9;UBDFANC%naUVwGo@qMl{z$Q&X>fC=d(LO`8L!PZ6A#^2y04m6_m!rl zy3@3b*Qz_!-OxYXR}&9ozl_&ozf^a|YsWqDV7?(9CWPt#?zwgS@Z37?Z9iQ<=u7{1 kpy$^1({XRUd2D~}2HFj@8)!GsZs32`z@Lfc<9{Ll0+sVlzyJUM literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.gpencil.sculpt_weight.dat b/release/datafiles/icons/ops.gpencil.sculpt_weight.dat new file mode 100644 index 0000000000000000000000000000000000000000..41b58abfda7805e0bad986ba0c1709a0f9390838 GIT binary patch literal 1736 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 +import bpy +from bpy.types import Menu, Panel, UIList +from rna_prop_ui import PropertyPanel +from .properties_grease_pencil_common import ( + GreasePencilDataPanel, + GreasePencilOnionPanel, + ) + +############################### +# Base-Classes (for shared stuff - e.g. poll, attributes, etc.) + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + +class LayerDataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return (context.object and + context.object.type == 'GPENCIL' and + context.active_gpencil_layer) + + +############################### +# GP Object Properties Panels and Helper Classes + +class DATA_PT_gpencil(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + + # Grease Pencil data selector + gpd_owner = context.gpencil_data_owner + gpd = context.gpencil_data + + layout.template_ID(gpd_owner, "data") + + +class GPENCIL_UL_layer(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.GPencilLayer) + gpl = item + gpd = context.gpencil_data + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if gpl.lock: + layout.active = False + + row = layout.row(align=True) + if gpl.is_parented: + icon = 'BONE_DATA' + else: + icon = 'BLANK1' + + row.label(text="", icon=icon) + row.prop(gpl, "info", text="", emboss=False) + + row = layout.row(align=True) + row.prop(gpl, "lock", text="", emboss=False) + row.prop(gpl, "hide", text="", emboss=False) + row.prop(gpl, "unlock_color", text="", emboss=False) + if gpl.use_onion_skinning is False: + icon = 'GHOST_DISABLED' + else: + icon = 'GHOST_ENABLED' + subrow = row.row(align=True) + subrow.prop(gpl, "use_onion_skinning", text="", icon=icon, emboss=False) + subrow.active = gpd.use_onion_skinning + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class GPENCIL_MT_layer_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon + + layout.separator() + + layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + + layout.separator() + + layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All") + + layout.separator() + + layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down") + + +class DATA_PT_gpencil_datapanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Layers" + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + + ob = context.object + if ob is not None and ob.type == 'GPENCIL': + return True + + return False + + @staticmethod + def draw(self, context): + layout = self.layout + #layout.use_property_split = True + layout.use_property_decorate = False + + gpd = context.gpencil_data + + # Grease Pencil data... + if (gpd is None) or (not gpd.layers): + layout.operator("gpencil.layer_add", text="New Layer") + else: + self.draw_layers(context, layout, gpd) + + def draw_layers(self, context, layout, gpd): + row = layout.row() + + col = row.column() + if len(gpd.layers) >= 2: + layer_rows = 5 + else: + layer_rows = 2 + col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) + + col = row.column() + + sub = col.column(align=True) + sub.operator("gpencil.layer_add", icon='ZOOMIN', text="") + sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="") + + gpl = context.active_gpencil_layer + if gpl: + sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="") + + if len(gpd.layers) > 1: + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True + + row = layout.row(align=True) + if gpl: + row.prop(gpl, "opacity", text="Opacity", slider=True) + + +class DATA_PT_gpencil_layer_optionpanel(LayerDataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Adjustments" + bl_parent_id = 'DATA_PT_gpencil_datapanel' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + gpl = context.active_gpencil_layer + layout.active = not gpl.lock + + # Layer options + # Offsets - Color Tint + layout.enabled = not gpl.lock + col = layout.column(align=True) + col.prop(gpl, "tint_color") + col.prop(gpl, "tint_factor", slider=True) + + # Offsets - Thickness + col = layout.row(align=True) + col.prop(gpl, "line_change", text="Stroke Thickness") + + +class DATA_PT_gpencil_parentpanel(LayerDataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Relations" + bl_parent_id = 'DATA_PT_gpencil_datapanel' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + gpl = context.active_gpencil_layer + col = layout.column(align=True) + col.active = not gpl.lock + col.prop(gpl, "parent", text="Parent") + col.prop(gpl, "parent_type", text="Parent Type") + parent = gpl.parent + + if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': + col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone") + + +class DATA_PT_gpencil_onionpanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Onion Skinning" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return bool(context.active_gpencil_layer) + + @staticmethod + def draw_header(self, context): + self.layout.prop(context.gpencil_data, "use_onion_skinning", text="") + + def draw(self, context): + gpd = context.gpencil_data + + layout = self.layout + layout.use_property_split = True + layout.enabled = gpd.use_onion_skinning + + GreasePencilOnionPanel.draw_settings(layout, gpd) + + +class GPENCIL_MT_gpencil_vertex_group(Menu): + bl_label = "GP Vertex Groups" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'EXEC_AREA' + layout.operator("object.vertex_group_add") + + ob = context.active_object + if ob.vertex_groups.active: + layout.separator() + + layout.operator("gpencil.vertex_group_assign", text="Assign to Active Group") + layout.operator("gpencil.vertex_group_remove_from", text="Remove from Active Group") + + layout.separator() + layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group") + layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False + layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True + + layout.separator() + layout.operator("gpencil.vertex_group_select", text="Select Points") + layout.operator("gpencil.vertex_group_deselect", text="Deselect Points") + + +class GPENCIL_UL_vgroups(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + vgroup = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon) + # icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED' + # layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class DATA_PT_gpencil_vertexpanel(DataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Vertex Groups" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + + ob = context.object + group = ob.vertex_groups.active + + rows = 2 + if group: + rows = 4 + + row = layout.row() + row.template_list("GPENCIL_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows) + + col = row.column(align=True) + col.operator("object.vertex_group_add", icon='ZOOMIN', text="") + col.operator("object.vertex_group_remove", icon='ZOOMOUT', text="").all = False + + if ob.vertex_groups: + row = layout.row() + + sub = row.row(align=True) + sub.operator("gpencil.vertex_group_assign", text="Assign") + sub.operator("gpencil.vertex_group_remove_from", text="Remove") + + sub = row.row(align=True) + sub.operator("gpencil.vertex_group_select", text="Select") + sub.operator("gpencil.vertex_group_deselect", text="Deselect") + + layout.prop(context.tool_settings, "vertex_group_weight", text="Weight") + + +class DATA_PT_gpencil_display(DataButtonsPanel, Panel): + bl_label = "Viewport Display" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + + gpd = context.gpencil_data + gpl = context.active_gpencil_layer + + layout.prop(gpd, "xray_mode", text="Depth Ordering") + layout.prop(gpd, "edit_line_color", text="Edit Line Color") + layout.prop(ob, "empty_draw_size", text="Marker Size") + + col = layout.column(align=True) + col.prop(gpd, "show_constant_thickness") + sub = col.column() + sub.active = not gpd.show_constant_thickness + sub.prop(gpd, "pixfactor", text="Thickness Scale") + + if gpl: + layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions") + + +class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel): + _context_path = "object.data" + _property_type = bpy.types.GreasePencil + +############################### + +classes = ( + DATA_PT_gpencil, + DATA_PT_gpencil_datapanel, + DATA_PT_gpencil_onionpanel, + DATA_PT_gpencil_layer_optionpanel, + DATA_PT_gpencil_parentpanel, + DATA_PT_gpencil_vertexpanel, + DATA_PT_gpencil_display, + DATA_PT_custom_props_gpencil, + + GPENCIL_UL_layer, + GPENCIL_UL_vgroups, + + GPENCIL_MT_layer_specials, + GPENCIL_MT_gpencil_vertex_group, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 03ebea69d2b..2328925bbad 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -28,10 +28,14 @@ class ModifierButtonsPanel: bl_context = "modifier" bl_options = {'HIDE_HEADER'} - class DATA_PT_modifiers(ModifierButtonsPanel, Panel): bl_label = "Modifiers" + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type != 'GPENCIL' + def draw(self, context): layout = self.layout @@ -1563,8 +1567,447 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind") +class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): + bl_label = "Modifiers" + + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type == 'GPENCIL' + + def draw(self, context): + layout = self.layout + + ob = context.object + + layout.operator_menu_enum("object.gpencil_modifier_add", "type") + + for md in ob.grease_pencil_modifiers: + box = layout.template_greasepencil_modifier(md) + if box: + # match enum type to our functions, avoids a lookup table. + getattr(self, md.type)(box, ob, md) + + # the mt.type enum is (ab)used for a lookup on function names + # ...to avoid lengthy if statements + # so each type must have a function here. + + def GP_NOISE(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "factor") + row.prop(md, "random", text="", icon="TIME", toggle=True) + row = col.row() + row.enabled = md.random + row.prop(md, "step") + col.prop(md, "full_stroke") + col.prop(md, "move_extreme") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.label("Affect:") + row = layout.row(align=True) + row.prop(md, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(md, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(md, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + row.prop(md, "affect_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) + + def GP_SMOOTH(self, layout, ob, md): + gpd = ob.data + row = layout.row(align=False) + row.prop(md, "factor") + row.prop(md, "step") + + split = layout.split() + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col = split.column() + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.label("Affect:") + row = layout.row(align=True) + row.prop(md, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(md, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(md, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + row.prop(md, "affect_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) + + def GP_SUBDIV(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "level") + row.prop(md, "simple", text="", icon="PARTICLE_POINT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + def GP_SIMPLIFY(self, layout, ob, md): + gpd = ob.data + + row = layout.row() + row.prop(md, "mode") + + split = layout.split() + + col = split.column() + col.label("Settings:") + row = col.row(align=True) + row.enabled = md.mode == 'FIXED' + row.prop(md, "step") + + row = col.row(align=True) + row.enabled = not md.mode == 'FIXED' + row.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_THICK(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "thickness") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + col.prop(md, "normalize_thickness") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + if not md.normalize_thickness: + split = layout.split() + col = split.column() + col.prop(md, "use_custom_curve") + + if md.use_custom_curve: + col.template_curve_mapping(md, "curve") + + def GP_TINT(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.prop(md, "color") + col.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row() + row.prop(md, "create_colors") + + def GP_COLOR(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label("Color:") + col.prop(md, "hue", text="H") + col.prop(md, "saturation", text="S") + col.prop(md, "value", text="V") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row() + row.prop(md, "create_colors") + + def GP_OPACITY(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label("Opacity:") + col.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_INSTANCE(self, layout, ob, md): + gpd = ob.data + + col = layout.column() + col.prop(md, "count") + col.prop(md, "use_make_objects") + + split = layout.split() + col = split.column() + col.label("Offset:") + col.prop(md, "offset", text="") + + col = split.column() + col.label("Shift:") + col.prop(md, "shift", text="") + row = col.row(align=True) + row.prop(md, "lock_axis", expand=True) + + split = layout.split() + col = split.column() + col.label("Rotation:") + col.prop(md, "rotation", text="") + col.separator() + row = col.row(align=True) + row.prop(md, "random_rot", text="", icon="TIME", toggle=True) + row.prop(md, "rot_factor", text="") + + col = split.column() + col.label("Scale:") + col.prop(md, "scale", text="") + col.separator() + row = col.row(align=True) + row.prop(md, "random_scale", text="", icon="TIME", toggle=True) + row.prop(md, "scale_factor", text="") + + split = layout.split() + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_BUILD(self, layout, ob, md): + gpd = ob.data + + split = layout.split() + + col = split.column() + col.prop(md, "mode") + if md.mode == 'CONCURRENT': + col.prop(md, "concurrent_time_alignment") + else: + col.separator() # For spacing + col.separator() + col.separator() + + col.prop(md, "transition") + sub = col.column(align=True) + sub.prop(md, "start_delay") + sub.prop(md, "length") + + col = split.column(align=True) + col.prop(md, "use_restrict_frame_range") + sub = col.column(align=True) + sub.active = md.use_restrict_frame_range + sub.prop(md, "frame_start", text="Start") + sub.prop(md, "frame_end", text="End") + col.separator() + + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + def GP_LATTICE(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label(text="Object:") + col.prop(md, "object", text="") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + layout.separator() + layout.prop(md, "strength", slider=True) + + def GP_MIRROR(self, layout, ob, md): + gpd = ob.data + + row = layout.row(align=True) + row.prop(md, "x_axis") + row.prop(md, "y_axis") + row.prop(md, "z_axis") + + # GPXX: Not implemented yet + # layout.separator() + # layout.prop(md, "clip") + + layout.label("Layer:") + row = layout.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + layout.label(text="Object:") + layout.prop(md, "object", text="") + + + def GP_HOOK(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label(text="Object:") + col.prop(md, "object", text="") + if md.object and md.object.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "subtarget", md.object.data, "bones", text="") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + use_falloff = (md.falloff_type != 'NONE') + split = layout.split() + + layout.separator() + + row = layout.row(align=True) + if use_falloff: + row.prop(md, "falloff_radius") + row.prop(md, "strength", slider=True) + layout.prop(md, "falloff_type") + + col = layout.column() + if use_falloff: + if md.falloff_type == 'CURVE': + col.template_curve_mapping(md, "falloff_curve") + + split = layout.split() + + col = split.column() + col.prop(md, "use_falloff_uniform") + + + def GP_OFFSET(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.prop(md, "location") + col.prop(md, "scale") + + col = split.column() + col.prop(md, "rotation") + + + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + classes = ( DATA_PT_modifiers, + DATA_PT_gpencil_modifiers, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_data_shaderfx.py b/release/scripts/startup/bl_ui/properties_data_shaderfx.py new file mode 100644 index 00000000000..5010f56d234 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_shaderfx.py @@ -0,0 +1,134 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# +import bpy +from bpy.types import Panel +from bpy.app.translations import pgettext_iface as iface_ + + +class ShaderFxButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "shaderfx" + bl_options = {'HIDE_HEADER'} + +class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): + bl_label = "Effects" + + @classmethod + def poll(cls, context): + return True + ob = context.object + return ob and ob.type == 'GPENCIL' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + + layout.operator_menu_enum("object.shaderfx_add", "type") + + for fx in ob.shader_effects: + box = layout.template_shaderfx(fx) + if box: + # match enum type to our functions, avoids a lookup table. + getattr(self, fx.type)(box, fx) + + # the mt.type enum is (ab)used for a lookup on function names + # ...to avoid lengthy if statements + # so each type must have a function here. + + def FX_BLUR(self, layout, fx): + + layout.prop(fx, "factor", text="Factor") + layout.prop(fx, "samples", text="Samples") + + layout.separator() + layout.prop(fx, "use_dof_mode") + if fx.use_dof_mode: + layout.prop(fx, "coc") + + def FX_COLORIZE(self, layout, fx): + layout.prop(fx, "mode", text="Mode") + + if fx.mode == 'BITONE': + layout.prop(fx, "low_color", text="Low Color") + if fx.mode == 'CUSTOM': + layout.prop(fx, "low_color", text="Color") + + if fx.mode == 'BITONE': + layout.prop(fx, "high_color", text="High Color") + + if fx.mode in {'BITONE', 'CUSTOM', 'TRANSPARENT'}: + layout.prop(fx, "factor") + + def FX_WAVE(self, layout,fx): + layout.prop(fx, "orientation", expand=True) + + layout.separator() + layout.prop(fx, "amplitude") + layout.prop(fx, "period") + layout.prop(fx, "phase") + + def FX_PIXEL(self, layout, fx): + layout.prop(fx, "size", text="Size") + + layout.prop(fx, "use_lines", text="Display Lines") + + col = layout.column() + col.enabled = fx.use_lines + col.prop(fx, "color") + + def FX_RIM(self, layout, fx): + layout.prop(fx, "offset", text="Offset") + + layout.prop(fx, "rim_color") + layout.prop(fx, "mask_color") + layout.prop(fx, "mode") + layout.prop(fx, "blur") + layout.prop(fx, "samples") + + def FX_SWIRL(self, layout, fx): + layout.prop(fx, "object", text="Object") + + layout.prop(fx, "radius") + layout.prop(fx, "angle") + + layout.prop(fx, "transparent") + + def FX_FLIP(self, layout, fx): + layout.prop(fx, "flip_horizontal") + layout.prop(fx, "flip_vertical") + + def FX_LIGHT(self, layout, fx): + layout.prop(fx, "object", text="Object") + + layout.prop(fx, "energy") + layout.prop(fx, "ambient") + + +classes = ( + DATA_PT_shader_fx, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 55b798d103a..252f87d369f 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -18,45 +18,30 @@ # - +import bpy from bpy.types import Menu, UIList from bpy.app.translations import pgettext_iface as iface_ def gpencil_stroke_placement_settings(context, layout): if context.space_data.type == 'VIEW_3D': - propname = "gpencil_stroke_placement_view3d" + propname = "annotation_stroke_placement_view3d" elif context.space_data.type == 'SEQUENCE_EDITOR': - propname = "gpencil_stroke_placement_sequencer_preview" + propname = "annotation_stroke_placement_sequencer_preview" elif context.space_data.type == 'IMAGE_EDITOR': - propname = "gpencil_stroke_placement_image_editor" + propname = "annotation_stroke_placement_image_editor" else: - propname = "gpencil_stroke_placement_view2d" + propname = "annotation_stroke_placement_view2d" ts = context.tool_settings col = layout.column(align=True) - col.label(text="Stroke Placement:") - - row = col.row(align=True) - row.prop_enum(ts, propname, 'VIEW') - row.prop_enum(ts, propname, 'CURSOR') - - if context.space_data.type == 'VIEW_3D': + if context.space_data.type != 'VIEW_3D': + col.label(text="Stroke Placement:") row = col.row(align=True) - row.prop_enum(ts, propname, 'SURFACE') - row.prop_enum(ts, propname, 'STROKE') - - row = col.row(align=False) - row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'} - row.prop(ts, "use_gpencil_stroke_endpoints") - - if context.scene.tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': - row = col.row(align=True) - row.label("Lock axis:") - row = col.row(align=True) - row.prop(ts.gpencil_sculpt, "lockaxis", expand=True) + row.prop_enum(ts, propname, 'VIEW') + row.prop_enum(ts, propname, 'CURSOR', text="Cursor") def gpencil_active_brush_settings_simple(context, layout): @@ -73,7 +58,7 @@ def gpencil_active_brush_settings_simple(context, layout): row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA') row.prop(brush, "name", text="") - col.prop(brush, "line_width", slider=True) + col.prop(brush, "size", slider=True) row = col.row(align=True) row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE') row.prop(brush, "pen_sensitivity_factor", slider=True) @@ -90,6 +75,7 @@ def gpencil_active_brush_settings_simple(context, layout): row.prop(brush, "angle_factor", text="Factor", slider=True) +# XXX: To be replaced with active tools class GreasePencilDrawingToolsPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' @@ -99,8 +85,7 @@ class GreasePencilDrawingToolsPanel: @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False + return True @staticmethod def draw(self, context): @@ -113,12 +98,12 @@ class GreasePencilDrawingToolsPanel: col.label(text="Draw:") row = col.row(align=True) - row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW' - row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon + row.operator("gpencil.annotate", icon='GREASEPENCIL', text="Draw").mode = 'DRAW' + row.operator("gpencil.annotate", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon row = col.row(align=True) - row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT' - row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY' + row.operator("gpencil.annotate", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT' + row.operator("gpencil.annotate", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY' col.separator() @@ -126,15 +111,15 @@ class GreasePencilDrawingToolsPanel: sub.operator("gpencil.blank_frame_add", icon='NEW') sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)") - sub = col.column(align=True) - sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") - sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") - sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back") + #sub = col.column(align=True) + #sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") + #sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") + #sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back") col.separator() col.separator() - if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}: + if context.space_data.type in {'CLIP_EDITOR'}: col.separator() col.label("Data Source:") row = col.row(align=True) @@ -143,8 +128,8 @@ class GreasePencilDrawingToolsPanel: elif is_clip_editor: row.prop(context.space_data, "grease_pencil_source", expand=True) - col.separator() - col.separator() + #col.separator() + #col.separator() gpencil_stroke_placement_settings(context, col) @@ -157,28 +142,16 @@ class GreasePencilDrawingToolsPanel: col = layout.column(align=True) col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True) - if is_3d_view: - col.separator() - col.separator() - - col.label(text="Tools:") - col.operator_menu_enum("gpencil.convert", text="Convert to Geometry...", property="type") - col.operator("view3d.ruler") - class GreasePencilStrokeEditPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' bl_label = "Edit Strokes" - bl_category = "Grease Pencil" + bl_category = "Tools" bl_region_type = 'TOOLS' - bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - if context.gpencil_data is None: return False @@ -204,7 +177,7 @@ class GreasePencilStrokeEditPanel: col.operator("gpencil.select_linked") col.operator("gpencil.select_more") col.operator("gpencil.select_less") - col.operator("gpencil.palettecolor_select") + col.operator("gpencil.select_alternate") layout.label(text="Edit:") row = layout.row(align=True) @@ -228,258 +201,124 @@ class GreasePencilStrokeEditPanel: layout.separator() - col = layout.column(align=True) - col.operator("transform.bend", text="Bend") - col.operator("transform.mirror", text="Mirror") - col.operator("transform.shear", text="Shear") - col.operator("transform.tosphere", text="To Sphere") - layout.separator() col = layout.column(align=True) col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") col.operator("gpencil.stroke_change_color", text="Move to Color") - if is_3d_view: - layout.separator() - layout.separator() col = layout.column(align=True) col.operator("gpencil.stroke_subdivide", text="Subdivide") - col.operator("gpencil.stroke_join", text="Join").type = 'JOIN' - col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' - col.operator("gpencil.stroke_flip", text="Flip Direction") + row = col.row(align=True) + row.operator("gpencil.stroke_simplify_fixed", text="Simplify") + row.operator("gpencil.stroke_simplify", text="Adaptative") - gpd = context.gpencil_data - if gpd: - col.prop(gpd, "show_stroke_direction", text="Show Directions") + col.separator() + + row = col.row(align=True) + row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' + row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY' + + col.operator("gpencil.stroke_flip", text="Flip Direction") if is_3d_view: layout.separator() - layout.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") + col = layout.column(align=True) + col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode") + col.operator("gpencil.stroke_split", text="Split") -class GreasePencilInterpolatePanel: - bl_space_type = 'VIEW_3D' - bl_label = "Interpolate" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - elif context.space_data.type != 'VIEW_3D': - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) - - @staticmethod - def draw(self, context): - layout = self.layout - settings = context.tool_settings.gpencil_interpolate - - col = layout.column(align=True) - col.operator("gpencil.interpolate", text="Interpolate") - col.operator("gpencil.interpolate_sequence", text="Sequence") - col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns") - - col = layout.column(align=True) - col.label(text="Options:") - col.prop(settings, "interpolate_all_layers") - col.prop(settings, "interpolate_selected_only") - - col = layout.column(align=True) - col.label(text="Sequence Options:") - col.prop(settings, "type") - if settings.type == 'CUSTOM': - box = layout.box() - # TODO: Options for loading/saving curve presets? - box.template_curve_mapping(settings, "interpolation_curve", brush=True) - elif settings.type != 'LINEAR': - col.prop(settings, "easing") - - if settings.type == 'BACK': - layout.prop(settings, "back") - elif setting.type == 'ELASTIC': - sub = layout.column(align=True) - sub.prop(settings, "amplitude") - sub.prop(settings, "period") - - -class GreasePencilBrushPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Drawing Brushes" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - @staticmethod - def draw(self, context): - layout = self.layout - - row = layout.row() - col = row.column() - ts = context.scene.tool_settings - if len(ts.gpencil_brushes) >= 2: - brows = 3 - else: - brows = 2 - col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows) - - col = row.column() - - sub = col.column(align=True) - sub.operator("gpencil.brush_add", icon='ZOOMIN', text="") - sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="") - sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="") - brush = context.active_gpencil_brush - if brush: - if len(ts.gpencil_brushes) > 1: - col.separator() - sub = col.column(align=True) - sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP' - sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN' - - # Brush details - if brush is not None: - row = layout.row() - row.prop(brush, "line_width") - row = layout.row(align=True) - row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE') - row.prop(brush, "pen_sensitivity_factor", slider=True) - row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row(align=True) - row.prop(brush, "use_random_strength", text="", icon='RNDCURVE') - row.prop(brush, "strength", slider=True) - row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row(align=True) - row.prop(brush, "random_press", slider=True) - - row = layout.row(align=True) - row.prop(brush, "jitter", slider=True) - row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row() - row.prop(brush, "angle", slider=True) - row.prop(brush, "angle_factor", text="Factor", slider=True) - - box = layout.box() - col = box.column(align=True) - col.label(text="Stroke Quality:") - col.prop(brush, "pen_smooth_factor") - col.prop(brush, "pen_smooth_steps") - col.separator() - row = col.row(align=False) - row.prop(brush, "pen_subdivision_steps") - row.prop(brush, "random_subdiv", text="Randomness", slider=True) + col = layout.column(align=True) + col.label(text="Cleanup:") + col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") + col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") class GreasePencilStrokeSculptPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' bl_label = "Sculpt Strokes" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) + bl_category = "Tools" @staticmethod def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False settings = context.tool_settings.gpencil_sculpt tool = settings.tool brush = settings.brush - layout.column().prop(settings, "tool") + layout.template_icon_view(settings, "tool", show_labels=True) - col = layout.column() - col.prop(brush, "size", slider=True) - row = col.row(align=True) + layout.prop(brush, "size", slider=True) + row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - col.prop(brush, "use_falloff") + + layout.prop(brush, "use_falloff") + if tool in {'SMOOTH', 'RANDOMIZE'}: - row = layout.row(align=True) - row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True) - row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True) - row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + layout.prop(settings, "affect_position", text="Affect Position") + layout.prop(settings, "affect_strength", text="Affect Strength") + layout.prop(settings, "affect_thickness", text="Affect Thickness") - layout.separator() + if tool == 'SMOOTH': + layout.prop(brush, "affect_pressure") - if tool == 'THICKNESS': - layout.row().prop(brush, "direction", expand=True) - elif tool == 'PINCH': - row = layout.row(align=True) - row.prop_enum(brush, "direction", 'ADD', text="Pinch") - row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate") - elif settings.tool == 'TWIST': - row = layout.row(align=True) - row.prop_enum(brush, "direction", 'SUBTRACT', text="CW") - row.prop_enum(brush, "direction", 'ADD', text="CCW") + layout.prop(settings, "affect_uv", text="Affect UV") - row = layout.row(align=True) - row.prop(settings, "use_select_mask") - row = layout.row(align=True) - row.prop(settings, "selection_alpha", slider=True) - - if tool == 'SMOOTH': - layout.prop(brush, "affect_pressure") + if tool in {'THICKNESS', 'PINCH', 'TWIST'}: + layout.prop(brush, "direction", expand=True) -class GreasePencilBrushCurvesPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Brush Curves" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' +# GP Object Tool Settings +class GreasePencilAppearancePanel: + bl_label = "Brush Appearance" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.active_gpencil_brush is None: - return False - - brush = context.active_gpencil_brush - return bool(brush) + ob = context.active_object + return ob and ob.type == 'GPENCIL' @staticmethod def draw(self, context): layout = self.layout - brush = context.active_gpencil_brush - # Brush - layout.label("Sensitivity") - box = layout.box() - box.template_curve_mapping(brush, "curve_sensitivity", brush=True) + layout.use_property_split = True + layout.use_property_decorate = False - layout.label("Strength") - box = layout.box() - box.template_curve_mapping(brush, "curve_strength", brush=True) + ob = context.active_object - layout.label("Jitter") - box = layout.box() - box.template_curve_mapping(brush, "curve_jitter", brush=True) + if ob.mode == 'GPENCIL_PAINT': + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + layout.prop(gp_settings, "gpencil_brush_type", text="Brush Type") + + sub = layout.column(align=True) + sub.enabled = not brush.use_custom_icon + sub.prop(gp_settings, "gp_icon", text="Icon") + + layout.prop(brush, "use_custom_icon") + sub = layout.column() + sub.active = brush.use_custom_icon + sub.prop(brush, "icon_filepath", text="") + + layout.prop(gp_settings, "use_cursor", text="Show Brush") + + if gp_settings.gpencil_brush_type == 'FILL': + layout.prop(brush, "cursor_color_add", text="Color") + + elif ob.mode in ('GPENCIL_SCULPT', 'GPENCIL_WEIGHT'): + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + col = layout.column(align=True) + col.prop(brush, "use_cursor", text="Show Brush") + col.row().prop(brush, "cursor_color_add", text="Add") + col.row().prop(brush, "cursor_color_sub", text="Subtract") ############################### @@ -539,6 +378,7 @@ class GPENCIL_MT_pie_tool_palette(Menu): col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT') col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY') col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO') + col.operator("gpencil.select_alternate", text="Alternate Select", icon='BORDER_LASSO') # SW - Edit Tools col = pie.column() @@ -566,7 +406,7 @@ class GPENCIL_MT_pie_settings_palette(Menu): pie = layout.menu_pie() gpd = context.gpencil_data gpl = context.active_gpencil_layer - palcolor = context.active_gpencil_palettecolor + palcolor = None #context.active_gpencil_palettecolor brush = context.active_gpencil_brush is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes) @@ -737,6 +577,16 @@ class GPENCIL_MT_snap(Menu): layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid") +class GPENCIL_MT_separate(Menu): + bl_label = "Separate" + + def draw(self, context): + layout = self.layout + layout.operator("gpencil.stroke_separate", text="Selected Points").mode = 'POINT' + layout.operator("gpencil.stroke_separate", text="Selected Strokes").mode = 'STROKE' + layout.operator("gpencil.stroke_separate", text="Active Layer").mode = 'LAYER' + + class GPENCIL_MT_gpencil_edit_specials(Menu): bl_label = "GPencil Specials" @@ -747,6 +597,14 @@ class GPENCIL_MT_gpencil_edit_specials(Menu): layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.operator("gpencil.stroke_simplify_fixed", text="Simplify") + layout.operator("gpencil.stroke_simplify", text="Simplify Adaptative") + + layout.separator() + layout.menu("GPENCIL_MT_separate", text="Separate") + + layout.separator() + layout.operator("gpencil.stroke_split", text="Split") layout.separator() @@ -754,167 +612,129 @@ class GPENCIL_MT_gpencil_edit_specials(Menu): layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' layout.operator("gpencil.stroke_flip", text="Flip Direction") + layout.separator() + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' + if is_3d_view: layout.separator() layout.operator("gpencil.reproject") -############################### +class GPENCIL_MT_gpencil_sculpt_specials(Menu): + bl_label = "GPencil Specials" + + def draw(self, context): + layout = self.layout + is_3d_view = context.space_data.type == 'VIEW_3D' + + layout.operator_context = 'INVOKE_REGION_WIN' + + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' + + layout.separator() + + layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.operator("gpencil.stroke_simplify_fixed", text="Simplify") + layout.operator("gpencil.stroke_simplify", text="Simplify Adaptative") -class GPENCIL_UL_brush(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - # assert(isinstance(item, bpy.types.GPencilBrush) - brush = item +class GPENCIL_MT_gpencil_draw_specials(Menu): + bl_label = "GPencil Draw Specials" - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA') - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) + def draw(self, context): + layout = self.layout + is_3d_view = context.space_data.type == 'VIEW_3D' + + layout.operator_context = 'INVOKE_REGION_WIN' + + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' + + layout.separator() + layout.operator("gpencil.primitive", text="Line", icon='IPO_CONSTANT').type = 'LINE' + layout.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX' + layout.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE' + + # colors + layout.separator() + layout.operator("gpencil.colorpick", text="Colors", icon="GROUP_VCOL") -class GPENCIL_UL_palettecolor(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - # assert(isinstance(item, bpy.types.PaletteColor) - palcolor = item +class GPENCIL_MT_gpencil_draw_delete(Menu): + bl_label = "GPencil Draw Delete" - if self.layout_type in {'DEFAULT', 'COMPACT'}: - if palcolor.lock: - layout.active = False + def draw(self, context): + layout = self.layout + is_3d_view = context.space_data.type == 'VIEW_3D' - split = layout.split(percentage=0.25) - row = split.row(align=True) - row.enabled = not palcolor.lock - row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible) - row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible) - split.prop(palcolor, "name", text="", emboss=False) + layout.operator_context = 'INVOKE_REGION_WIN' - row = layout.row(align=True) - row.prop(palcolor, "lock", text="", emboss=False) - row.prop(palcolor, "hide", text="", emboss=False) - if palcolor.ghost is True: - icon = 'GHOST_DISABLED' - else: - icon = 'GHOST_ENABLED' - row.prop(palcolor, "ghost", text="", icon=icon, emboss=False) - - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) + layout.operator("gpencil.active_frames_delete_all", text="Delete Frame") -class GPENCIL_UL_layer(UIList): +class GPENCIL_UL_annotation_layer(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): # assert(isinstance(item, bpy.types.GPencilLayer) gpl = item + gpd = context.gpencil_data if self.layout_type in {'DEFAULT', 'COMPACT'}: if gpl.lock: layout.active = False - row = layout.row(align=True) - if gpl.is_parented: - icon = 'BONE_DATA' - else: - icon = 'BLANK1' - - row.label(text="", icon=icon) - row.prop(gpl, "info", text="", emboss=False) + split = layout.split(percentage=0.2) + split.prop(gpl, "color", text="", emboss=True) + split.prop(gpl, "info", text="", emboss=False) row = layout.row(align=True) - row.prop(gpl, "lock", text="", emboss=False) + # row.prop(gpl, "lock", text="", emboss=False) row.prop(gpl, "hide", text="", emboss=False) - row.prop(gpl, "unlock_color", text="", emboss=False) elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) -class GPENCIL_MT_layer_specials(Menu): - bl_label = "Layer" - - def draw(self, context): - layout = self.layout - - layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon - - layout.separator() - - layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All") - layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True - - layout.separator() - - layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") - layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All") - - layout.separator() - - layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down") - - -class GPENCIL_MT_brush_specials(Menu): - bl_label = "Layer" - - def draw(self, context): - layout = self.layout - layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy Current Drawing Brush") - layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a Set of Predefined Brushes") - - -class GPENCIL_MT_palettecolor_specials(Menu): - bl_label = "Layer" - - def draw(self, context): - layout = self.layout - - layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") - layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True - - layout.separator() - - layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All") - layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All") - layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color") - - layout.separator() - - layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes") - layout.operator("gpencil.stroke_change_color", icon='MAN_TRANS', text="Move to Color") - - class GreasePencilDataPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Grease Pencil Layers" + bl_label = "Annotations" bl_region_type = 'UI' + @classmethod + def poll(cls, context): + # Show this panel as long as someone that might own this exists + # AND the owner isn't an object (e.g. GP Object) + if context.gpencil_data_owner is None: + return False + elif type(context.gpencil_data_owner) is bpy.types.Object: + return False + else: + return True + @staticmethod def draw_header(self, context): - self.layout.prop(context.space_data, "show_grease_pencil", text="") + if context.space_data.type != 'VIEW_3D': + self.layout.prop(context.space_data, "show_annotation", text="") @staticmethod def draw(self, context): layout = self.layout + #layout.use_property_split = True + layout.use_property_decorate = False # owner of Grease Pencil data gpd_owner = context.gpencil_data_owner gpd = context.gpencil_data # Owner Selector - if context.space_data.type == 'VIEW_3D': - layout.row().prop(context.tool_settings, "grease_pencil_source", expand=True) - elif context.space_data.type == 'CLIP_EDITOR': + if context.space_data.type == 'CLIP_EDITOR': layout.row().prop(context.space_data, "grease_pencil_source", expand=True) - # Grease Pencil data selector layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink") # Grease Pencil data... if (gpd is None) or (not gpd.layers): - layout.operator("gpencil.layer_add", text="New Layer") + layout.operator("gpencil.layer_add", text="New Note") else: self.draw_layers(context, layout, gpd) @@ -926,7 +746,7 @@ class GreasePencilDataPanel: layer_rows = 5 else: layer_rows = 2 - col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) + col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) col = row.column() @@ -936,8 +756,6 @@ class GreasePencilDataPanel: gpl = context.active_gpencil_layer if gpl: - sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="") - if len(gpd.layers) > 1: col.separator() @@ -945,203 +763,70 @@ class GreasePencilDataPanel: sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' - col.separator() - - sub = col.column(align=True) - sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False - sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True - if gpl: - self.draw_layer(context, layout, gpl) + # layout.prop(gpl, "opacity", text="Opacity", slider=True) + # layout.prop(gpl, "thickness", text="Thickness") + # + # layout.separator() - def draw_layer(self, context, layout, gpl): - row = layout.row(align=True) - row.prop(gpl, "opacity", text="Opacity", slider=True) + # Full-Row - Frame Locking (and Delete Frame) + row = layout.row(align=True) + row.active = not gpl.lock - # Layer options - split = layout.split(percentage=0.5) - split.active = not gpl.lock - split.prop(gpl, "show_x_ray") - split.prop(gpl, "show_points") + if gpl.active_frame: + lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked") + lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status) + else: + lock_label = iface_("Lock Frame") + row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED') + row.operator("gpencil.active_frame_delete", text="", icon='X') - # Offsets + Parenting (where available) - if context.space_data.type == 'VIEW_3D': - split = layout.split(percentage=0.5) - else: - split = layout.column() # parenting is not available in 2D editors... - split.active = not gpl.lock - # Offsets - Color Tint - col = split.column() - subcol = col.column(align=True) - subcol.label("Tint") - subcol.enabled = not gpl.lock - subcol.prop(gpl, "tint_color", text="") - subcol.prop(gpl, "tint_factor", text="Factor", slider=True) - # Offsets - Thickness - row = col.row(align=True) - row.prop(gpl, "line_change", text="Thickness Change", slider=True) - row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="") +class GreasePencilOnionPanel: + @staticmethod + def draw_settings(layout, gp): + col = layout.column() - # Parenting - if context.space_data.type == 'VIEW_3D': - col = split.column(align=True) - col.label(text="Parent:") - col.prop(gpl, "parent", text="") - - sub = col.column() - sub.prop(gpl, "parent_type", text="") - parent = gpl.parent - if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': - sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="") - - layout.separator() - - # Full-Row - Frame Locking (and Delete Frame) - row = layout.row(align=True) - row.active = not gpl.lock - - if gpl.active_frame: - lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked") - lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status) - else: - lock_label = iface_("Lock Frame") - row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED') - row.operator("gpencil.active_frame_delete", text="", icon='X') - - layout.separator() - - # Onion skinning - col = layout.column(align=True) - col.active = not gpl.lock + col.prop(gp, "onion_mode") row = col.row() - row.prop(gpl, "use_onion_skinning") - sub = row.row(align=True) - icon = 'RESTRICT_RENDER_OFF' if gpl.use_ghosts_always else 'RESTRICT_RENDER_ON' - sub.prop(gpl, "use_ghosts_always", text="", icon=icon) - sub.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR') - - split = col.split(percentage=0.5) - split.active = gpl.use_onion_skinning + row.prop(gp, "onion_factor", text="Opacity", slider=True) # - Before Frames - sub = split.column(align=True) + sub = layout.column(align=True) row = sub.row(align=True) - row.active = gpl.use_ghost_custom_colors - row.prop(gpl, "before_color", text="") - sub.prop(gpl, "ghost_before_range", text="Before") + row.active = gp.use_ghost_custom_colors + row.prop(gp, "before_color", text="Color Before") + + row = sub.row(align=True) + row.active = gp.onion_mode in ('ABSOLUTE', 'RELATIVE') + row.prop(gp, "ghost_before_range", text="Frames Before") # - After Frames - sub = split.column(align=True) + sub = layout.column(align=True) row = sub.row(align=True) - row.active = gpl.use_ghost_custom_colors - row.prop(gpl, "after_color", text="") - sub.prop(gpl, "ghost_after_range", text="After") + row.active = gp.use_ghost_custom_colors + row.prop(gp, "after_color", text="Color After") + + row = sub.row(align=True) + row.active = gp.onion_mode in ('ABSOLUTE', 'RELATIVE') + row.prop(gp, "ghost_after_range", text="Frames After") + + layout.prop(gp, "use_ghost_custom_colors", text="Use Custom Color") + layout.prop(gp, "use_ghosts_always", text="View In Render") + + # - fade and loop + row = layout.row() + row.active = gp.use_onion_skinning + row.prop(gp, "use_onion_fade", text="Fade") + if hasattr(gp, "use_onion_loop"): # XXX + subrow = layout.row() + subrow.active = gp.onion_mode in ('RELATIVE', 'SELECTED') + subrow.prop(gp, "use_onion_loop", text="Loop") -class GreasePencilPaletteColorPanel: - # subclass must set - bl_label = "Grease Pencil Colors" - bl_region_type = 'UI' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(gpd.layers.active) - - @staticmethod - def draw(self, context): - layout = self.layout - palette = context.active_gpencil_palette - - if palette: - row = layout.row(align=True) - row.operator_context = 'EXEC_REGION_WIN' - row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR') - row.prop(palette, "name", text="") - row.operator("gpencil.palette_add", icon='ZOOMIN', text="") - row.operator("gpencil.palette_remove", icon='X', text="") - - # Palette colors - row = layout.row() - col = row.column() - if len(palette.colors) >= 2: - color_rows = 5 - else: - color_rows = 2 - col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index", - rows=color_rows) - - col = row.column() - - sub = col.column(align=True) - sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="") - sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="") - - palcol = context.active_gpencil_palettecolor - if palcol: - sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="") - - if len(palette.colors) > 1: - col.separator() - - sub = col.column(align=True) - sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP' - sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN' - - row = layout.row() - sub = row.row(align=True) - sub.label(text="Isolate:") # based on active color only - sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False - sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True - sub = row.row(align=True) - sub.label(text="Lock:") # based on other stuff... - sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="") - sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="") - - pcolor = palette.colors.active - if pcolor: - self.draw_palettecolors(layout, pcolor) - - # Draw palette colors - def draw_palettecolors(self, layout, pcolor): - # color settings - split = layout.split(percentage=0.5) - split.active = not pcolor.lock - - # Column 1 - Stroke - col = split.column(align=True) - col.enabled = not pcolor.lock - col.label(text="Stroke:") - col.prop(pcolor, "color", text="") - col.prop(pcolor, "alpha", slider=True) - - # Column 2 - Fill - col = split.column(align=True) - col.enabled = not pcolor.lock - col.label(text="Fill:") - col.prop(pcolor, "fill_color", text="") - col.prop(pcolor, "fill_alpha", text="Opacity", slider=True) - - # Options - split = layout.split(percentage=0.5) - split.active = not pcolor.lock - - col = split.column(align=True) - col.active = not pcolor.lock - col.prop(pcolor, "use_volumetric_strokes") - col = split.column(align=True) - col.active = not pcolor.lock - col.prop(pcolor, "use_hq_fill") - +############################### class GreasePencilToolsPanel: # For use in "2D" Editors without their own toolbar @@ -1150,6 +835,7 @@ class GreasePencilToolsPanel: # bl_options = {'DEFAULT_CLOSED'} bl_label = "Grease Pencil Settings" bl_region_type = 'UI' + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -1183,20 +869,23 @@ class GreasePencilToolsPanel: gpencil_stroke_placement_settings(context, layout) +############################### classes = ( GPENCIL_MT_pie_tool_palette, GPENCIL_MT_pie_settings_palette, GPENCIL_MT_pie_tools_more, GPENCIL_MT_pie_sculpt, + GPENCIL_MT_snap, + GPENCIL_MT_separate, + GPENCIL_MT_gpencil_edit_specials, - GPENCIL_UL_brush, - GPENCIL_UL_palettecolor, - GPENCIL_UL_layer, - GPENCIL_MT_layer_specials, - GPENCIL_MT_brush_specials, - GPENCIL_MT_palettecolor_specials, + GPENCIL_MT_gpencil_sculpt_specials, + GPENCIL_MT_gpencil_draw_specials, + GPENCIL_MT_gpencil_draw_delete, + + GPENCIL_UL_annotation_layer, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 0e3e50b3497..5d12f762073 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -86,8 +86,11 @@ class EEVEE_MATERIAL_PT_context_material(MaterialButtonsPanel, Panel): @classmethod def poll(cls, context): - engine = context.engine - return (context.material or context.object) and (engine in cls.COMPAT_ENGINES) + if context.active_object and context.active_object.type == 'GPENCIL': + return False + else: + engine = context.engine + return (context.material or context.object) and (engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py new file mode 100644 index 00000000000..2d823594547 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -0,0 +1,322 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# +import bpy +from bpy.types import Menu, Panel, UIList +from rna_prop_ui import PropertyPanel + + +class GPENCIL_MT_color_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.color_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.color_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + + layout.separator() + + layout.operator("gpencil.color_lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.color_unlock_all", icon='UNLOCKED', text="UnLock All") + + layout.separator() + + layout.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="Lock Unselected") + layout.operator("gpencil.lock_layer", icon='COLOR', text="Lock Unused") + + +class GPENCIL_UL_matslots(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + slot = item + ma = slot.material + if (ma is not None) and (ma.grease_pencil is not None): + gpcolor = ma.grease_pencil + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if gpcolor.lock: + layout.active = False + + row = layout.row(align=True) + row.enabled = not gpcolor.lock + row.prop(ma, "name", text="", emboss=False, icon_value=icon) + + row = layout.row(align=True) + row.prop(gpcolor, "lock", text="", emboss=False) + row.prop(gpcolor, "hide", text="", emboss=False) + if gpcolor.ghost is True: + icon = 'GHOST_DISABLED' + else: + icon = 'GHOST_ENABLED' + row.prop(gpcolor, "ghost", text="", icon=icon, emboss=False) + + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class GPMaterialButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + + @classmethod + def poll(cls, context): + ob = context.object + return (ob and ob.type == 'GPENCIL' and + ob.active_material and + ob.active_material.grease_pencil) + + + +class MATERIAL_PT_gpencil_slots(Panel): + bl_label = "Grease Pencil Material Slots" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + bl_options = {'HIDE_HEADER'} + + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + gpd = context.gpencil_data + + mat = context.object.active_material + ob = context.object + slot = context.material_slot + space = context.space_data + + if ob: + is_sortable = len(ob.material_slots) > 1 + rows = 1 + if (is_sortable): + rows = 4 + + row = layout.row() + + row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows) + + col = row.column(align=True) + col.operator("object.material_slot_add", icon='ZOOMIN', text="") + col.operator("object.material_slot_remove", icon='ZOOMOUT', text="") + + col.menu("GPENCIL_MT_color_specials", icon='DOWNARROW_HLT', text="") + + if is_sortable: + col.separator() + + col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP' + col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True + + row = layout.row() + + if ob: + row.template_ID(ob, "active_material", new="material.new", live_icon=True) + + if slot: + icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA' + row.prop(slot, "link", icon=icon_link, icon_only=True) + + if gpd.use_stroke_edit_mode: + row = layout.row(align=True) + row.operator("gpencil.stroke_change_color", text="Assign") + row.operator("gpencil.color_select", text="Select") + + elif mat: + row.template_ID(space, "pin_id") + + +# Used as parent for "Stroke" and "Fill" panels +class MATERIAL_PT_gpencil_surface(GPMaterialButtonsPanel, Panel): + bl_label = "Surface" + + @classmethod + def poll(cls, context): + ob = context.object + ma = context.object.active_material + if ma is None or ma.grease_pencil is None: + return False + + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + +class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel): + bl_label = "Stroke" + bl_parent_id = 'MATERIAL_PT_gpencil_surface' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil is not None: + gpcolor = ma.grease_pencil + + col = layout.column() + col.active = not gpcolor.lock + + col.prop(gpcolor, "mode") + + col.prop(gpcolor, "stroke_style", text="Style") + + if gpcolor.stroke_style == 'TEXTURE': + row = col.row() + row.enabled = not gpcolor.lock + col = row.column(align=True) + col.template_ID(gpcolor, "stroke_image", open="image.open") + col.prop(gpcolor, "pixel_size", text="UV Factor") + col.prop(gpcolor, "use_stroke_pattern", text="Use As Pattern") + + if gpcolor.stroke_style == 'SOLID' or gpcolor.use_stroke_pattern is True: + col.prop(gpcolor, "color", text="Color") + + +class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel): + bl_label = "Fill" + bl_parent_id = 'MATERIAL_PT_gpencil_surface' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil: + gpcolor = ma.grease_pencil + + # color settings + col = layout.column() + col.active = not gpcolor.lock + col.prop(gpcolor, "fill_style", text="Style") + + if gpcolor.fill_style == 'GRADIENT': + col.prop(gpcolor, "gradient_type") + + if gpcolor.fill_style != 'TEXTURE': + col.prop(gpcolor, "fill_color", text="Color") + + if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'): + col.prop(gpcolor, "mix_color", text="Secondary Color") + + if gpcolor.fill_style == 'GRADIENT': + col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True) + + if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'): + col.prop(gpcolor, "flip", text="Flip Colors") + + col.prop(gpcolor, "pattern_shift", text="Location") + col.prop(gpcolor, "pattern_scale", text="Scale") + + if gpcolor.gradient_type == 'RADIAL' and gpcolor.fill_style not in ('SOLID', 'CHESSBOARD'): + col.prop(gpcolor, "pattern_radius", text="Radius") + else: + if gpcolor.fill_style != 'SOLID': + col.prop(gpcolor, "pattern_angle", text="Angle") + + if gpcolor.fill_style == 'CHESSBOARD': + col.prop(gpcolor, "pattern_gridsize", text="Box Size") + + # Texture + if gpcolor.fill_style == 'TEXTURE' or (gpcolor.texture_mix is True and gpcolor.fill_style == 'SOLID'): + col.template_ID(gpcolor, "fill_image", open="image.open") + + if gpcolor.fill_style == 'TEXTURE': + col.prop(gpcolor, "use_fill_pattern", text="Use As Pattern") + if gpcolor.use_fill_pattern is True: + col.prop(gpcolor, "fill_color", text="Color") + + col.prop(gpcolor, "texture_offset", text="Offset") + col.prop(gpcolor, "texture_scale", text="Scale") + col.prop(gpcolor, "texture_angle") + col.prop(gpcolor, "texture_opacity") + col.prop(gpcolor, "texture_clamp", text="Clip Image") + + if gpcolor.use_fill_pattern is False: + col.prop(gpcolor, "texture_mix", text="Mix With Color") + + if gpcolor.texture_mix is True: + col.prop(gpcolor, "fill_color", text="Mix Color") + col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True) + + +class MATERIAL_PT_gpencil_preview(GPMaterialButtonsPanel, Panel): + bl_label = "Preview" + COMPAT_ENGINES = {'BLENDER_EEVEE'} + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + ma = context.object.active_material + self.layout.label(ma.name) + self.layout.template_preview(ma) + + +class MATERIAL_PT_gpencil_custom_props(GPMaterialButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_OPENGL'} + _context_path = "object.active_material" + _property_type = bpy.types.Material + + +class MATERIAL_PT_gpencil_options(GPMaterialButtonsPanel, Panel): + bl_label = "Options" + bl_options = {'DEFAULT_CLOSED'} + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil is not None: + gpcolor = ma.grease_pencil + layout.prop(gpcolor, "pass_index") + + +classes = ( + GPENCIL_UL_matslots, + GPENCIL_MT_color_specials, + MATERIAL_PT_gpencil_slots, + MATERIAL_PT_gpencil_preview, + MATERIAL_PT_gpencil_surface, + MATERIAL_PT_gpencil_strokecolor, + MATERIAL_PT_gpencil_fillcolor, + MATERIAL_PT_gpencil_options, + MATERIAL_PT_gpencil_custom_props, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index 38bfc6ad294..91be9bb5d0a 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -28,9 +28,9 @@ from rna_prop_ui import PropertyPanel from bl_operators.presets import PresetMenu from .properties_physics_common import ( - point_cache_ui, - effector_weights_ui, -) + point_cache_ui, + effector_weights_ui, + ) class SCENE_PT_units_length_presets(PresetMenu): @@ -104,7 +104,6 @@ class SCENE_PT_unit(SceneButtonsPanel, Panel): col.prop(unit, "scale_length") col.prop(unit, "use_separate") - class SceneKeyingSetsPanel: @staticmethod @@ -568,6 +567,33 @@ class SCENE_PT_simplify_render(SceneButtonsPanel, Panel): col.prop(rd, "simplify_child_particles_render", text="Max Child Particles") +class SCENE_PT_simplify_greasepencil(SceneButtonsPanel, Panel): + bl_label = "Simplify Grease Pencil" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'} + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + rd = context.scene.render + self.layout.prop(rd, "simplify_gpencil", text="") + + def draw(self, context): + layout = self.layout + + rd = context.scene.render + + layout.active = rd.simplify_gpencil + + row = layout.row() + row.prop(rd, "simplify_gpencil_onplay", text="Only on Play") + + split = layout.split() + + col = split.column() + col.prop(rd, "simplify_gpencil_view_fill", text="Fill") + col.prop(rd, "simplify_gpencil_remove_lines", text="Remove Fill Lines") + col.prop(rd, "simplify_gpencil_view_modifier", text="Modifiers") + + class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} _context_path = "scene" @@ -593,6 +619,7 @@ classes = ( SCENE_PT_simplify, SCENE_PT_simplify_viewport, SCENE_PT_simplify_render, + SCENE_PT_simplify_greasepencil, SCENE_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 23c3e97ac9a..8e32d98529b 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -23,14 +23,8 @@ from bpy.types import Panel, Header, Menu, UIList from bpy.app.translations import pgettext_iface as iface_ from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel) class CLIP_UL_tracking_objects(UIList): @@ -1154,40 +1148,12 @@ class CLIP_PT_grease_pencil(GreasePencilDataPanel, CLIP_PT_clip_view_panel, Pane # But, this should only be visible in "clip" view -# Grease Pencil palette colors -class CLIP_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, CLIP_PT_clip_view_panel, Panel): - bl_space_type = 'CLIP_EDITOR' - bl_region_type = 'UI' - bl_options = {'DEFAULT_CLOSED'} - - # NOTE: this is just a wrapper around the generic GP Panel - # But, this should only be visible in "clip" view - - # Grease Pencil drawing tools class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'CLIP_EDITOR' + bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class CLIP_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil stroke sculpting tools -class CLIP_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil drawing brushes -class CLIP_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil drawing curves -class CLIP_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - class CLIP_MT_view(Menu): bl_label = "View" @@ -1515,12 +1481,7 @@ classes = ( CLIP_PT_footage_info, CLIP_PT_tools_scenesetup, CLIP_PT_grease_pencil, - CLIP_PT_grease_pencil_palettecolor, CLIP_PT_tools_grease_pencil_draw, - CLIP_PT_tools_grease_pencil_edit, - CLIP_PT_tools_grease_pencil_sculpt, - CLIP_PT_tools_grease_pencil_brush, - CLIP_PT_tools_grease_pencil_brushcurves, CLIP_MT_view, CLIP_MT_clip, CLIP_MT_proxy, diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 1303e46ab6c..501f58e9901 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -21,20 +21,15 @@ import bpy import math from bpy.types import Header, Menu, Panel, UIList from .properties_paint_common import ( - UnifiedPaintPanel, - brush_texture_settings, - brush_texpaint_common, - brush_mask_texture_settings, -) + UnifiedPaintPanel, + brush_texture_settings, + brush_texpaint_common, + brush_mask_texture_settings, + ) from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel + ) from bpy.app.translations import pgettext_iface as iface_ @@ -1346,39 +1341,12 @@ class IMAGE_PT_grease_pencil(GreasePencilDataPanel, Panel): # NOTE: this is just a wrapper around the generic GP Panel - -# Grease Pencil palette colors -class IMAGE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - # Grease Pencil drawing tools class IMAGE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class IMAGE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil stroke sculpting tools -class IMAGE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil drawing brushes -class IMAGE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil drawing curves -class IMAGE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - classes = ( IMAGE_MT_view, @@ -1430,12 +1398,7 @@ classes = ( IMAGE_PT_sample_line, IMAGE_PT_scope_sample, IMAGE_PT_grease_pencil, - IMAGE_PT_grease_pencil_palettecolor, IMAGE_PT_tools_grease_pencil_draw, - IMAGE_PT_tools_grease_pencil_edit, - IMAGE_PT_tools_grease_pencil_sculpt, - IMAGE_PT_tools_grease_pencil_brush, - IMAGE_PT_tools_grease_pencil_brushcurves, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 45343c09b27..affbf70b1a0 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -23,15 +23,10 @@ from bpy.types import Header, Menu, Panel from bpy.app.translations import pgettext_iface as iface_ from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, - GreasePencilToolsPanel -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel, + GreasePencilToolsPanel + ) class NODE_HT_header(Header): @@ -539,19 +534,6 @@ class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel): return snode is not None and snode.node_tree is not None -# Grease Pencil palette colors -class NODE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - @classmethod - def poll(cls, context): - snode = context.space_data - return snode is not None and snode.node_tree is not None - - class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' @@ -571,31 +553,6 @@ class NODE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - - -# Grease Pencil stroke sculpting tools -class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - -# Grease Pencil drawing brushes - - -class NODE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - -# Grease Pencil drawing curves - - -class NODE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - # ----------------------------- @@ -620,13 +577,8 @@ classes = ( NODE_PT_quality, NODE_UL_interface_sockets, NODE_PT_grease_pencil, - NODE_PT_grease_pencil_palettecolor, NODE_PT_grease_pencil_tools, NODE_PT_tools_grease_pencil_draw, - NODE_PT_tools_grease_pencil_edit, - NODE_PT_tools_grease_pencil_sculpt, - NODE_PT_tools_grease_pencil_brush, - NODE_PT_tools_grease_pencil_brushcurves, ) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index bac462dcbab..84ae59772b6 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -22,7 +22,6 @@ from bpy.types import Header, Menu, Panel from rna_prop_ui import PropertyPanel from .properties_grease_pencil_common import ( GreasePencilDataPanel, - GreasePencilPaletteColorPanel, GreasePencilToolsPanel, ) from bpy.app.translations import pgettext_iface as iface_ @@ -1281,14 +1280,6 @@ class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Ou # But, it should only show up when there are images in the preview region -class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel): - bl_space_type = 'SEQUENCE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - # But, it should only show up when there are images in the preview region - - class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -1333,7 +1324,6 @@ classes = ( SEQUENCER_PT_view_safe_areas, SEQUENCER_PT_modifiers, SEQUENCER_PT_grease_pencil, - SEQUENCER_PT_grease_pencil_palettecolor, SEQUENCER_PT_grease_pencil_tools, SEQUENCER_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 3101ffc8336..b682588629b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -24,6 +24,7 @@ # For now keep this in a single file since it's an area that may change, # so avoid making changes all over the place. +import bpy from bpy.types import Panel from .space_toolsystem_common import ( @@ -39,21 +40,77 @@ def generate_from_brushes_ex( brush_category_attr, brush_category_layout, ): + def draw_settings(context, layout, tool): + _defs_gpencil_paint.draw_settings_common(context, layout, tool) + # Categories brush_categories = {} - for brush in context.blend_data.brushes: - if getattr(brush, brush_test_attr): - category = getattr(brush, brush_category_attr) - name = brush.name - brush_categories.setdefault(category, []).append( - ToolDef.from_dict( - dict( - text=name, - icon=icon_prefix + category.lower(), - data_block=name, + if context.mode != 'GPENCIL_PAINT': + for brush in context.blend_data.brushes: + if getattr(brush, brush_test_attr) and brush.gpencil_settings is None: + category = getattr(brush, brush_category_attr) + name = brush.name + brush_categories.setdefault(category, []).append( + ToolDef.from_dict( + dict( + text=name, + icon=icon_prefix + category.lower(), + data_block=name, + ) ) ) - ) + else: + for brush_type in brush_category_layout: + for brush in context.blend_data.brushes: + if getattr(brush, brush_test_attr) and brush.gpencil_settings.gp_icon == brush_type[0]: + category = brush_type[0] + name = brush.name + + # rename default brushes for tool bar + if name.startswith("Draw "): + text = name.replace("Draw ", "") + elif name.startswith("Eraser "): + text = name.replace("Eraser ", "") + elif name.startswith("Fill "): + text = name.replace(" Area", "") + else: + text = name + + # define icon + gp_icon = brush.gpencil_settings.gp_icon + if gp_icon == 'PENCIL': + icon_name = 'draw_pencil' + elif gp_icon == 'PEN': + icon_name = 'draw_pen' + elif gp_icon == 'INK': + icon_name = 'draw_ink' + elif gp_icon == 'INKNOISE': + icon_name = 'draw_noise' + elif gp_icon == 'BLOCK': + icon_name = 'draw_block' + elif gp_icon == 'MARKER': + icon_name = 'draw_marker' + elif gp_icon == 'FILL': + icon_name = 'draw_fill' + elif gp_icon == 'SOFT': + icon_name = 'draw.eraser_soft' + elif gp_icon == 'HARD': + icon_name = 'draw.eraser_hard' + elif gp_icon == 'STROKE': + icon_name = 'draw.eraser_stroke' + + brush_categories.setdefault(category, []).append( + ToolDef.from_dict( + dict( + text=text, + icon=icon_prefix + icon_name, + data_block=name, + widget=None, + operator="gpencil.draw", + draw_settings=draw_settings, + ) + ) + ) def tools_from_brush_group(groups): assert(type(groups) is tuple) @@ -61,6 +118,7 @@ def generate_from_brushes_ex( tool_defs = tuple(brush_categories.pop(groups[0], ())) else: tool_defs = tuple(item for g in groups for item in brush_categories.pop(g, ())) + if len(tool_defs) > 1: return (tool_defs,) else: @@ -125,6 +183,112 @@ class _defs_view3d_generic: ) +class _defs_annotate: + @classmethod + def draw_settings_common(cls, context, layout, tool): + user_prefs = context.user_preferences + ts = context.tool_settings + + # XXX: These context checks are needed for layer-dependent settings, + # but this breaks for using topbar for 2D editor active tools, etc. + if type(context.gpencil_data_owner) is bpy.types.Object: + gpd = context.scene.grease_pencil + else: + gpd = context.gpencil_data + + gpl = gpd.layers.active if gpd else None + + if gpd and gpl: + layout.prop(gpd.layers, "active_note", text="") + layout.prop(gpl, "thickness", text="Thickness") + else: + layout.prop(user_prefs.edit, "grease_pencil_default_color", text="Color") + layout.prop(ts, "annotation_thickness", text="Thickness") + + # For 3D view, show the stroke placement settings + # XXX: How to tell what editor the active tool comes from? + is_3d_view = True + if is_3d_view: + layout.separator() + + row = layout.row(align=True) + row.prop(ts, "annotation_stroke_placement_view3d", text="Orientation") + if ts.gpencil_stroke_placement_view3d == 'CURSOR': + row.prop(ts.gpencil_sculpt, "lockaxis") + elif ts.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: + row.prop(ts, "use_gpencil_stroke_endpoints") + + @ToolDef.from_fn + def scribble(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Annotate", + icon="ops.gpencil.draw", + cursor='PAINT_BRUSH', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def line(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Draw Line", + icon="ops.gpencil.draw.line", + cursor='CROSSHAIR', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW_STRAIGHT', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def poly(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Draw Polygon", + icon="ops.gpencil.draw.poly", + cursor='CROSSHAIR', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW_POLY', wait_for_input=False), + dict(type='ACTIONMOUSE', value='PRESS')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def eraser(): + def draw_settings(context, layout, tool): + # TODO: Move this setting to toolsettings + user_prefs = context.user_preferences + layout.prop(user_prefs.edit, "grease_pencil_eraser_radius", text="Radius") + + return dict( + text="Eraser", + icon="ops.gpencil.draw.eraser", + cursor='CROSSHAIR', # XXX: Always show brush circle when enabled + keymap=( + ("gpencil.annotate", + dict(mode='ERASER', wait_for_input=False), + dict(type='ACTIONMOUSE', value='PRESS')), + ), + draw_settings=draw_settings, + ) + + class _defs_transform: @ToolDef.from_fn @@ -865,6 +1029,331 @@ class _defs_uv_select: ), ) +class _defs_gpencil_paint: + @classmethod + def draw_color_selector(cls, context, layout): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + ts = context.tool_settings + row = layout.row(align=True) + row.prop(ts, "use_gpencil_thumbnail_list", text="", icon="IMGDISPLAY") + if ts.use_gpencil_thumbnail_list is False: + row.template_ID(gp_settings, "material", live_icon=True) + else: + row.template_greasepencil_color(gp_settings, "material", rows=3, cols=8, scale=0.8) + + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_PAINT': + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + tool_settings= context.tool_settings + + if gp_settings.gpencil_brush_type == 'ERASE': + row = layout.row() + row.prop(brush, "size", text="Radius") + elif gp_settings.gpencil_brush_type == 'FILL': + row = layout.row() + row.prop(gp_settings, "gpencil_fill_leak", text="Leak Size") + row.prop(brush, "size", text="Thickness") + row.prop(gp_settings, "gpencil_fill_simplyfy_level", text="Simplify") + + _defs_gpencil_paint.draw_color_selector(context, layout) + + row = layout.row(align=True) + row.prop(gp_settings, "gpencil_fill_draw_mode", text="") + row.prop(gp_settings, "gpencil_fill_show_boundary", text="", icon='GRID') + + else: # bgpsettings.gpencil_brush_type == 'DRAW': + row = layout.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + _defs_gpencil_paint.draw_color_selector(context, layout) + + + @staticmethod + def generate_from_brushes(context): + return generate_from_brushes_ex( + context, + icon_prefix="brush.gpencil.", + brush_test_attr="use_paint_grease_pencil", + brush_category_attr="grease_pencil_tool", + brush_category_layout=( + ('PENCIL',), + ('PEN',), + ('INK',), + ('INKNOISE',), + ('BLOCK',), + ('MARKER',), + ('FILL',), + ('SOFT',), + ('HARD',), + ('STROKE',), + ) + ) + + +class _defs_gpencil_edit: + @ToolDef.from_fn + def bend(): + return dict( + text="Bend", + icon="ops.gpencil.edit_bend", + widget=None, + keymap=( + ("transform.bend", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def mirror(): + return dict( + text="Mirror", + icon="ops.gpencil.edit_mirror", + widget=None, + keymap=( + ("transform.mirror", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def shear(): + return dict( + text="Shear", + icon="ops.gpencil.edit_shear", + widget=None, + keymap=( + ("transform.shear", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def tosphere(): + return dict( + text="To Sphere", + icon="ops.gpencil.edit_to_sphere", + widget=None, + keymap=( + ("transform.tosphere", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + +class _defs_gpencil_sculpt: + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_SCULPT': + ts = context.tool_settings + settings = ts.gpencil_sculpt + brush = settings.brush + + layout.prop(brush, "size", slider=True) + + row = layout.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + row.separator() + row.prop(ts.gpencil_sculpt, "use_select_mask", text="") + + @ToolDef.from_fn + def smooth(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Smooth", + icon="ops.gpencil.sculpt_smooth", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='SMOOTH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def thickness(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Thickness", + icon="ops.gpencil.sculpt_thickness", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='THICKNESS', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def strength(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Strength", + icon="ops.gpencil.sculpt_strength", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='STRENGTH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def grab(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Grab", + icon="ops.gpencil.sculpt_grab", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='GRAB', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def push(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Push", + icon="ops.gpencil.sculpt_push", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='PUSH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def twist(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Twist", + icon="ops.gpencil.sculpt_twist", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='TWIST', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def pinch(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Pinch", + icon="ops.gpencil.sculpt_pinch", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='PINCH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def randomize(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Randomize", + icon="ops.gpencil.sculpt_randomize", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='RANDOMIZE', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def clone(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Clone", + icon="ops.gpencil.sculpt_clone", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='CLONE', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + +class _defs_gpencil_weight: + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_WEIGHT': + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + layout.prop(brush, "size", slider=True) + + row = layout.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + + @ToolDef.from_fn + def paint(): + def draw_settings(context, layout, tool): + _defs_gpencil_weight.draw_settings_common(context, layout, tool) + + return dict( + text="Draw", + icon="ops.gpencil.sculpt_weight", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='WEIGHT', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): bl_space_type = 'IMAGE_EDITOR' @@ -951,8 +1440,6 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_transform.scale, _defs_transform.scale_cage, ), - None, - _defs_view3d_generic.ruler, ) _tools_select = ( @@ -963,6 +1450,16 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), ) + _tools_annotate = ( + ( + _defs_annotate.scribble, + _defs_annotate.line, + _defs_annotate.poly, + _defs_annotate.eraser, + ), + _defs_view3d_generic.ruler, + ) + _tools = { None: [ _defs_view3d_generic.cursor, @@ -972,21 +1469,27 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): *_tools_select, None, *_tools_transform, + None, + *_tools_annotate, ], 'POSE': [ *_tools_select, *_tools_transform, None, + *_tools_annotate, + None, ( _defs_pose.breakdown, _defs_pose.push, _defs_pose.relax, - ) + ), ], 'EDIT_ARMATURE': [ *_tools_select, None, *_tools_transform, + None, + *_tools_annotate, _defs_edit_armature.roll, ( _defs_edit_armature.bone_size, @@ -996,13 +1499,15 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ( _defs_edit_armature.extrude, _defs_edit_armature.extrude_cursor, - ) + ), ], 'EDIT_MESH': [ *_tools_select, None, *_tools_transform, None, + *_tools_annotate, + None, _defs_edit_mesh.cube_add, None, ( @@ -1047,6 +1552,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_transform, None, + *_tools_annotate, + None, _defs_edit_curve.draw, _defs_edit_curve.extrude_cursor, ], @@ -1075,6 +1582,33 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, _defs_weight_paint.gradient, ], + 'GPENCIL_PAINT': [ + _defs_gpencil_paint.generate_from_brushes, + ], + 'GPENCIL_EDIT': [ + *_tools_select, + None, + *_tools_transform, + None, + _defs_gpencil_edit.bend, + _defs_gpencil_edit.mirror, + _defs_gpencil_edit.shear, + _defs_gpencil_edit.tosphere, + ], + 'GPENCIL_SCULPT': [ + _defs_gpencil_sculpt.smooth, + _defs_gpencil_sculpt.thickness, + _defs_gpencil_sculpt.strength, + _defs_gpencil_sculpt.grab, + _defs_gpencil_sculpt.push, + _defs_gpencil_sculpt.twist, + _defs_gpencil_sculpt.pinch, + _defs_gpencil_sculpt.randomize, + _defs_gpencil_sculpt.clone, + ], + 'GPENCIL_WEIGHT': [ + _defs_gpencil_weight.paint, + ], } diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 55129aa0ce1..0d837ae413a 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -128,6 +128,8 @@ class TOPBAR_HT_lower_bar(Header): pass elif mode == 'PARTICLE': layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="") + elif mode == 'GPENCIL_PAINT': + layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_paint", category="") def draw_center(self, context): pass @@ -165,6 +167,15 @@ class TOPBAR_HT_lower_bar(Header): layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="") elif mode == 'OBJECT': layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".objectmode", category="") + elif mode == 'GPENCIL_PAINT': + layout.prop(context.tool_settings, "gpencil_stroke_placement_view3d", text='') + if context.tool_settings.gpencil_stroke_placement_view3d in ('ORIGIN', 'CURSOR'): + layout.prop(context.tool_settings.gpencil_sculpt, "lockaxis", text='') + layout.prop(context.tool_settings, "use_gpencil_draw_onback", text="", icon='ORTHO') + layout.prop(context.tool_settings, "use_gpencil_additive_drawing", text="", icon='FREEZE') + + elif mode == 'GPENCIL_SCULPT': + layout.prop(context.tool_settings.gpencil_sculpt, "lockaxis", text='') class _draw_left_context_mode: diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 53aaff7c4bf..52d4640806d 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -361,14 +361,16 @@ class USERPREF_PT_edit(Panel): row.separator() col = row.column() - col.label(text="Grease Pencil:") + col.label(text="Annotations:") + sub = col.row() + sub.prop(edit, "grease_pencil_default_color", text="Default Color") col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") col.separator() + col.label(text="Grease Pencil/Annotations:") + col.separator() col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") col.separator() - col.prop(edit, "grease_pencil_default_color", text="Default Color") - col.separator() col.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke") col.separator() col.separator() @@ -527,7 +529,10 @@ class USERPREF_PT_system(Panel): col.prop(system, "gpu_viewport_quality") col.separator() + col.label(text="Grease Pencil Options:") + col.prop(system, "gpencil_multi_sample", text="") + col.separator() col.label(text="Text Draw Options:") col.prop(system, "use_text_antialiasing") if system.use_text_antialiasing: diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index aed5faff73c..eb595e9f12d 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -19,11 +19,8 @@ # import bpy from bpy.types import Header, Menu, Panel -from .properties_grease_pencil_common import ( - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) from .properties_paint_common import UnifiedPaintPanel +from .properties_grease_pencil_common import GreasePencilDataPanel from bpy.app.translations import contexts as i18n_contexts @@ -80,18 +77,40 @@ class VIEW3D_HT_header(Header): row.operator("pose.paste", text="", icon='PASTEDOWN').flipped = False row.operator("pose.paste", text="", icon='PASTEFLIPDOWN').flipped = True - # GPencil - if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode: - row = layout.row(align=True) - row.operator("gpencil.copy", text="", icon='COPYDOWN') - row.operator("gpencil.paste", text="", icon='PASTEDOWN') + # Grease Pencil + if obj and obj.type == 'GPENCIL' and context.gpencil_data: + gpd = context.gpencil_data - # XXX: icon - layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH') + if gpd.is_stroke_paint_mode: + row = layout.row(align=True) + row.popover( + panel="VIEW3D_PT_tools_grease_pencil_shapes", + text="Shapes" + ) - row = layout.row(align=True) - row.prop(tool_settings.gpencil_sculpt, "use_select_mask") - row.prop(tool_settings.gpencil_sculpt, "selection_alpha", slider=True) + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode: + row = layout.row(align=True) + row.prop(gpd, "use_multiedit", text="", icon="FORCE_HARMONIC") + + sub = row.row(align=True) + sub.active = gpd.use_multiedit + sub.popover( + panel="VIEW3D_PT_gpencil_multi_frame", + text="Multiframe" + ) + + if gpd.use_stroke_edit_mode: + row = layout.row(align=True) + row.operator("gpencil.copy", text="", icon='COPYDOWN') + row.operator("gpencil.paste", text="", icon='PASTEDOWN') + + row = layout.row(align=True) + row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="") + + row.popover( + panel="VIEW3D_PT_tools_grease_pencil_interpolate", + text="Interpolate" + ) VIEW3D_MT_editor_menus.draw_collapsible(context, layout) @@ -101,7 +120,7 @@ class VIEW3D_HT_header(Header): scene = context.scene # Orientation - if object_mode in {'OBJECT', 'EDIT', 'POSE'}: + if object_mode in {'OBJECT', 'EDIT', 'POSE', 'GPENCIL_EDIT'}: orientation = scene.transform_orientation current_orientation = scene.current_orientation @@ -126,7 +145,8 @@ class VIEW3D_HT_header(Header): if obj is None: show_snap = True else: - if object_mode not in {'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT'}: + if object_mode not in {'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', + 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'}: show_snap = True else: @@ -160,13 +180,15 @@ class VIEW3D_HT_header(Header): # Proportional editing if obj: - if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode: - row = layout.row(align=True) - row.prop(tool_settings, "proportional_edit", icon_only=True) + gpd = context.gpencil_data + if gpd is not None: + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode: + row = layout.row(align=True) + row.prop(tool_settings, "proportional_edit", icon_only=True) - sub = row.row(align=True) - sub.active = tool_settings.proportional_edit != 'DISABLED' - sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) + sub = row.row(align=True) + sub.active = tool_settings.proportional_edit != 'DISABLED' + sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) elif object_mode in {'EDIT', 'PARTICLE_EDIT'}: row = layout.row(align=True) @@ -190,7 +212,7 @@ class VIEW3D_HT_header(Header): sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) # Pivot - if object_mode in {'OBJECT', 'EDIT', 'POSE'}: + if object_mode in {'OBJECT', 'EDIT', 'POSE', 'GPENCIL_EDIT', 'GPENCIL_SCULPT'}: pivot_point = tool_settings.transform_pivot_point act_pivot_point = bpy.types.ToolSettings.bl_rna.properties["transform_pivot_point"].enum_items[pivot_point] row = layout.row(align=True) @@ -234,13 +256,14 @@ class VIEW3D_MT_editor_menus(Menu): obj = context.active_object mode_string = context.mode edit_object = context.edit_object - gp_edit = context.gpencil_data and context.gpencil_data.use_stroke_edit_mode + gp_edit = obj and obj.mode in {'GPENCIL_EDIT', 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'} layout.menu("VIEW3D_MT_view") # Select Menu if gp_edit: - layout.menu("VIEW3D_MT_select_gpencil") + if mode_string not in {'GPENCIL_PAINT', 'GPENCIL_WEIGHT'}: + layout.menu("VIEW3D_MT_select_gpencil") elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: mesh = obj.data if mesh.use_paint_mask: @@ -266,7 +289,15 @@ class VIEW3D_MT_editor_menus(Menu): layout.menu("INFO_MT_edit_armature_add", text="Add") if gp_edit: - layout.menu("VIEW3D_MT_edit_gpencil") + if obj and obj.mode == 'GPENCIL_PAINT': + layout.menu("VIEW3D_MT_paint_gpencil") + elif obj and obj.mode == 'GPENCIL_EDIT': + layout.menu("VIEW3D_MT_edit_gpencil") + elif obj and obj.mode == 'GPENCIL_SCULPT': + layout.menu("VIEW3D_MT_sculpt_gpencil") + elif obj and obj.mode == 'GPENCIL_WEIGHT': + layout.menu("VIEW3D_MT_weight_gpencil") + elif edit_object: layout.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower()) @@ -1194,6 +1225,7 @@ class VIEW3D_MT_select_gpencil(Menu): layout.separator() layout.operator("gpencil.select_linked", text="Linked") + layout.operator("gpencil.select_alternate") layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped") layout.separator() @@ -1454,6 +1486,7 @@ class INFO_MT_add(Menu): layout.menu("INFO_MT_armature_add", icon='OUTLINER_OB_ARMATURE') layout.operator("object.add", text="Lattice", icon='OUTLINER_OB_LATTICE').type = 'LATTICE' layout.operator_menu_enum("object.empty_add", "type", text="Empty", icon='OUTLINER_OB_EMPTY') + layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL') layout.separator() layout.operator("object.speaker_add", text="Speaker", icon='OUTLINER_OB_SPEAKER') @@ -3110,12 +3143,17 @@ class VIEW3D_MT_edit_gpencil_delete(Menu): layout.separator() - layout.operator("gpencil.dissolve") + layout.operator_enum("gpencil.dissolve", "type") layout.separator() layout.operator("gpencil.active_frames_delete_all") + layout.separator() + + layout.operator("gpencil.frame_clean_fill", text="Clean Boundary Strokes").mode = 'ACTIVE' + layout.operator("gpencil.frame_clean_fill", text="Clean Boundary Strokes all Frames").mode = 'ALL' + # Edit Curve # draw_curve is used by VIEW3D_MT_edit_curve and VIEW3D_MT_edit_surface @@ -3476,11 +3514,34 @@ class VIEW3D_MT_edit_armature_delete(Menu): layout.operator("armature.dissolve", text="Dissolve") -# ********** GPencil Stroke Edit menu ********** +# ********** Grease Pencil Stroke menus ********** +class VIEW3D_MT_gpencil_simplify(Menu): + bl_label = "Simplify" + + def draw(self, context): + layout = self.layout + layout.operator("gpencil.stroke_simplify_fixed", text="Fixed") + layout.operator("gpencil.stroke_simplify", text="Adaptative") + + +class VIEW3D_MT_paint_gpencil(Menu): + bl_label = "Strokes" + + def draw(self, context): + + layout = self.layout + + layout.menu("VIEW3D_MT_gpencil_animation") + layout.menu("VIEW3D_MT_edit_gpencil_interpolate") + + layout.separator() + + layout.operator("gpencil.delete", text="Delete Frame").type = 'FRAME' + layout.operator("gpencil.active_frames_delete_all") class VIEW3D_MT_edit_gpencil(Menu): - bl_label = "GPencil" + bl_label = "Strokes" def draw(self, context): tool_settings = context.tool_settings @@ -3488,44 +3549,41 @@ class VIEW3D_MT_edit_gpencil(Menu): layout = self.layout layout.menu("VIEW3D_MT_edit_gpencil_transform") - layout.operator("transform.mirror", text="Mirror") + + layout.separator() layout.menu("GPENCIL_MT_snap") layout.separator() - layout.operator("gpencil.brush_paint", text="Sculpt Strokes").wait_for_input = True - layout.prop_menu_enum(tool_settings.gpencil_sculpt, "tool", text="Sculpt Brush") + layout.menu("VIEW3D_MT_gpencil_animation") layout.separator() - layout.menu("VIEW3D_MT_object_animation") # NOTE: provides keyingset access... layout.menu("VIEW3D_MT_edit_gpencil_interpolate") layout.separator() layout.operator("gpencil.duplicate_move", text="Duplicate") layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.menu("VIEW3D_MT_gpencil_simplify") layout.separator() + layout.operator_menu_enum("gpencil.stroke_separate", "mode", text="Separate...") + layout.operator("gpencil.stroke_split", text="Split") layout.operator_menu_enum("gpencil.stroke_join", "type", text="Join...") layout.operator("gpencil.stroke_flip", text="Flip Direction") layout.separator() layout.operator("gpencil.copy", text="Copy") - layout.operator("gpencil.paste", text="Paste") - - layout.separator() - - layout.operator("gpencil.reveal") - layout.operator("gpencil.hide", text="Show Active Layer Only").unselected = True - layout.operator("gpencil.hide", text="Hide Active Layer").unselected = False + layout.operator("gpencil.paste", text="Paste").type = 'COPY' + layout.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE' layout.separator() layout.operator_menu_enum("gpencil.move_to_layer", "layer", text="Move to Layer") - layout.operator("gpencil.stroke_change_color", text="Move to Color") + layout.operator("gpencil.stroke_change_color", text="Change Color") layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes...") layout.separator() @@ -3535,6 +3593,82 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.separator() layout.menu("VIEW3D_MT_edit_gpencil_delete") + layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' + + layout.separator() + + layout.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") + + +class VIEW3D_MT_sculpt_gpencil(Menu): + bl_label = "Strokes" + + def draw(self, context): + layout = self.layout + + layout.menu("VIEW3D_MT_edit_gpencil_transform") + + layout.separator() + layout.menu("GPENCIL_MT_snap") + + layout.separator() + + layout.operator("gpencil.duplicate_move", text="Duplicate") + layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.menu("VIEW3D_MT_gpencil_simplify") + + layout.separator() + + layout.operator_menu_enum("gpencil.stroke_separate", "mode", text="Separate...") + layout.operator("gpencil.stroke_split", text="Split") + layout.operator_menu_enum("gpencil.stroke_join", "type", text="Join...") + layout.operator("gpencil.stroke_flip", text="Flip Direction") + + layout.separator() + + layout.operator("gpencil.copy", text="Copy") + layout.operator("gpencil.paste", text="Paste").type = 'COPY' + layout.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE' + + layout.separator() + + layout.operator_menu_enum("gpencil.move_to_layer", "layer", text="Move to Layer") + layout.operator("gpencil.stroke_change_color", text="Change Color") + layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes...") + + layout.separator() + + layout.operator_menu_enum("gpencil.convert", "type", text="Convert to Geometry...") + + +class VIEW3D_MT_weight_gpencil(Menu): + bl_label = "Weights" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.vertex_group_invert", text="Invert") + layout.operator("gpencil.vertex_group_smooth", text="Smooth") + + +class VIEW3D_MT_gpencil_animation(Menu): + bl_label = "Animation" + + @classmethod + def poll(cls, context): + ob = context.active_object + return ob and ob.type == 'GPENCIL' and ob.mode != 'OBJECT' + + @staticmethod + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.blank_frame_add") + layout.operator("gpencil.active_frames_delete_all", text="Delete Frame(s)") + + layout.separator() + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate All Layers").mode = 'ALL' class VIEW3D_MT_edit_gpencil_transform(Menu): @@ -3595,20 +3729,6 @@ class VIEW3D_MT_view_pie(Menu): # ********** Panel ********** -class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - -class VIEW3D_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - class VIEW3D_PT_view3d_properties(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -3720,6 +3840,7 @@ class VIEW3D_PT_object_type_visibility(Panel): "armature", "lattice", "empty", + "grease_pencil", "camera", "light", "light_probe", @@ -4040,6 +4161,8 @@ class VIEW3D_PT_overlay_guides(Panel): if shading.type == 'MATERIAL': col.prop(overlay, "show_look_dev") + col.prop(overlay, "show_annotation", text="Annotations") + class VIEW3D_PT_overlay_object(Panel): bl_space_type = 'VIEW_3D' @@ -4578,6 +4701,60 @@ class VIEW3D_PT_transform_orientations(Panel): row.operator("transform.delete_orientation", text="", icon='X', emboss=False) +class VIEW3D_PT_overlay_gpencil_options(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_parent_id = 'VIEW3D_PT_overlay' + bl_label = "" + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + def draw_header(self, context): + layout = self.layout + layout.label(text={ + 'GPENCIL_PAINT': "Draw Grease Pencil", + 'GPENCIL_EDIT': "Edit Grease Pencil", + 'GPENCIL_SCULPT': "Sculpt Grease Pencil", + 'GPENCIL_WEIGHT': "Weight Grease Pencil", + 'OBJECT': "Grease Pencil", + }[context.mode]) + + def draw(self, context): + layout = self.layout + view = context.space_data + overlay = view.overlay + + layout.prop(overlay, "use_gpencil_onion_skin", text="Onion Skin") + + col = layout.column() + row = col.row() + row.prop(overlay, "use_gpencil_paper", text="") + sub = row.row() + sub.active = overlay.use_gpencil_paper + sub.prop(overlay, "gpencil_paper_opacity", text="Fade 3D Objects", slider=True) + + col = layout.column() + row = col.row() + row.prop(overlay, "use_gpencil_grid", text="") + sub = row.row() + sub.active = overlay.use_gpencil_grid + sub.prop(overlay, "gpencil_grid_opacity", text="Canvas Grid", slider=True) + + if overlay.use_gpencil_grid: + row = layout.row(align=True) + row.prop(overlay, "gpencil_grid_scale") + col = row.column() + col.prop(overlay, "gpencil_grid_lines", text="Subdivisions") + col.prop(overlay, "gpencil_grid_axis") + + if context.object.mode in {'GPENCIL_EDIT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'}: + layout.prop(overlay, "use_gpencil_edit_lines", text="Edit Lines") + layout.prop(overlay, "use_gpencil_multiedit_line_only", text="Show Edit Lines only in multiframe") + layout.prop(overlay, "vertex_opacity", text="Vertex Opacity", slider=True) + + class VIEW3D_PT_quad_view(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -4605,6 +4782,14 @@ class VIEW3D_PT_quad_view(Panel): row.prop(region, "use_box_clip") +# Annotation properties +class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + + class VIEW3D_PT_view3d_stereo(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -4684,6 +4869,27 @@ class VIEW3D_PT_context_properties(Panel): rna_prop_ui.draw(self.layout, context, member, object, False) +# Grease Pencil Object - Multiframe falloff tools +class VIEW3D_PT_gpencil_multi_frame(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Multi Frame" + + @staticmethod + def draw(self, context): + gpd = context.gpencil_data + settings = context.tool_settings.gpencil_sculpt + + layout = self.layout + col = layout.column(align=True) + col.prop(settings, "use_multiframe_falloff") + + # Falloff curve + if gpd.use_multiedit and settings.use_multiframe_falloff: + layout.template_curve_mapping(settings, "multiframe_falloff_curve", brush=True) + + + classes = ( VIEW3D_HT_header, VIEW3D_MT_editor_menus, @@ -4791,8 +4997,13 @@ classes = ( VIEW3D_MT_edit_mesh_clean, VIEW3D_MT_edit_mesh_delete, VIEW3D_MT_edit_mesh_showhide, + VIEW3D_MT_paint_gpencil, VIEW3D_MT_edit_gpencil, VIEW3D_MT_edit_gpencil_delete, + VIEW3D_MT_sculpt_gpencil, + VIEW3D_MT_weight_gpencil, + VIEW3D_MT_gpencil_animation, + VIEW3D_MT_gpencil_simplify, VIEW3D_MT_edit_curve, VIEW3D_MT_edit_curve_ctrlpoints, VIEW3D_MT_edit_curve_segments, @@ -4815,12 +5026,12 @@ classes = ( VIEW3D_MT_edit_gpencil_interpolate, VIEW3D_MT_object_mode_pie, VIEW3D_MT_view_pie, - VIEW3D_PT_grease_pencil, - VIEW3D_PT_grease_pencil_palettecolor, VIEW3D_PT_view3d_properties, VIEW3D_PT_view3d_camera_lock, VIEW3D_PT_view3d_cursor, VIEW3D_PT_object_type_visibility, + VIEW3D_PT_grease_pencil, + VIEW3D_PT_gpencil_multi_frame, VIEW3D_PT_quad_view, VIEW3D_PT_view3d_stereo, VIEW3D_PT_shading, @@ -4849,6 +5060,7 @@ classes = ( VIEW3D_PT_pivot_point, VIEW3D_PT_snapping, VIEW3D_PT_transform_orientations, + VIEW3D_PT_overlay_gpencil_options, VIEW3D_PT_context_properties, ) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 87c6a3840ae..70d8c4089d3 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -20,19 +20,17 @@ import bpy from bpy.types import Menu, Panel, UIList from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilInterpolatePanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel -) + GreasePencilStrokeEditPanel, + GreasePencilStrokeSculptPanel, + GreasePencilAppearancePanel, + ) from .properties_paint_common import ( - UnifiedPaintPanel, - brush_texture_settings, - brush_texpaint_common, - brush_mask_texture_settings, -) + UnifiedPaintPanel, + brush_texture_settings, + brush_texpaint_common, + brush_mask_texture_settings, + ) +from bl_operators.presets import PresetMenu class View3DPanel: @@ -70,6 +68,12 @@ def draw_vpaint_symmetry(layout, vpaint): col.use_property_split = True col.prop(vpaint, "radial_symmetry", text="Radial") +# Most of these panels should not be visible in GP edit modes +def is_not_gpencil_edit_mode(context): + is_gpmode = context.active_object and \ + context.active_object.mode in {'GPENCIL_EDIT', 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'} + return not is_gpmode + # ********** default tools for editmode_mesh **************** @@ -1341,9 +1345,272 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, Panel): sub.prop(pe, "fade_frames", slider=True) -# Grease Pencil drawing tools -class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): +# ********** grease pencil object tool panels **************** + +# Grease Pencil drawing brushes +class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Brush" + + @classmethod + def poll(cls, context): + is_3d_view = context.space_data.type == 'VIEW_3D' + if is_3d_view: + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.is_stroke_paint_mode) + else: + return True + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + ts = context.scene.tool_settings + settings = ts.gpencil_paint + + row = layout.row() + col = row.column() + col.template_ID_preview(settings, "brush", new="brush.add_gpencil", rows=3, cols=8) + + col = row.column() + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + sub = col.column(align=True) + sub.operator("gpencil.brush_presets_create", icon='HELP', text="") + + if brush is not None: + # XXX: Items in "sub" currently show up beside the brush selector in a separate column + if gp_settings.gpencil_brush_type == 'ERASE': + sub.prop(gp_settings, "default_eraser", text="") + + # Brush details + if gp_settings.gpencil_brush_type == 'ERASE': + col = layout.column(align=True) + col.prop(brush, "size", text="Radius") + + col.separator() + row = col.row() + row.prop(gp_settings, "eraser_mode", expand=True) + elif gp_settings.gpencil_brush_type == 'FILL': + col = layout.column(align=True) + col.prop(gp_settings, "gpencil_fill_leak", text="Leak Size") + col.prop(brush, "size", text="Thickness") + col.prop(gp_settings, "gpencil_fill_simplyfy_level", text="Simplify") + + col = layout.row(align=True) + col.template_ID(gp_settings, "material") + + row = layout.row(align=True) + row.prop(gp_settings, "gpencil_fill_draw_mode", text="Boundary Draw Mode") + row.prop(gp_settings, "gpencil_fill_show_boundary", text="", icon='GRID') + + col = layout.column(align=True) + col.enabled = gp_settings.gpencil_fill_draw_mode != "STROKE" + col.prop(gp_settings, "gpencil_fill_hide", text="Hide Transparent Lines") + sub = col.row(align=True) + sub.enabled = gp_settings.gpencil_fill_hide + sub.prop(gp_settings, "gpencil_fill_threshold", text="Threshold") + else: # bgpsettings.gpencil_brush_type == 'DRAW': + row = layout.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + row = layout.row(align=True) + row.template_ID(gp_settings, "material") + + +# Grease Pencil drawing brushes options +class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Options" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header_preset(self, context): + VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + if brush is not None: + col = layout.column(align=True) + col.prop(gp_settings, "input_samples") + col.separator() + + col.prop(gp_settings, "active_smooth_factor") + col.separator() + + col.prop(gp_settings, "angle", slider=True) + col.prop(gp_settings, "angle_factor", text="Factor", slider=True) + col.separator() + + +class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Stabilizer" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + return brush is not None and gp_settings.gpencil_brush_type == 'DRAW' + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "use_stabilizer", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.use_stabilizer + + layout.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + layout.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + +class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Post-processing Settings" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + + return brush is not None + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "enable_settings", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.enable_settings + + layout.prop(gp_settings, "pen_smooth_factor") + layout.prop(gp_settings, "pen_smooth_steps") + + layout.prop(gp_settings, "pen_thick_smooth_factor") + layout.prop(gp_settings, "pen_thick_smooth_steps") + + layout.prop(gp_settings, "pen_subdivision_steps") + layout.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + + +class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Random Settings" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + + return brush is not None + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "enable_random", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.enable_random + + layout.prop(gp_settings, "random_pressure", text="Pressure", slider=True) + layout.prop(gp_settings, "random_strength", text="Strength", slider=True) + layout.prop(gp_settings, "uv_random", text="UV", slider=True) + + row = layout.row(align=True) + row.prop(gp_settings, "pen_jitter", slider=True) + row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') + + +# Grease Pencil drawingcurves +class VIEW3D_PT_tools_grease_pencil_brushcurves(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Curves" + bl_options = {'DEFAULT_CLOSED'} + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + # Brush + layout.label("Sensitivity") + layout.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True) + + layout.label("Strength") + layout.template_curve_mapping(gp_settings, "curve_strength", brush=True) + + layout.label("Jitter") + layout.template_curve_mapping(gp_settings, "curve_jitter", brush=True) + + +# Grease Pencil create shapes +class VIEW3D_PT_tools_grease_pencil_shapes(View3DPanel, Panel): bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Shapes" + + @classmethod + def poll(cls, context): + ob = context.active_object + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + col = layout.column(align=True) + col.operator("gpencil.primitive", text="Line", icon='IPO_CONSTANT').type = 'LINE' + col.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX' + col.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE' + + layout.operator("object.gpencil_add", text="Monkey", icon='MONKEY').type = 'MONKEY' # Grease Pencil stroke editing tools @@ -1352,24 +1619,109 @@ class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): # Grease Pencil stroke interpolation tools -class VIEW3D_PT_tools_grease_pencil_interpolate(GreasePencilInterpolatePanel, Panel): +class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Interpolate" + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) + + @staticmethod + def draw(self, context): + layout = self.layout + settings = context.tool_settings.gpencil_interpolate + + col = layout.column(align=True) + col.label("Interpolate Strokes") + col.operator("gpencil.interpolate", text="Interpolate") + col.operator("gpencil.interpolate_sequence", text="Sequence") + col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns") + + col = layout.column(align=True) + col.label(text="Options:") + col.prop(settings, "interpolate_all_layers") + col.prop(settings, "interpolate_selected_only") + + col = layout.column(align=True) + col.label(text="Sequence Options:") + col.prop(settings, "type") + if settings.type == 'CUSTOM': + # TODO: Options for loading/saving curve presets? + col.template_curve_mapping(settings, "interpolation_curve", brush=True) + elif settings.type != 'LINEAR': + col.prop(settings, "easing") + + if settings.type == 'BACK': + layout.prop(settings, "back") + elif setting.type == 'ELASTIC': + sub = layout.column(align=True) + sub.prop(settings, "amplitude") + sub.prop(settings, "period") # Grease Pencil stroke sculpting tools -class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'VIEW_3D' +class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_category = "Tools" + bl_label = "Sculpt Strokes" -# Grease Pencil drawing brushes -class VIEW3D_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'VIEW_3D' +# Grease Pencil weight painting tools +class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): + bl_context = ".greasepencil_weight" + bl_category = "Tools" + bl_label = "Weight Paint" -# Grease Pencil drawingcurves + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + gpd = context.gpencil_data + settings = context.tool_settings.gpencil_sculpt + tool = settings.tool + brush = settings.brush + + layout.template_icon_view(settings, "weight_tool", show_labels=True) + + col = layout.column() + col.prop(brush, "size", slider=True) + row = col.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + + col.prop(brush, "use_falloff") -class VIEW3D_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'VIEW_3D' +# Grease Pencil Brush Appeareance (one for each mode) +class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Appearance" + + +class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Appearance" + + +class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_weight" + bl_label = "Appearance" + + +class VIEW3D_PT_gpencil_brush_presets(PresetMenu): + """Brush settings""" + bl_label = "Brush Presets" + preset_subdir = "gpencil_brush" + preset_operator = "script.execute_preset" + preset_add_operator = "scene.gpencil_brush_preset_add" classes = ( @@ -1401,12 +1753,21 @@ classes = ( VIEW3D_PT_tools_projectpaint, VIEW3D_MT_tools_projectpaint_stencil, VIEW3D_PT_tools_particlemode, - VIEW3D_PT_tools_grease_pencil_draw, - VIEW3D_PT_tools_grease_pencil_edit, - VIEW3D_PT_tools_grease_pencil_interpolate, - VIEW3D_PT_tools_grease_pencil_sculpt, + + VIEW3D_PT_gpencil_brush_presets, VIEW3D_PT_tools_grease_pencil_brush, + VIEW3D_PT_tools_grease_pencil_brush_option, + VIEW3D_PT_tools_grease_pencil_brush_settings, + VIEW3D_PT_tools_grease_pencil_brush_stabilizer, + VIEW3D_PT_tools_grease_pencil_brush_random, VIEW3D_PT_tools_grease_pencil_brushcurves, + VIEW3D_PT_tools_grease_pencil_shapes, + VIEW3D_PT_tools_grease_pencil_sculpt, + VIEW3D_PT_tools_grease_pencil_weight_paint, + VIEW3D_PT_tools_grease_pencil_paint_appearance, + VIEW3D_PT_tools_grease_pencil_sculpt_appearance, + VIEW3D_PT_tools_grease_pencil_weight_appearance, + VIEW3D_PT_tools_grease_pencil_interpolate, ) if __name__ == "__main__": # only for live edit. diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 5709ac723f4..2c9b1efe2f4 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -44,7 +44,9 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_fileglobal_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_freestyle_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_genfile.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_modifier_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpencil_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_shader_fx_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_gpu_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_group_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_image_types.h @@ -112,6 +114,8 @@ add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) add_subdirectory(modifiers) +add_subdirectory(gpencil_modifiers) +add_subdirectory(shader_fx) add_subdirectory(makesdna) add_subdirectory(makesrna) diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index eda1c51bbc2..489746cbfd9 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -28,11 +28,14 @@ */ enum eCurveMappingPreset; +struct bContext; struct Brush; +struct Paint; struct ImBuf; struct ImagePool; struct Main; struct Scene; +struct ToolSettings; struct UnifiedPaintSettings; // enum eCurveMappingPreset; @@ -45,14 +48,17 @@ void BKE_brush_system_exit(void); /* datablock functions */ void BKE_brush_init(struct Brush *brush); struct Brush *BKE_brush_add(struct Main *bmain, const char *name, const eObjectMode ob_mode); +struct Brush *BKE_brush_add_gpencil(struct Main *bmain, struct ToolSettings *ts, const char *name); struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode); void BKE_brush_copy_data(struct Main *bmain, struct Brush *brush_dst, const struct Brush *brush_src, const int flag); struct Brush *BKE_brush_copy(struct Main *bmain, const struct Brush *brush); void BKE_brush_make_local(struct Main *bmain, struct Brush *brush, const bool lib_local); -void BKE_brush_unlink(struct Main *bmain, struct Brush *brush); void BKE_brush_free(struct Brush *brush); void BKE_brush_sculpt_reset(struct Brush *brush); +void BKE_brush_gpencil_presets(struct bContext *C); +struct Brush *BKE_brush_getactive_gpencil(struct ToolSettings *ts); +struct Paint *BKE_brush_get_gpencil_paint(struct ToolSettings *ts); /* image icon function */ struct ImBuf *get_brush_icon(struct Brush *brush); diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 9f57859d318..28dcf9cb127 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -65,9 +65,7 @@ struct bPoseChannel; struct bGPdata; struct bGPDlayer; struct bGPDframe; -struct bGPDpalette; -struct bGPDpalettecolor; -struct bGPDbrush; +struct Brush; struct wmWindow; struct wmWindowManager; struct RenderEngineType; @@ -120,6 +118,10 @@ enum { CTX_MODE_PAINT_TEXTURE, CTX_MODE_PARTICLE, CTX_MODE_OBJECT, + CTX_MODE_GPENCIL_PAINT, + CTX_MODE_GPENCIL_EDIT, + CTX_MODE_GPENCIL_SCULPT, + CTX_MODE_GPENCIL_WEIGHT, CTX_MODE_NUM /* must be last */ }; @@ -313,9 +315,7 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list); struct bGPdata *CTX_data_gpencil_data(const bContext *C); struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C); struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C); -struct bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C); -struct bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C); -struct bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C); +struct Brush *CTX_data_active_gpencil_brush(const bContext *C); int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index 3a951b7860d..887a7f4f67b 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -31,25 +31,45 @@ * \author Joshua Leung */ +struct CurveMapping; +struct Depsgraph; +struct GpencilModifierData; struct ToolSettings; struct ListBase; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct bGPDspoint; struct bGPDstroke; +struct Material; struct bGPDpalette; struct bGPDpalettecolor; struct Main; +struct BoundBox; +struct Brush; +struct Object; +struct bDeformGroup; +struct SimplifyGpencilModifierData; +struct InstanceGpencilModifierData; +struct LatticeGpencilModifierData; + +struct MDeformVert; +struct MDeformWeight; /* ------------ Grease-Pencil API ------------------ */ +void BKE_gpencil_free_point_weights(struct MDeformVert *dvert); +void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps); void BKE_gpencil_free_stroke(struct bGPDstroke *gps); bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); void BKE_gpencil_free_frames(struct bGPDlayer *gpl); void BKE_gpencil_free_layers(struct ListBase *list); -void BKE_gpencil_free_brushes(struct ListBase *list); -void BKE_gpencil_free_palettes(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd, bool free_palettes); +bool BKE_gpencil_free_frame_runtime_data(struct bGPDframe *derived_gpf); +void BKE_gpencil_free_derived_frames(struct bGPdata *gpd); +void BKE_gpencil_free(struct bGPdata *gpd, bool free_all); + +void BKE_gpencil_batch_cache_dirty(struct bGPdata *gpd); +void BKE_gpencil_batch_cache_free(struct bGPdata *gpd); void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps); @@ -60,21 +80,36 @@ struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]) struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src); struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); +void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst); +struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src); + void BKE_gpencil_copy_data(struct Main *bmain, struct bGPdata *gpd_dst, const struct bGPdata *gpd_src, const int flag); +struct bGPdata *BKE_gpencil_copy(struct Main *bmain, const struct bGPdata *gpd); struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, const struct bGPdata *gpd, bool internal_copy); void BKE_gpencil_make_local(struct Main *bmain, struct bGPdata *gpd, const bool lib_local); void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); -struct bGPDpalette *BKE_gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive); -struct bGPDpalette *BKE_gpencil_palette_duplicate(const struct bGPDpalette *palette_src); -struct bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive); +/* materials */ +void BKE_gpencil_material_index_remove(struct bGPdata *gpd, int index); +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len); -struct bGPDbrush *BKE_gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive); -struct bGPDbrush *BKE_gpencil_brush_duplicate(const struct bGPDbrush *brush_src); -void BKE_gpencil_brush_init_presets(struct ToolSettings *ts); +/* statistics functions */ +void BKE_gpencil_stats_update(struct bGPdata *gpd); +/* Utilities for creating and populating GP strokes */ +/* - Number of values defining each point in the built-in data + * buffers for primitives (e.g. 2D Monkey) + */ +#define GP_PRIM_DATABUF_SIZE 5 + +void BKE_gpencil_stroke_add_points( + struct bGPDstroke *gps, + const float *array, const int totpoints, + const float mat[4][4]); + +struct bGPDstroke *BKE_gpencil_add_stroke(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness); /* Stroke and Fill - Alpha Visibility Threshold */ #define GPENCIL_ALPHA_OPACITY_THRESH 0.001f @@ -103,20 +138,40 @@ struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd); void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); -struct bGPDbrush *BKE_gpencil_brush_getactive(struct ToolSettings *ts); -void BKE_gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active); -void BKE_gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *brush); +struct Material *BKE_gpencil_get_material_from_brush(struct Brush *brush); +struct Material *BKE_gpencil_material_ensure(struct Main *bmain, struct Object *ob); -struct bGPDpalette *BKE_gpencil_palette_getactive(struct bGPdata *gpd); -void BKE_gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active); -void BKE_gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette); -void BKE_gpencil_palette_change_strokes(struct bGPdata *gpd); +/* object boundbox */ +bool BKE_gpencil_stroke_minmax( + const struct bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]); -struct bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(struct bGPDpalette *palette); -void BKE_gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active); -void BKE_gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor); -struct bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name); -void BKE_gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname); -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name); +struct BoundBox *BKE_gpencil_boundbox_get(struct Object *ob); +void BKE_gpencil_centroid_3D(struct bGPdata *gpd, float r_centroid[3]); + +/* vertex groups */ +float BKE_gpencil_vgroup_use_index(struct MDeformVert *dvert, int index); +void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup); +struct MDeformWeight *BKE_gpencil_vgroup_add_point_weight(struct MDeformVert *dvert, int index, float weight); +bool BKE_gpencil_vgroup_remove_point_weight(struct MDeformVert *dvert, int index); +void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst); + +/* GPencil geometry evaluation */ +void BKE_gpencil_eval_geometry(struct Depsgraph *depsgraph, struct bGPdata *gpd); + +/* stroke geometry utilities */ +void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]); +void BKE_gpencil_simplify_stroke(struct bGPDstroke *gps, float factor); +void BKE_gpencil_simplify_fixed(struct bGPDstroke *gps); + +void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]); + +bool BKE_gpencil_smooth_stroke(struct bGPDstroke *gps, int i, float inf); +bool BKE_gpencil_smooth_stroke_strength(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index, float influence); +bool BKE_gpencil_smooth_stroke_uv(struct bGPDstroke *gps, int point_index, float influence); + +void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe); +float BKE_gpencil_multiframe_falloff_calc(struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff); #endif /* __BKE_GPENCIL_H__ */ diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h new file mode 100644 index 00000000000..cd6b6540012 --- /dev/null +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -0,0 +1,256 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is: all of this file. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_GPENCIL_MODIFIER_H__ +#define __BKE_GPENCIL_MODIFIER_H__ + +/** \file BKE_greasepencil_modifier.h + * \ingroup bke + */ + +#include "DNA_gpencil_modifier_types.h" /* needed for all enum typdefs */ +#include "BLI_compiler_attrs.h" +#include "BKE_customdata.h" + +struct ID; +struct Depsgraph; +struct DerivedMesh; +struct bContext; /* NOTE: bakeModifier() - called from UI - needs to create new datablocks, hence the need for this */ +struct Mesh; +struct Object; +struct Scene; +struct ViewLayer; +struct ListBase; +struct bArmature; +struct Main; +struct GpencilModifierData; +struct BMEditMesh; +struct DepsNodeHandle; +struct bGPDlayer; +struct bGPDframe; +struct bGPDstroke; +struct ModifierUpdateDepsgraphContext; + +#define GPENCIL_MODIFIER_ACTIVE(_md, _is_render) (((_md->mode & eGpencilModifierMode_Realtime) && (_is_render == false)) || \ + ((_md->mode & eGpencilModifierMode_Render) && (_is_render == true))) +#define GPENCIL_MODIFIER_EDIT(_md, _is_edit) (((_md->mode & eGpencilModifierMode_Editmode) == 0) && (_is_edit)) + +typedef enum { + /* Should not be used, only for None modifier type */ + eGpencilModifierTypeType_None, + + /* grease pencil modifiers */ + eGpencilModifierTypeType_Gpencil, +} GpencilModifierTypeType; + +typedef enum { + eGpencilModifierTypeFlag_SupportsMapping = (1 << 0), + eGpencilModifierTypeFlag_SupportsEditmode = (1 << 1), + + /* For modifiers that support editmode this determines if the + * modifier should be enabled by default in editmode. This should + * only be used by modifiers that are relatively speedy and + * also generally used in editmode, otherwise let the user enable + * it by hand. + */ + eGpencilModifierTypeFlag_EnableInEditmode = (1 << 2), + + /* For modifiers that require original data and so cannot + * be placed after any non-deformative modifier. + */ + eGpencilModifierTypeFlag_RequiresOriginalData = (1 << 3), + + /* max one per type */ + eGpencilModifierTypeFlag_Single = (1 << 4), + + /* can't be added manually by user */ + eGpencilModifierTypeFlag_NoUserAdd = (1 << 5), +} GpencilModifierTypeFlag; + +/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ +typedef void(*GreasePencilObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag); +typedef void(*GreasePencilIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); +typedef void(*GreasePencilTexWalkFunc)(void *userData, struct Object *ob, struct GpencilModifierData *md, const char *propname); + +typedef struct GpencilModifierTypeInfo { + /* The user visible name for this modifier */ + char name[32]; + + /* The DNA struct name for the modifier data type, used to + * write the DNA data out. + */ + char struct_name[32]; + + /* The size of the modifier data type, used by allocation. */ + int struct_size; + + GpencilModifierType type; + GpencilModifierTypeFlag flags; + + + /********************* Non-optional functions *********************/ + + /* Copy instance data for this modifier type. Should copy all user + * level settings to the target modifier. + */ + void (*copyData)(const struct GpencilModifierData *md, struct GpencilModifierData *target); + + /* Callback for GP "stroke" modifiers that operate on the + * shape and parameters of the provided strokes (e.g. Thickness, Noise, etc.) + * + * The gpl parameter contains the GP layer that the strokes come from. + * While access is provided to this data, you should not directly access + * the gpl->frames data from the modifier. Instead, use the gpf parameter + * instead. + * + * The gps parameter contains the GP stroke to operate on. This is usually a copy + * of the original (unmodified and saved to files) stroke data. + */ + void (*deformStroke)(struct GpencilModifierData *md, struct Depsgraph *depsgraph, + struct Object *ob, struct bGPDlayer *gpl, struct bGPDstroke *gps); + + /* Callback for GP "geometry" modifiers that create extra geometry + * in the frame (e.g. Array) + * + * The gpf parameter contains the GP frame/strokes to operate on. This is + * usually a copy of the original (unmodified and saved to files) stroke data. + * Modifiers should only add any generated strokes to this frame (and not one accessed + * via the gpl parameter). + * + * The modifier_index parameter indicates where the modifier is + * in the modifier stack in relation to other modifiers. + */ + void (*generateStrokes)(struct GpencilModifierData *md, struct Depsgraph *depsgraph, + struct Object *ob, struct bGPDlayer *gpl, struct bGPDframe *gpf); + + /* Bake-down GP modifier's effects into the GP datablock. + * + * This gets called when the user clicks the "Apply" button in the UI. + * As such, this callback needs to go through all layers/frames in the + * datablock, mutating the geometry and/or creating new datablocks/objects + */ + void (*bakeModifier)(struct Main *bmain, struct Depsgraph *depsgraph, + struct GpencilModifierData *md, struct Object *ob); + + /********************* Optional functions *********************/ + + /* Initialize new instance data for this modifier type, this function + * should set modifier variables to their default values. + * + * This function is optional. + */ + void (*initData)(struct GpencilModifierData *md); + + /* Free internal modifier data variables, this function should + * not free the md variable itself. + * + * This function is optional. + */ + void (*freeData)(struct GpencilModifierData *md); + + /* Return a boolean value indicating if this modifier is able to be + * calculated based on the modifier data. This is *not* regarding the + * md->flag, that is tested by the system, this is just if the data + * validates (for example, a lattice will return false if the lattice + * object is not defined). + * + * This function is optional (assumes never disabled if not present). + */ + bool (*isDisabled)(struct GpencilModifierData *md, int userRenderParams); + + /* Add the appropriate relations to the dependency graph. + * + * This function is optional. + */ + void (*updateDepsgraph)(struct GpencilModifierData *md, + const struct ModifierUpdateDepsgraphContext *ctx); + + /* Should return true if the modifier needs to be recalculated on time + * changes. + * + * This function is optional (assumes false if not present). + */ + bool (*dependsOnTime)(struct GpencilModifierData *md); + + + /* Should call the given walk function on with a pointer to each Object + * pointer that the modifier data stores. This is used for linking on file + * load and for unlinking objects or forwarding object references. + * + * This function is optional. + */ + void (*foreachObjectLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilObjectWalkFunc walk, void *userData); + + /* Should call the given walk function with a pointer to each ID + * pointer (i.e. each datablock pointer) that the modifier data + * stores. This is used for linking on file load and for + * unlinking datablocks or forwarding datablock references. + * + * This function is optional. If it is not present, foreachObjectLink + * will be used. + */ + void (*foreachIDLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilIDWalkFunc walk, void *userData); + + /* Should call the given walk function for each texture that the + * modifier data stores. This is used for finding all textures in + * the context for the UI. + * + * This function is optional. If it is not present, it will be + * assumed the modifier has no textures. + */ + void (*foreachTexLink)(struct GpencilModifierData *md, struct Object *ob, + GreasePencilTexWalkFunc walk, void *userData); +} GpencilModifierTypeInfo; + +void BKE_gpencil_instance_modifier_instance_tfm(struct InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]); + +/* Initialize modifier's global data (type info and some common global storages). */ +void BKE_gpencil_modifier_init(void); + +const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type); +struct GpencilModifierData *BKE_gpencil_modifier_new(int type); +void BKE_gpencil_modifier_free_ex(struct GpencilModifierData *md, const int flag); +void BKE_gpencil_modifier_free(struct GpencilModifierData *md); +bool BKE_gpencil_modifier_unique_name(struct ListBase *modifiers, struct GpencilModifierData *gmd); +bool BKE_gpencil_modifier_dependsOnTime(struct GpencilModifierData *md); +struct GpencilModifierData *BKE_gpencil_modifiers_findByType(struct Object *ob, GpencilModifierType type); +struct GpencilModifierData *BKE_gpencil_modifiers_findByName(struct Object *ob, const char *name); +void BKE_gpencil_modifier_copyData_generic(const struct GpencilModifierData *md_src, struct GpencilModifierData *md_dst); +void BKE_gpencil_modifier_copyData(struct GpencilModifierData *md, struct GpencilModifierData *target); +void BKE_gpencil_modifier_copyData_ex(struct GpencilModifierData *md, struct GpencilModifierData *target, const int flag); +void BKE_gpencil_modifiers_foreachIDLink(struct Object *ob, GreasePencilIDWalkFunc walk, void *userData); +void BKE_gpencil_modifiers_foreachTexLink(struct Object *ob, GreasePencilTexWalkFunc walk, void *userData); + +bool BKE_gpencil_has_geometry_modifiers(struct Object *ob); + +void BKE_gpencil_stroke_modifiers( + struct Depsgraph *depsgraph, struct Object *ob, + struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, bool is_render); +void BKE_gpencil_geometry_modifiers( + struct Depsgraph *depsgraph, struct Object *ob, + struct bGPDlayer *gpl, struct bGPDframe *gpf, bool is_render); + +void BKE_gpencil_lattice_init(struct Object *ob); +void BKE_gpencil_lattice_clear(struct Object *ob); + +#endif /* __BKE_GPENCIL_MODIFIER_H__ */ diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index 22897d2ea80..7a5262e0a14 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -43,7 +43,10 @@ enum { ICON_DATA_PREVIEW, /** 2D triangles: obj is #Icon_Geom */ ICON_DATA_GEOM, + /** Studiolight */ ICON_DATA_STUDIOLIGHT, + /** GPencil Layer color preview (annotations): obj is #bGPDlayer */ + ICON_DATA_GPLAYER, }; struct Icon { @@ -79,6 +82,7 @@ struct ImBuf; struct PreviewImage; struct ID; struct StudioLight; +struct bGPDlayer; enum eIconSizes; @@ -87,6 +91,9 @@ void BKE_icons_init(int first_dyn_id); /* return icon id for library object or create new icon if not found */ int BKE_icon_id_ensure(struct ID *id); +/* return icon id for Grease Pencil layer (color preview) or create new icon if not found */ +int BKE_icon_gplayer_color_ensure(struct bGPDlayer *gpl); + int BKE_icon_preview_ensure(struct ID *id, struct PreviewImage *preview); /* retrieve icon for id */ diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h index c2ac5e98f76..67e6a32edfd 100644 --- a/source/blender/blenkernel/BKE_lattice.h +++ b/source/blender/blenkernel/BKE_lattice.h @@ -54,7 +54,6 @@ void BKE_lattice_free(struct Lattice *lt); void BKE_lattice_make_local(struct Main *bmain, struct Lattice *lt, const bool lib_local); void calc_lat_fudu(int flag, int res, float *r_fu, float *r_du); -struct LatticeDeformData; struct LatticeDeformData *init_latt_deform(struct Object *oblatt, struct Object *ob) ATTR_WARN_UNUSED_RESULT; void calc_latt_deform(struct LatticeDeformData *lattice_deform_data, float co[3], float weight); void end_latt_deform(struct LatticeDeformData *lattice_deform_data); diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index c85017a2216..1ca8928c61d 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -54,11 +54,13 @@ void BKE_material_init(struct Material *ma); void BKE_material_remap_object(struct Object *ob, const unsigned int *remap); void BKE_material_remap_object_calc(struct Object *ob_dst, struct Object *ob_src, short *remap_src_to_dst); struct Material *BKE_material_add(struct Main *bmain, const char *name); +struct Material *BKE_material_add_gpencil(struct Main *bmain, const char *name); void BKE_material_copy_data(struct Main *bmain, struct Material *ma_dst, const struct Material *ma_src, const int flag); struct Material *BKE_material_copy(struct Main *bmain, const struct Material *ma); struct Material *BKE_material_localize(struct Material *ma); struct Material *give_node_material(struct Material *ma); /* returns node material or self */ void BKE_material_make_local(struct Main *bmain, struct Material *ma, const bool lib_local); +void BKE_material_init_gpencil_settings(struct Material *ma); /* UNUSED */ // void automatname(struct Material *); @@ -87,6 +89,8 @@ short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob); bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob); +struct MaterialGPencilStyle *BKE_material_gpencil_settings_get(struct Object *ob, short act); + void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma); void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 79e4f1d448a..7d795c25a04 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -37,8 +37,11 @@ extern "C" { struct Base; struct Depsgraph; +struct GpencilModifierData; struct Scene; +struct ShaderFxData; struct ViewLayer; +struct ID; struct Object; struct BoundBox; struct View3D; @@ -49,6 +52,7 @@ struct Mesh; struct RigidBodyWorld; struct HookModifierData; struct ModifierData; +struct HookGpencilModifierData; #include "DNA_object_enums.h" @@ -69,11 +73,16 @@ void BKE_object_free_derived_mesh_caches(struct Object *ob); void BKE_object_free_caches(struct Object *object); void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd); +void BKE_object_modifier_gpencil_hook_reset(struct Object *ob, struct HookGpencilModifierData *hmd); +bool BKE_object_modifier_gpencil_use_time(struct Object *ob, struct GpencilModifierData *md); + +bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md); bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type); void BKE_object_link_modifiers(struct Scene *scene, struct Object *ob_dst, const struct Object *ob_src); void BKE_object_free_modifiers(struct Object *ob, const int flag); +void BKE_object_free_shaderfx(struct Object *ob, const int flag); void BKE_object_make_proxy(struct Main *bmain, struct Object *ob, struct Object *target, struct Object *gob); void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); @@ -108,6 +117,9 @@ struct Object *BKE_object_add_from( struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, int type, const char *name, struct Object *ob_src) ATTR_NONNULL(1, 2, 3, 6) ATTR_RETURNS_NONNULL; +struct Object *BKE_object_add_for_data( + struct Main *bmain, struct ViewLayer *view_layer, + int type, const char *name, struct ID *data, bool do_id_user) ATTR_RETURNS_NONNULL; void *BKE_object_obdata_add_from_type( struct Main *bmain, int type, const char *name) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 6ade14b275c..c440a634c9f 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -77,7 +77,8 @@ typedef enum ePaintMode { ePaintTextureProjective = 3, ePaintTexture2D = 4, ePaintSculptUV = 5, - ePaintInvalid = 6 + ePaintInvalid = 6, + ePaintGpencil = 7 } ePaintMode; /* overlay invalidation */ diff --git a/source/blender/blenkernel/BKE_shader_fx.h b/source/blender/blenkernel/BKE_shader_fx.h new file mode 100644 index 00000000000..11c5983106a --- /dev/null +++ b/source/blender/blenkernel/BKE_shader_fx.h @@ -0,0 +1,180 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is: all of this file. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_SHADER_FX_H__ +#define __BKE_SHADER_FX_H__ + +/** \file BKE_shader_fx.h + * \ingroup bke + */ + +#include "DNA_shader_fx_types.h" /* needed for all enum typdefs */ +#include "BLI_compiler_attrs.h" +#include "BKE_customdata.h" + +struct ID; +struct Depsgraph; +struct DerivedMesh; +struct Mesh; +struct Object; +struct Scene; +struct ViewLayer; +struct ListBase; +struct bArmature; +struct Main; +struct ShaderFxData; +struct DepsNodeHandle; +struct bGPDlayer; +struct bGPDframe; +struct bGPDstroke; +struct ModifierUpdateDepsgraphContext; + +#define SHADER_FX_ACTIVE(_fx, _is_render) (((_fx->mode & eShaderFxMode_Realtime) && (_is_render == false)) || \ + ((_fx->mode & eShaderFxMode_Render) && (_is_render == true))) +#define SHADER_FX_EDIT(_fx, _is_edit) (((_fx->mode & eShaderFxMode_Editmode) == 0) && (_is_edit)) + +typedef enum { + /* Should not be used, only for None type */ + eShaderFxType_NoneType, + + /* grease pencil effects */ + eShaderFxType_GpencilType, +} ShaderFxTypeType; + +typedef enum { + eShaderFxTypeFlag_SupportsEditmode = (1 << 0), + + /* For effects that support editmode this determines if the + * effect should be enabled by default in editmode. + */ + eShaderFxTypeFlag_EnableInEditmode = (1 << 2), + + /* max one per type */ + eShaderFxTypeFlag_Single = (1 << 4), + + /* can't be added manually by user */ + eShaderFxTypeFlag_NoUserAdd = (1 << 5), +} ShaderFxTypeFlag; + +/* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ +typedef void(*ShaderFxObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag); +typedef void(*ShaderFxIDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); +typedef void(*ShaderFxTexWalkFunc)(void *userData, struct Object *ob, struct ShaderFxData *fx, const char *propname); + +typedef struct ShaderFxTypeInfo { + /* The user visible name for this effect */ + char name[32]; + + /* The DNA struct name for the effect data type, used to + * write the DNA data out. + */ + char struct_name[32]; + + /* The size of the effect data type, used by allocation. */ + int struct_size; + + ShaderFxTypeType type; + ShaderFxTypeFlag flags; + + /* Copy instance data for this effect type. Should copy all user + * level settings to the target effect. + */ + void(*copyData)(const struct ShaderFxData *fx, struct ShaderFxData *target); + + /* Initialize new instance data for this effect type, this function + * should set effect variables to their default values. + * + * This function is optional. + */ + void (*initData)(struct ShaderFxData *fx); + + /* Free internal effect data variables, this function should + * not free the fx variable itself. + * + * This function is optional. + */ + void (*freeData)(struct ShaderFxData *fx); + + /* Return a boolean value indicating if this effect is able to be + * calculated based on the effect data. This is *not* regarding the + * fx->flag, that is tested by the system, this is just if the data + * validates (for example, a lattice will return false if the lattice + * object is not defined). + * + * This function is optional (assumes never disabled if not present). + */ + bool (*isDisabled)(struct ShaderFxData *fx, int userRenderParams); + + /* Add the appropriate relations to the dependency graph. + * + * This function is optional. + */ + void (*updateDepsgraph)(struct ShaderFxData *fx, + const struct ModifierUpdateDepsgraphContext *ctx); + + /* Should return true if the effect needs to be recalculated on time + * changes. + * + * This function is optional (assumes false if not present). + */ + bool (*dependsOnTime)(struct ShaderFxData *fx); + + + /* Should call the given walk function on with a pointer to each Object + * pointer that the effect data stores. This is used for linking on file + * load and for unlinking objects or forwarding object references. + * + * This function is optional. + */ + void (*foreachObjectLink)(struct ShaderFxData *fx, struct Object *ob, + ShaderFxObjectWalkFunc walk, void *userData); + + /* Should call the given walk function with a pointer to each ID + * pointer (i.e. each datablock pointer) that the effect data + * stores. This is used for linking on file load and for + * unlinking datablocks or forwarding datablock references. + * + * This function is optional. If it is not present, foreachObjectLink + * will be used. + */ + void (*foreachIDLink)(struct ShaderFxData *fx, struct Object *ob, + ShaderFxIDWalkFunc walk, void *userData); +} ShaderFxTypeInfo; + +/* Initialize global data (type info and some common global storages). */ +void BKE_shaderfx_init(void); + +const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type); +struct ShaderFxData *BKE_shaderfx_new(int type); +void BKE_shaderfx_free_ex(struct ShaderFxData *fx, const int flag); +void BKE_shaderfx_free(struct ShaderFxData *fx); +bool BKE_shaderfx_unique_name(struct ListBase *shaderfx, struct ShaderFxData *fx); +bool BKE_shaderfx_dependsOnTime(struct ShaderFxData *fx); +struct ShaderFxData *BKE_shaderfx_findByType(struct Object *ob, ShaderFxType type); +struct ShaderFxData *BKE_shaderfx_findByName(struct Object *ob, const char *name); +void BKE_shaderfx_copyData_generic(const struct ShaderFxData *fx_src, struct ShaderFxData *fx_dst); +void BKE_shaderfx_copyData(struct ShaderFxData *fx, struct ShaderFxData *target); +void BKE_shaderfx_copyData_ex(struct ShaderFxData *fx, struct ShaderFxData *target, const int flag); +void BKE_shaderfx_foreachIDLink(struct Object *ob, ShaderFxIDWalkFunc walk, void *userData); + +bool BKE_shaderfx_has_gpencil(struct Object *ob); + +#endif /* __BKE_SHADER_FX_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 01910bffdb0..7169597f100 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -38,6 +38,8 @@ set(INC ../makesrna ../bmesh ../modifiers + ../gpencil_modifiers + ../shader_fx ../nodes ../physics ../render/extern/include @@ -115,6 +117,7 @@ set(SRC intern/font.c intern/freestyle.c intern/gpencil.c + intern/gpencil_modifier.c intern/icons.c intern/icons_rasterize.c intern/idcode.c @@ -180,6 +183,7 @@ set(SRC intern/seqeffects.c intern/seqmodifier.c intern/sequencer.c + intern/shader_fx.c intern/shrinkwrap.c intern/smoke.c intern/softbody.c @@ -259,6 +263,7 @@ set(SRC BKE_freestyle.h BKE_global.h BKE_gpencil.h + BKE_gpencil_modifier.h BKE_icons.h BKE_idcode.h BKE_idprop.h @@ -306,6 +311,7 @@ set(SRC BKE_scene.h BKE_screen.h BKE_sequencer.h + BKE_shader_fx.h BKE_shrinkwrap.h BKE_smoke.h BKE_softbody.h diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index fd7497f9ba1..7dfedfe6c06 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -104,6 +104,7 @@ bool id_type_can_have_animdata(const short id_type) case ID_MSK: case ID_GD: case ID_CF: + case ID_PAL: return true; /* no AnimData */ @@ -1150,6 +1151,9 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use /* grease pencil */ ANIMDATA_IDS_CB(bmain->gpencil.first); + /* palettes */ + ANIMDATA_IDS_CB(bmain->palettes.first); + /* cache files */ ANIMDATA_IDS_CB(bmain->cachefiles.first); } @@ -2925,6 +2929,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Depsgraph *depsgraph, Scene /* grease pencil */ EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM); + /* palettes */ + EVAL_ANIM_IDS(main->palettes.first, ADT_RECALC_ANIM); + /* cache files */ EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 42cd7968321..598eb9b5b54 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -29,6 +29,7 @@ #include "DNA_brush_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" @@ -36,6 +37,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" +#include "BKE_context.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_library_query.h" @@ -129,6 +131,7 @@ static void brush_defaults(Brush *brush) brush->stencil_dimension[0] = 256; brush->stencil_dimension[1] = 256; + } /* Datablock add/copy/free/make_local */ @@ -164,6 +167,368 @@ Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode) return brush; } +/* add a new gp-brush */ +Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name) +{ + Brush *brush; + Paint *paint = BKE_brush_get_gpencil_paint(ts); + brush = BKE_brush_add(bmain, name, OB_MODE_GPENCIL_PAINT); + + BKE_paint_brush_set(paint, brush); + id_us_min(&brush->id); + + /* grease pencil basic settings */ + brush->size = 3; + + brush->gpencil_settings = MEM_callocN(sizeof(BrushGpencilSettings), "BrushGpencilSettings"); + + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->flag = 0; + brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + + /* curves */ + brush->gpencil_settings->curve_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->gpencil_settings->curve_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + /* return brush */ + return brush; +} + +Paint *BKE_brush_get_gpencil_paint(ToolSettings *ts) +{ + /* alloc paint session */ + if (ts->gp_paint == NULL) { + ts->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + } + + return &ts->gp_paint->paint; +} + +/* grease pencil cumapping->preset */ +typedef enum eGPCurveMappingPreset { + GPCURVE_PRESET_PENCIL = 0, + GPCURVE_PRESET_INK = 1, + GPCURVE_PRESET_INKNOISE = 2, +} eGPCurveMappingPreset; + +static void brush_gpencil_curvemap_reset(CurveMap *cuma, int preset) +{ + if (cuma->curve) + MEM_freeN(cuma->curve); + + cuma->totpoint = 3; + cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), __func__); + + switch (preset) { + case GPCURVE_PRESET_PENCIL: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.75115f; + cuma->curve[1].y = 0.25f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INK: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63448f; + cuma->curve[1].y = 0.375f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_INKNOISE: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.63134f; + cuma->curve[1].y = 0.3625f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + } + + if (cuma->table) { + MEM_freeN(cuma->table); + cuma->table = NULL; + } +} + +/* create a set of grease pencil presets */ +void BKE_brush_gpencil_presets(bContext *C) +{ +#define SMOOTH_STROKE_RADIUS 40 +#define SMOOTH_STROKE_FACTOR 0.9f + + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = BKE_brush_get_gpencil_paint(ts); + Main *bmain = CTX_data_main(C); + + Brush *brush, *deft; + CurveMapping *custom_curve; + + /* Pencil brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pencil"); + brush->size = 25.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.6f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Pen brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Pen"); + deft = brush; /* save default brush */ + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Ink brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Ink"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.6f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INK); + + /* Ink Noise brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Noise"); + brush->size = 60.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.7f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 1.0f; + brush->gpencil_settings->draw_smoothlvl = 2; + brush->gpencil_settings->thick_smoothfac = 0.5f; + brush->gpencil_settings->thick_smoothlvl = 2; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_INKNOISE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Curve */ + custom_curve = brush->gpencil_settings->curve_sensitivity; + curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, GPCURVE_PRESET_INKNOISE); + + /* Block Basic brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Block"); + brush->size = 150.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 0.7f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->gpencil_settings->draw_random_press = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = 0.0f; + brush->gpencil_settings->draw_angle_factor = 0.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.0f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 0; + brush->gpencil_settings->draw_random_sub = 0; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_BLOCK; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Marker brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Draw Marker"); + brush->size = 80.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_USE_PRESSURE | GP_BRUSH_ENABLE_CURSOR); + brush->gpencil_settings->draw_sensitivity = 1.0f; + + brush->gpencil_settings->draw_strength = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM; + brush->gpencil_settings->draw_random_press = 0.374f; + brush->gpencil_settings->draw_random_strength = 0.0f; + + brush->gpencil_settings->draw_jitter = 0.0f; + brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->gpencil_settings->draw_angle = M_PI_4; /* 45 degrees */ + brush->gpencil_settings->draw_angle_factor = 1.0f; + + brush->gpencil_settings->flag |= GP_BRUSH_GROUP_SETTINGS; + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + brush->gpencil_settings->draw_random_sub = 0.0f; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_MARKER; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_DRAW; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + /* Fill brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Fill Area"); + brush->size = 1.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->draw_sensitivity = 1.0f; + brush->gpencil_settings->fill_leak = 3; + brush->gpencil_settings->fill_threshold = 0.1f; + brush->gpencil_settings->fill_simplylvl = 1; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_FILL; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_FILL; + + brush->gpencil_settings->draw_smoothfac = 0.5f; + brush->gpencil_settings->draw_smoothlvl = 1; + brush->gpencil_settings->thick_smoothfac = 1.0f; + brush->gpencil_settings->thick_smoothlvl = 3; + brush->gpencil_settings->draw_subdivide = 1; + + brush->smooth_stroke_radius = SMOOTH_STROKE_RADIUS; + brush->smooth_stroke_factor = SMOOTH_STROKE_FACTOR; + + brush->gpencil_settings->draw_strength = 1.0f; + + /* Soft Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Soft"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; + + /* Hard Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Hard"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_HARD; + + /* Stroke Eraser brush */ + brush = BKE_brush_add_gpencil(bmain, ts, "Eraser Stroke"); + brush->size = 30.0f; + brush->gpencil_settings->flag |= GP_BRUSH_ENABLE_CURSOR; + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; + brush->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_STROKE; + + /* set defaut brush */ + BKE_paint_brush_set(paint, deft); + +} + +/* get the active gp-brush for editing */ +Brush *BKE_brush_getactive_gpencil(ToolSettings *ts) +{ + /* error checking */ + if (ELEM(NULL, ts, ts->gp_paint)) { + return NULL; + } + Paint *paint = &ts->gp_paint->paint; + + return paint->brush; +} + struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mode) { Brush *brush; @@ -197,6 +562,12 @@ void BKE_brush_copy_data(Main *UNUSED(bmain), Brush *brush_dst, const Brush *bru } brush_dst->curve = curvemapping_copy(brush_src->curve); + if (brush_src->gpencil_settings != NULL) { + brush_dst->gpencil_settings = MEM_dupallocN(brush_src->gpencil_settings); + brush_dst->gpencil_settings->curve_sensitivity = curvemapping_copy(brush_src->gpencil_settings->curve_sensitivity); + brush_dst->gpencil_settings->curve_strength = curvemapping_copy(brush_src->gpencil_settings->curve_strength); + brush_dst->gpencil_settings->curve_jitter = curvemapping_copy(brush_src->gpencil_settings->curve_jitter); + } /* enable fake user by default */ id_fake_user_set(&brush_dst->id); @@ -215,11 +586,18 @@ void BKE_brush_free(Brush *brush) if (brush->icon_imbuf) { IMB_freeImBuf(brush->icon_imbuf); } - curvemapping_free(brush->curve); + if (brush->gpencil_settings != NULL) { + curvemapping_free(brush->gpencil_settings->curve_sensitivity); + curvemapping_free(brush->gpencil_settings->curve_strength); + curvemapping_free(brush->gpencil_settings->curve_jitter); + MEM_SAFE_FREE(brush->gpencil_settings); + } + MEM_SAFE_FREE(brush->gradient); + BKE_previewimg_free(&(brush->preview)); } diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index ff4795afe87..d18572a57f6 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -282,6 +282,7 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) case CURVE_PRESET_MID9: cuma->totpoint = 9; break; case CURVE_PRESET_ROUND: cuma->totpoint = 4; break; case CURVE_PRESET_ROOT: cuma->totpoint = 4; break; + case CURVE_PRESET_GAUSS: cuma->totpoint = 7; break; } cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points"); @@ -352,6 +353,24 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope) cuma->curve[3].x = 1; cuma->curve[3].y = 0; break; + case CURVE_PRESET_GAUSS: + cuma->curve[0].x = 0; + cuma->curve[0].y = 0.025f; + cuma->curve[1].x = 0.16f; + cuma->curve[1].y = 0.135f; + cuma->curve[2].x = 0.298f; + cuma->curve[2].y = 0.36f; + + cuma->curve[3].x = 0.50f; + cuma->curve[3].y = 1.0f; + + cuma->curve[4].x = 0.70f; + cuma->curve[4].y = 0.36f; + cuma->curve[5].x = 0.84f; + cuma->curve[5].y = 0.135f; + cuma->curve[6].x = 1.0f; + cuma->curve[6].y = 0.025f; + break; } /* mirror curve in x direction to have positive slope diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 3dfe9732062..84ca143dc55 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1014,6 +1014,10 @@ int CTX_data_mode_enum_ex(const Object *obedit, const Object *ob, const eObjectM else if (object_mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX; else if (object_mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE; else if (object_mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE; + else if (object_mode & OB_MODE_GPENCIL_PAINT) return CTX_MODE_GPENCIL_PAINT; + else if (object_mode & OB_MODE_GPENCIL_EDIT) return CTX_MODE_GPENCIL_EDIT; + else if (object_mode & OB_MODE_GPENCIL_SCULPT) return CTX_MODE_GPENCIL_SCULPT; + else if (object_mode & OB_MODE_GPENCIL_WEIGHT) return CTX_MODE_GPENCIL_WEIGHT; } } @@ -1044,6 +1048,10 @@ static const char *data_mode_strings[] = { "imagepaint", "particlemode", "objectmode", + "greasepencil_paint", + "greasepencil_edit", + "greasepencil_sculpt", + "greasepencil_weight", NULL }; BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1, "Must have a string for each context mode") @@ -1212,17 +1220,7 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C) return ctx_data_pointer_get(C, "active_gpencil_layer"); } -bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palette"); -} - -bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C) -{ - return ctx_data_pointer_get(C, "active_gpencil_palettecolor"); -} - -bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C) +Brush *CTX_data_active_gpencil_brush(const bContext *C) { return ctx_data_pointer_get(C, "active_gpencil_brush"); } diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index d08e3643ca7..ddf9840a32e 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -74,7 +74,9 @@ bDeformGroup *BKE_defgroup_new(Object *ob, const char *name) BLI_addtail(&ob->defbase, defgroup); defgroup_unique_name(defgroup, ob); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + if (ob->type != OB_GPENCIL) { + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } return defgroup; } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index e89508fd6c0..de3f891f9f9 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -39,26 +39,83 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_math_color.h" #include "BLI_string_utils.h" +#include "BLI_rand.h" +#include "BLI_ghash.h" #include "BLT_translation.h" +#include "DNA_anim_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_material_types.h" #include "DNA_gpencil_types.h" #include "DNA_userdef_types.h" #include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "BKE_context.h" +#include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_colortools.h" +#include "BKE_icons.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_material.h" +#include "DEG_depsgraph.h" /* ************************************************** */ -/* GENERAL STUFF */ +/* Draw Engine */ -/* --------- Memory Management ------------ */ +void(*BKE_gpencil_batch_cache_dirty_cb)(bGPdata *gpd) = NULL; +void(*BKE_gpencil_batch_cache_free_cb)(bGPdata *gpd) = NULL; + +void BKE_gpencil_batch_cache_dirty(bGPdata *gpd) +{ + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + BKE_gpencil_batch_cache_dirty_cb(gpd); + } +} + +void BKE_gpencil_batch_cache_free(bGPdata *gpd) +{ + if (gpd) { + BKE_gpencil_batch_cache_free_cb(gpd); + } +} + +/* ************************************************** */ +/* Memory Management */ + +/* clean vertex groups weights */ +void BKE_gpencil_free_point_weights(MDeformVert *dvert) +{ + if (dvert == NULL) { + return; + } + MEM_SAFE_FREE(dvert->dw); +} + +void BKE_gpencil_free_stroke_weights(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + if (gps->dvert == NULL) { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } +} /* free stroke, doesn't unlink from any listbase */ void BKE_gpencil_free_stroke(bGPDstroke *gps) @@ -66,10 +123,14 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps) if (gps == NULL) { return; } - /* free stroke memory arrays, then stroke itself */ - if (gps->points) + if (gps->points) { MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } if (gps->triangles) MEM_freeN(gps->triangles); @@ -92,6 +153,26 @@ bool BKE_gpencil_free_strokes(bGPDframe *gpf) return changed; } +/* Free strokes and colors belonging to a gp-frame */ +bool BKE_gpencil_free_frame_runtime_data(bGPDframe *derived_gpf) +{ + bGPDstroke *gps_next; + if (!derived_gpf) { + return false; + } + + /* free strokes */ + for (bGPDstroke *gps = derived_gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + BKE_gpencil_free_stroke(gps); + } + BLI_listbase_clear(&derived_gpf->strokes); + + MEM_SAFE_FREE(derived_gpf); + + return true; +} + /* Free all of a gp-layer's frames */ void BKE_gpencil_free_frames(bGPDlayer *gpl) { @@ -111,67 +192,7 @@ void BKE_gpencil_free_frames(bGPDlayer *gpl) gpl->actframe = NULL; } -/* Free all of a gp-colors */ -static void free_gpencil_colors(bGPDpalette *palette) -{ - /* error checking */ - if (palette == NULL) { - return; - } - /* free colors */ - BLI_freelistN(&palette->colors); -} - -/* Free all of the gp-palettes and colors */ -void BKE_gpencil_free_palettes(ListBase *list) -{ - bGPDpalette *palette_next; - - /* error checking */ - if (list == NULL) { - return; - } - - /* delete palettes */ - for (bGPDpalette *palette = list->first; palette; palette = palette_next) { - palette_next = palette->next; - /* free palette colors */ - free_gpencil_colors(palette); - - MEM_freeN(palette); - } - BLI_listbase_clear(list); -} - -/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ -void BKE_gpencil_free_brushes(ListBase *list) -{ - bGPDbrush *brush_next; - - /* error checking */ - if (list == NULL) { - return; - } - - /* delete brushes */ - for (bGPDbrush *brush = list->first; brush; brush = brush_next) { - brush_next = brush->next; - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); - } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); - } - - MEM_freeN(brush); - } - BLI_listbase_clear(list); -} /* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ void BKE_gpencil_free_layers(ListBase *list) @@ -191,21 +212,81 @@ void BKE_gpencil_free_layers(ListBase *list) } } -/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd, bool free_palettes) +/* clear all runtime derived data */ +static void BKE_gpencil_clear_derived(bGPDlayer *gpl) { - BKE_animdata_free(&gpd->id, false); + GHashIterator gh_iter; - /* free layers */ - BKE_gpencil_free_layers(&gpd->layers); + if (gpl->runtime.derived_data == NULL) { + return; + } - /* free palettes */ - if (free_palettes) { - BKE_gpencil_free_palettes(&gpd->palettes); + GHASH_ITER(gh_iter, gpl->runtime.derived_data) { + bGPDframe *gpf = (bGPDframe *)BLI_ghashIterator_getValue(&gh_iter); + if (gpf) { + BKE_gpencil_free_frame_runtime_data(gpf); + } } } -/* -------- Container Creation ---------- */ +/* Free all of the gp-layers temp data*/ +static void BKE_gpencil_free_layers_temp_data(ListBase *list) +{ + bGPDlayer *gpl_next; + + /* error checking */ + if (list == NULL) return; + /* delete layers */ + for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { + gpl_next = gpl->next; + BKE_gpencil_clear_derived(gpl); + + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + } +} + +/* Free temp gpf derived frames */ +void BKE_gpencil_free_derived_frames(bGPdata *gpd) +{ + /* error checking */ + if (gpd == NULL) return; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + BKE_gpencil_clear_derived(gpl); + + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + } +} + +/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ +void BKE_gpencil_free(bGPdata *gpd, bool free_all) +{ + /* clear animation data */ + BKE_animdata_free(&gpd->id, false); + + /* free layers */ + if (free_all) { + BKE_gpencil_free_layers_temp_data(&gpd->layers); + } + BKE_gpencil_free_layers(&gpd->layers); + + /* materials */ + MEM_SAFE_FREE(gpd->mat); + + /* free all data */ + if (free_all) { + /* clear cache */ + BKE_gpencil_batch_cache_free(gpd); + } +} + +/* ************************************************** */ +/* Container Creation */ /* add a new gp-frame to the given layer */ bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) @@ -329,28 +410,31 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti /* add to datablock */ BLI_addtail(&gpd->layers, gpl); - /* set basic settings */ - copy_v4_v4(gpl->color, U.gpencil_new_layer_col); - /* Since GPv2 thickness must be 0 */ - gpl->thickness = 0; + /* annotation vs GP Object behaviour is slightly different */ + if (gpd->flag & GP_DATA_ANNOTATIONS) { + /* set default color of new strokes for this layer */ + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); + gpl->opacity = 1.0f; - gpl->opacity = 1.0f; + /* set default thickness of new strokes for this layer */ + gpl->thickness = 3; - /* onion-skinning settings */ - if (gpd->flag & GP_DATA_SHOW_ONIONSKINS) - gpl->flag |= GP_LAYER_ONIONSKIN; - - gpl->flag |= (GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL); - - ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ - ARRAY_SET_ITEMS(gpl->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ - - /* high quality fill by default */ - gpl->flag |= GP_LAYER_HQ_FILL; + /* onion-skinning settings */ + gpl->onion_flag |= GP_LAYER_ONIONSKIN; + } + else { + /* thickness parameter represents "thickness change", not absolute thickness */ + gpl->thickness = 0; + gpl->opacity = 1.0f; + } /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); - BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + BLI_uniquename(&gpd->layers, gpl, + (gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"), + '.', + offsetof(bGPDlayer, info), + sizeof(gpl->info)); /* make this one the active one */ if (setactive) @@ -360,267 +444,6 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti return gpl; } -/* add a new gp-palette and make it the active */ -bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) -{ - bGPDpalette *palette; - - /* check that list is ok */ - if (gpd == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); - - /* add to datablock */ - BLI_addtail(&gpd->palettes, palette); - - /* set basic settings */ - /* auto-name */ - BLI_strncpy(palette->info, name, sizeof(palette->info)); - BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), - sizeof(palette->info)); - - /* make this one the active one */ - /* NOTE: Always make this active if there's nothing else yet (T50123) */ - if ((setactive) || (gpd->palettes.first == gpd->palettes.last)) { - BKE_gpencil_palette_setactive(gpd, palette); - } - - /* return palette */ - return palette; -} - -/* create a set of default drawing brushes with predefined presets */ -void BKE_gpencil_brush_init_presets(ToolSettings *ts) -{ - bGPDbrush *brush; - /* Basic brush */ - brush = BKE_gpencil_brush_addnew(ts, "Basic", true); - brush->thickness = 3.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 0; - brush->draw_random_sub = 0.0f; - - /* Pencil brush */ - brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.7f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Ink brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink", false); - brush->thickness = 7.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.6f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Ink Noise brush */ - brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false); - brush->thickness = 6.0f; - brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 1.611f; - brush->flag |= GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 1.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 1.1f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Marker brush */ - brush = BKE_gpencil_brush_addnew(ts, "Marker", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 2.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 1.0f; - brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = M_PI_4; /* 45 degrees */ - brush->draw_angle_factor = 1.0f; - - brush->draw_smoothfac = 1.0f; - brush->draw_smoothlvl = 2; - brush->sublevel = 2; - brush->draw_random_sub = 0.0f; - - /* Crayon brush */ - brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); - brush->thickness = 10.0f; - brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; - brush->draw_sensitivity = 3.0f; - brush->flag &= ~GP_BRUSH_USE_PRESSURE; - - brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; - brush->draw_strength = 0.140f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - - brush->draw_random_press = 0.0f; - - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - brush->draw_angle = 0.0f; - brush->draw_angle_factor = 0.0f; - - brush->draw_smoothfac = 0.0f; - brush->draw_smoothlvl = 1; - brush->sublevel = 2; - brush->draw_random_sub = 0.5f; - -} - -/* add a new gp-brush and make it the active */ -bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) -{ - bGPDbrush *brush; - - /* check that list is ok */ - if (ts == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush"); - - /* add to datablock */ - BLI_addtail(&ts->gp_brushes, brush); - - /* set basic settings */ - brush->thickness = 3; - brush->draw_smoothlvl = 1; - brush->flag |= GP_BRUSH_USE_PRESSURE; - brush->draw_sensitivity = 1.0f; - brush->draw_strength = 1.0f; - brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; - brush->draw_jitter = 0.0f; - brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; - - /* curves */ - brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - - /* auto-name */ - BLI_strncpy(brush->info, name, sizeof(brush->info)); - BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); - - /* make this one the active one */ - if (setactive) { - BKE_gpencil_brush_setactive(ts, brush); - } - - /* return brush */ - return brush; -} - -/* add a new gp-palettecolor and make it the active */ -bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) -{ - bGPDpalettecolor *palcolor; - - /* check that list is ok */ - if (palette == NULL) { - return NULL; - } - - /* allocate memory and add to end of list */ - palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); - - /* add to datablock */ - BLI_addtail(&palette->colors, palcolor); - - /* set basic settings */ - palcolor->flag |= PC_COLOR_HQ_FILL; - copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); - ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); - - /* auto-name */ - BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); - BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), - sizeof(palcolor->info)); - - /* make this one the active one */ - if (setactive) { - BKE_gpencil_palettecolor_setactive(palette, palcolor); - } - - /* return palette color */ - return palcolor; -} - /* add a new gp-datablock */ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) { @@ -632,20 +455,142 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) /* initial settings */ gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); - /* for now, stick to view is also enabled by default - * since this is more useful... - */ + /* general flags */ gpd->flag |= GP_DATA_VIEWALIGN; + /* GP object specific settings */ + ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f); + + gpd->xray_mode = GP_XRAY_3DSPACE; + gpd->runtime.batch_cache_data = NULL; + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; + + /* onion-skinning settings (datablock level) */ + gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); + gpd->onion_flag |= GP_ONION_FADE; + gpd->onion_mode = GP_ONION_MODE_RELATIVE; + gpd->onion_factor = 0.5f; + ARRAY_SET_ITEMS(gpd->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ + ARRAY_SET_ITEMS(gpd->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ + gpd->gstep = 1; + gpd->gstep_next = 1; + return gpd; } -/* -------- Data Duplication ---------- */ + +/* ************************************************** */ +/* Primitive Creation */ +/* Utilities for easier bulk-creation of geometry */ + +/** + * Populate stroke with point data from data buffers + * + * \param array Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values + * \param mat 4x4 transform matrix to transform points into the right coordinate space + */ +void BKE_gpencil_stroke_add_points(bGPDstroke *gps, const float *array, const int totpoints, const float mat[4][4]) +{ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + const int x = GP_PRIM_DATABUF_SIZE * i; + + pt->x = array[x]; + pt->y = array[x + 1]; + pt->z = array[x + 2]; + mul_m4_v3(mat, &pt->x); + + pt->pressure = array[x + 3]; + pt->strength = array[x + 4]; + } +} + +/* Create a new stroke, with pre-allocated data buffers */ +bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness) +{ + /* allocate memory for a new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + + gps->thickness = thickness * 25; + gps->inittime = 0; + + /* enable recalculation flag by default */ + gps->flag = GP_STROKE_RECALC_CACHES | GP_STROKE_3DSPACE; + + gps->totpoints = totpoints; + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + + /* initialize triangle memory to dummy data */ + gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + gps->mat_nr = mat_idx; + + /* add to frame */ + BLI_addtail(&gpf->strokes, gps); + + return gps; +} + + +/* ************************************************** */ +/* Data Duplication */ + +/* make a copy of a given gpencil weights */ +void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst) +{ + if (gps_src == NULL) { + return; + } + BLI_assert(gps_src->totpoints == gps_dst->totpoints); + + if ((gps_src->dvert == NULL) || (gps_dst->dvert == NULL)){ + return; + } + + for (int i = 0; i < gps_src->totpoints; i++) { + MDeformVert *dvert_src = &gps_src->dvert[i]; + MDeformVert *dvert_dst = &gps_dst->dvert[i]; + if (dvert_src->totweight > 0) { + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + } + else { + dvert_dst->dw = NULL; + } + + } +} + +/* make a copy of a given gpencil stroke */ +bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src) +{ + bGPDstroke *gps_dst = NULL; + + gps_dst = MEM_dupallocN(gps_src); + gps_dst->prev = gps_dst->next = NULL; + + gps_dst->points = MEM_dupallocN(gps_src->points); + + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); + + /* Don't clear triangles, so that modifier evaluation can just use + * this without extra work first. Most places that need to force + * this data to get recalculated will destroy the data anyway though. + */ + gps_dst->triangles = MEM_dupallocN(gps_dst->triangles); + /* gps_dst->flag |= GP_STROKE_RECALC_CACHES; */ + + /* return new stroke */ + return gps_dst; +} /* make a copy of a given gpencil frame */ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { - bGPDstroke *gps_dst; + bGPDstroke *gps_dst = NULL; bGPDframe *gpf_dst; /* error checking */ @@ -660,11 +605,8 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) /* copy strokes */ BLI_listbase_clear(&gpf_dst->strokes); for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_CACHES; + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); BLI_addtail(&gpf_dst->strokes, gps_dst); } @@ -672,55 +614,24 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) return gpf_dst; } -/* make a copy of a given gpencil brush */ -bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) +/* make a copy of strokes between gpencil frames */ +void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_dst) { - bGPDbrush *brush_dst; - + bGPDstroke *gps_dst = NULL; /* error checking */ - if (brush_src == NULL) { - return NULL; + if ((gpf_src == NULL) || (gpf_dst == NULL)) { + return; } - /* make a copy of source brush */ - brush_dst = MEM_dupallocN(brush_src); - brush_dst->prev = brush_dst->next = NULL; - /* make a copy of curves */ - brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity); - brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength); - brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter); - - /* return new brush */ - return brush_dst; + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { + /* make copy of source stroke */ + gps_dst = BKE_gpencil_stroke_duplicate(gps_src); + BLI_addtail(&gpf_dst->strokes, gps_dst); + } } -/* make a copy of a given gpencil palette */ -bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) -{ - bGPDpalette *palette_dst; - const bGPDpalettecolor *palcolor_src; - bGPDpalettecolor *palcolord_dst; - - /* error checking */ - if (palette_src == NULL) { - return NULL; - } - - /* make a copy of source palette */ - palette_dst = MEM_dupallocN(palette_src); - palette_dst->prev = palette_dst->next = NULL; - - /* copy colors */ - BLI_listbase_clear(&palette_dst->colors); - for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) { - /* make a copy of source */ - palcolord_dst = MEM_dupallocN(palcolor_src); - BLI_addtail(&palette_dst->colors, palcolord_dst); - } - - /* return new palette */ - return palette_dst; -} /* make a copy of a given gpencil layer */ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { @@ -736,6 +647,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /* make a copy of source layer */ gpl_dst = MEM_dupallocN(gpl_src); gpl_dst->prev = gpl_dst->next = NULL; + gpl_dst->runtime.derived_data = NULL; /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); @@ -755,7 +667,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) /** * Only copy internal data of GreasePencil ID from source to already allocated/initialized destination. - * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. + * You probably never want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. * * WARNING! This function will not handle ID user count! * @@ -763,6 +675,14 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) */ void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) { + /* cache data is not duplicated */ + gpd_dst->runtime.batch_cache_data = NULL; + + /* duplicate material array */ + if (gpd_src->mat) { + gpd_dst->mat = MEM_dupallocN(gpd_src->mat); + } + /* copy layers */ BLI_listbase_clear(&gpd_dst->layers); for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { @@ -771,45 +691,46 @@ void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata BLI_addtail(&gpd_dst->layers, gpl_dst); } - /* copy palettes */ - BLI_listbase_clear(&gpd_dst->palettes); - for (const bGPDpalette *palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) { - bGPDpalette *palette_dst = BKE_gpencil_palette_duplicate(palette_src); /* TODO here too could add unused flags... */ - BLI_addtail(&gpd_dst->palettes, palette_dst); - } +} + +/* Standard API to make a copy of GP datablock, separate from copying its data */ +bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd) +{ + bGPdata *gpd_copy; + BKE_id_copy_ex(bmain, &gpd->id, (ID **)&gpd_copy, 0, false); + return gpd_copy; } /* make a copy of a given gpencil datablock */ +// XXX: Should this be deprecated? bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy) { + bGPdata *gpd_dst; + /* Yuck and super-uber-hyper yuck!!! * Should be replaceable with a no-main copy (LIB_ID_COPY_NO_MAIN etc.), but not sure about it, * so for now keep old code for that one. */ - if (internal_copy) { - const bGPDlayer *gpl_src; - bGPDlayer *gpl_dst; - bGPdata *gpd_dst; + /* error checking */ + if (gpd_src == NULL) { + return NULL; + } + + if (internal_copy) { /* make a straight copy for undo buffers used during stroke drawing */ gpd_dst = MEM_dupallocN(gpd_src); - - /* copy layers */ - BLI_listbase_clear(&gpd_dst->layers); - for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { - /* make a copy of source layer and its data */ - gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); - BLI_addtail(&gpd_dst->layers, gpl_dst); - } - - /* return new */ - return gpd_dst; } else { BLI_assert(bmain != NULL); - bGPdata *gpd_copy; - BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_copy, 0, false); - return gpd_copy; + BKE_id_copy_ex(bmain, &gpd_src->id, (ID **)&gpd_dst, 0, false); + gpd_dst->runtime.batch_cache_data = NULL; } + + /* Copy internal data (layers, etc.) */ + BKE_gpencil_copy_data(bmain, gpd_dst, gpd_src, 0); + + /* return new */ + return gpd_dst; } void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) @@ -817,7 +738,8 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) BKE_id_make_local_generic(bmain, &gpd->id, true, lib_local); } -/* -------- GP-Stroke API --------- */ +/* ************************************************** */ +/* GP Stroke API */ /* ensure selection status of stroke is in sync with its points */ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) @@ -842,7 +764,8 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) } } -/* -------- GP-Frame API ---------- */ +/* ************************************************** */ +/* GP Frame API */ /* delete the last stroke of the given frame */ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) @@ -855,7 +778,13 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) return; /* free the stroke and its data */ - MEM_freeN(gps->points); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); @@ -866,7 +795,8 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) } } -/* -------- GP-Layer API ---------- */ +/* ************************************************** */ +/* GP Layer API */ /* Check if the given layer is able to be edited or not */ bool gpencil_layer_is_editable(const bGPDlayer *gpl) @@ -1048,8 +978,6 @@ bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) */ if (gpl->actframe == gpf) gpl->actframe = gpf->prev; - else - gpl->actframe = NULL; /* free the frame and its data */ changed = BKE_gpencil_free_strokes(gpf); @@ -1103,255 +1031,602 @@ void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) /* free layer */ BKE_gpencil_free_frames(gpl); + + /* free icon providing preview of icon color */ + BKE_icon_delete(gpl->runtime.icon_id); + + /* free derived data */ + BKE_gpencil_clear_derived(gpl); + if (gpl->runtime.derived_data) { + BLI_ghash_free(gpl->runtime.derived_data, NULL, NULL); + gpl->runtime.derived_data = NULL; + } + BLI_freelinkN(&gpd->layers, gpl); } -/* ************************************************** */ -/* get the active gp-brush for editing */ -bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts) +Material *BKE_gpencil_get_material_from_brush(Brush *brush) { - bGPDbrush *brush; + Material *ma = NULL; - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first)) { - return NULL; + if ((brush != NULL) && (brush->gpencil_settings != NULL) && + (brush->gpencil_settings->material != NULL)) + { + ma = brush->gpencil_settings->material; } - /* loop over brushes until found (assume only one active) */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - if (brush->flag & GP_BRUSH_ACTIVE) { - return brush; + return ma; +} + +/* Get active color, and add all default settings if we don't find anything */ +Material *BKE_gpencil_material_ensure(Main *bmain, Object *ob) +{ + Material *ma = NULL; + + /* sanity checks */ + if (ELEM(NULL, bmain, ob)) + return NULL; + + ma = give_current_material(ob, ob->actcol); + if (ma == NULL) { + if (ob->totcol == 0) { + BKE_object_material_slot_add(bmain, ob); + } + ma = BKE_material_add_gpencil(bmain, DATA_("Material")); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + } + else if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); + } + + return ma; +} + +/* ************************************************** */ +/* GP Object - Boundbox Support */ + +/** + * Get min/max coordinate bounds for single stroke + * \return Returns whether we found any selected points + */ +bool BKE_gpencil_stroke_minmax( + const bGPDstroke *gps, const bool use_select, + float r_min[3], float r_max[3]) +{ + const bGPDspoint *pt; + int i; + bool changed = false; + + if (ELEM(NULL, gps, r_min, r_max)) + return false; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) { + minmax_v3v3_v3(r_min, r_max, &pt->x); + changed = true; } } - - /* no active brush found */ - return NULL; + return changed; } -/* set the active gp-brush */ -void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +/* get min/max bounds of all strokes in GP datablock */ +static void gpencil_minmax(bGPdata *gpd, float r_min[3], float r_max[3]) { - bGPDbrush *brush; + INIT_MINMAX(r_min, r_max); - /* error checking */ - if (ELEM(NULL, ts, ts->gp_brushes.first, active)) { + if (gpd == NULL) return; - } - /* loop over brushes deactivating all */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - brush->flag &= ~GP_BRUSH_ACTIVE; - } + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *gpf = gpl->actframe; - /* set as active one */ - active->flag |= GP_BRUSH_ACTIVE; -} - -/* delete the active gp-brush */ -void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) -{ - /* error checking */ - if (ELEM(NULL, ts, brush)) { - return; - } - - /* free curves */ - if (brush->cur_sensitivity) { - curvemapping_free(brush->cur_sensitivity); - } - if (brush->cur_strength) { - curvemapping_free(brush->cur_strength); - } - if (brush->cur_jitter) { - curvemapping_free(brush->cur_jitter); - } - - /* free */ - BLI_freelinkN(&ts->gp_brushes, brush); -} - -/* ************************************************** */ -/* get the active gp-palette for editing */ -bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) -{ - bGPDpalette *palette; - - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first)) { - return NULL; - } - - /* loop over palettes until found (assume only one active) */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - if (palette->flag & PL_PALETTE_ACTIVE) - return palette; - } - - /* no active palette found */ - return NULL; -} - -/* set the active gp-palette */ -void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) -{ - bGPDpalette *palette; - - /* error checking */ - if (ELEM(NULL, gpd, gpd->palettes.first, active)) { - return; - } - - /* loop over palettes deactivating all */ - for (palette = gpd->palettes.first; palette; palette = palette->next) { - palette->flag &= ~PL_PALETTE_ACTIVE; - } - - /* set as active one */ - active->flag |= PL_PALETTE_ACTIVE; - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); -} - -/* delete the active gp-palette */ -void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) -{ - /* error checking */ - if (ELEM(NULL, gpd, palette)) { - return; - } - - /* free colors */ - free_gpencil_colors(palette); - BLI_freelinkN(&gpd->palettes, palette); - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); -} - -/* Set all strokes to recalc the palette color */ -void BKE_gpencil_palette_change_strokes(bGPdata *gpd) -{ - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - gps->flag |= GP_STROKE_RECALC_COLOR; + if (gpf != NULL) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); } } } } - -/* get the active gp-palettecolor for editing */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) +/* compute center of bounding box */ +void BKE_gpencil_centroid_3D(bGPdata *gpd, float r_centroid[3]) { - bGPDpalettecolor *palcolor; + float min[3], max[3], tot[3]; - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first)) { - return NULL; + gpencil_minmax(gpd, min, max); + + add_v3_v3v3(tot, min, max); + mul_v3_v3fl(r_centroid, tot, 0.5f); +} + + +/* create bounding box values */ +static void boundbox_gpencil(Object *ob) +{ + BoundBox *bb; + bGPdata *gpd; + float min[3], max[3]; + + if (ob->bb == NULL) { + ob->bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); } - /* loop over colors until found (assume only one active) */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - if (palcolor->flag & PC_COLOR_ACTIVE) { - return palcolor; + bb = ob->bb; + gpd = ob->data; + + gpencil_minmax(gpd, min, max); + BKE_boundbox_init_from_minmax(bb, min, max); + + bb->flag &= ~BOUNDBOX_DIRTY; +} + +/* get bounding box */ +BoundBox *BKE_gpencil_boundbox_get(Object *ob) +{ + bGPdata *gpd; + + if (ELEM(NULL, ob, ob->data)) + return NULL; + + gpd = ob->data; + if ((ob->bb) && ((ob->bb->flag & BOUNDBOX_DIRTY) == 0) && + ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) + { + return ob->bb; + } + + boundbox_gpencil(ob); + + return ob->bb; +} + +/* ************************************************** */ +/* Apply Transforms */ + +void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) +{ + if (gpd == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* FIXME: For now, we just skip parented layers. + * Otherwise, we have to update each frame to find + * the current parent position/effects. + */ + if (gpl->parent) { + continue; + } + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *pt; + int i; + + for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) { + mul_m4_v3(mat, &pt->x); + } + + /* TODO: Do we need to do this? distortion may mean we need to re-triangulate */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } } } - /* no active color found */ - return NULL; -} -/* get the gp-palettecolor looking for name */ -bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) -{ - /* error checking */ - if (ELEM(NULL, palette, name)) { - return NULL; - } - - return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info)); } -/* Change color name in all strokes */ -void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +/* ************************************************** */ +/* GP Object - Vertex Groups */ + +/* remove a vertex group */ +void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) { - bGPDlayer *gpl; - bGPDframe *gpf; - bGPDstroke *gps; + bGPdata *gpd = ob->data; + MDeformVert *dvert = NULL; + MDeformWeight *gpw = NULL; + const int def_nr = BLI_findindex(&ob->defbase, defgroup); - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, oldname, newname)) - return; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (STREQ(gps->colorname, oldname)) { - BLI_strncpy(gps->colorname, newname, sizeof(gps->colorname)); + /* Remove points data */ + if (gpd) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + for (int i2 = 0; i2 < dvert->totweight; i2++) { + gpw = &dvert->dw[i2]; + if (gpw->def_nr == def_nr) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + /* if index is greater, must be moved one back */ + if (gpw->def_nr > def_nr) { + gpw->def_nr--; + } + } + } } } } } + /* Remove the group */ + BLI_freelinkN(&ob->defbase, defgroup); } -/* Delete all strokes of the color */ -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +/* add a new weight */ +MDeformWeight *BKE_gpencil_vgroup_add_point_weight(MDeformVert *dvert, int index, float weight) +{ + MDeformWeight *new_gpw = NULL; + MDeformWeight *tmp_gpw; + + /* need to verify if was used before to update */ + for (int i = 0; i < dvert->totweight; i++) { + tmp_gpw = &dvert->dw[i]; + if (tmp_gpw->def_nr == index) { + tmp_gpw->weight = weight; + return tmp_gpw; + } + } + + dvert->totweight++; + if (dvert->totweight == 1) { + dvert->dw = MEM_callocN(sizeof(MDeformWeight), "gp_weight"); + } + else { + dvert->dw = MEM_reallocN(dvert->dw, sizeof(MDeformWeight) * dvert->totweight); + } + new_gpw = &dvert->dw[dvert->totweight - 1]; + new_gpw->def_nr = index; + new_gpw->weight = weight; + + return new_gpw; +} + +/* return the weight if use index or -1*/ +float BKE_gpencil_vgroup_use_index(MDeformVert *dvert, int index) +{ + MDeformWeight *gpw; + for (int i = 0; i < dvert->totweight; i++) { + gpw = &dvert->dw[i]; + if (gpw->def_nr == index) { + return gpw->weight; + } + } + return -1.0f; +} + +/* add a new weight */ +bool BKE_gpencil_vgroup_remove_point_weight(MDeformVert *dvert, int index) +{ + int e = 0; + + if (BKE_gpencil_vgroup_use_index(dvert, index) < 0.0f) { + return false; + } + + /* if the array get empty, exit */ + if (dvert->totweight == 1) { + dvert->totweight = 0; + MEM_SAFE_FREE(dvert->dw); + return true; + } + + /* realloc weights */ + MDeformWeight *tmp = MEM_dupallocN(dvert->dw); + MEM_SAFE_FREE(dvert->dw); + dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight - 1, "gp_weights"); + + for (int x = 0; x < dvert->totweight; x++) { + MDeformWeight *gpw = &tmp[e]; + MDeformWeight *final_gpw = &dvert->dw[e]; + if (gpw->def_nr != index) { + final_gpw->def_nr = gpw->def_nr; + final_gpw->weight = gpw->weight; + e++; + } + } + MEM_SAFE_FREE(tmp); + dvert->totweight--; + + return true; +} + + +/* ************************************************** */ + +/** + * Apply smooth to stroke point + * \param gps Stroke to smooth + * \param i Point index + * \param inf Amount of smoothing to apply + */ +bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *pt = &gps->points[i]; + // float pressure = 0.0f; + float sco[3] = { 0.0f }; + + /* Do nothing if not enough points to smooth out */ + if (gps->totpoints <= 2) { + return false; + } + + /* Only affect endpoints by a fraction of the normal strength, + * to prevent the stroke from shrinking too much + */ + if ((i == 0) || (i == gps->totpoints - 1)) { + inf *= 0.1f; + } + + /* Compute smoothed coordinate by taking the ones nearby */ + /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ + { + // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) + const int steps = 2; + const float average_fac = 1.0f / (float)(steps * 2 + 1); + int step; + + /* add the point itself */ + madd_v3_v3fl(sco, &pt->x, average_fac); + + /* n-steps before/after current point */ + // XXX: review how the endpoints are treated by this algorithm + // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight + for (step = 1; step <= steps; step++) { + bGPDspoint *pt1, *pt2; + int before = i - step; + int after = i + step; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pt1 = &gps->points[before]; + pt2 = &gps->points[after]; + + /* add both these points to the average-sum (s += p[i]/n) */ + madd_v3_v3fl(sco, &pt1->x, average_fac); + madd_v3_v3fl(sco, &pt2->x, average_fac); + + } + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v3_v3v3(&pt->x, &pt->x, sco, inf); + + return true; +} + +/** + * Apply smooth for strength to stroke point */ +bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence) +{ + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the strength + * at the distance of point b + */ + const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - influence) * ptb->strength + influence * optimal; + + return true; +} + +/** + * Apply smooth for thickness to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence) +{ + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->pressure, pta->pressure, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->pressure = interpf(optimal, ptb->pressure, influence); + + return true; +} + +/** +* Apply smooth for UV rotation to stroke point (use pressure) */ +bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence) +{ + bGPDspoint *ptb = &gps->points[point_index]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value */ + bGPDspoint *pta, *ptc; + int before = point_index - 1; + int after = point_index + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac); + + /* Based on influence factor, blend between original and optimal */ + ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence); + CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2); + + return true; +} + +/** + * Get range of selected frames in layer. + * Always the active frame is considered as selected, so if no more selected the range + * will be equal to the current active frame. + * \param gpl Layer + * \param r_initframe Number of first selected frame + * \param r_endframe Number of last selected frame + */ +void BKE_gpencil_get_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe) +{ + *r_initframe = gpl->actframe->framenum; + *r_endframe = gpl->actframe->framenum; + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (gpf->framenum < *r_initframe) { + *r_initframe = gpf->framenum; + } + if (gpf->framenum > *r_endframe) { + *r_endframe = gpf->framenum; + } + } + } +} + +/** + * Get Falloff factor base on frame range + * \param gpf Frame + * \param actnum Number of active frame in layer + * \param f_init Number of first selected frame + * \param f_end Number of last selected frame + * \param cur_falloff Curve with falloff factors + */ +float BKE_gpencil_multiframe_falloff_calc(bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff) +{ + float fnum = 0.5f; /* default mid curve */ + float value; + + /* frames to the right of the active frame */ + if (gpf->framenum < actnum) { + fnum = (float)(gpf->framenum - f_init) / (actnum - f_init); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum); + } + /* frames to the left of the active frame */ + else if (gpf->framenum > actnum) { + fnum = (float)(gpf->framenum - actnum) / (f_end - actnum); + fnum *= 0.5f; + value = curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f); + } + else { + value = 1.0f; + } + + return value; +} + +/* remove strokes using a material */ +void BKE_gpencil_material_index_remove(bGPdata *gpd, int index) { - bGPDlayer *gpl; - bGPDframe *gpf; bGPDstroke *gps, *gpsn; - /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */ - if (ELEM(NULL, gpd, name)) - return; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - if (STREQ(gps->colorname, name)) { - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->mat_nr == index) { + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + } + else { + /* reassign strokes */ + if (gps->mat_nr > index) { + gps->mat_nr--; + } + } } } } +} + +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len) +{ + const short remap_len_short = (short)remap_len; + +#define MAT_NR_REMAP(n) \ + if (n < remap_len_short) { \ + BLI_assert(n >= 0 && remap[n] < remap_len_short); \ + n = remap[n]; \ + } ((void)0) + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* reassign strokes */ + MAT_NR_REMAP(gps->mat_nr); + } + } + } + +#undef MAT_NR_REMAP + +} + +/* statistics functions */ +void BKE_gpencil_stats_update(bGPdata *gpd) +{ + gpd->totlayer = 0; + gpd->totframe = 0; + gpd->totstroke = 0; + gpd->totpoint = 0; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpd->totlayer++; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + gpd->totframe++; + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + gpd->totstroke++; + gpd->totpoint += gps->totpoints; + } + } } } - -/* set the active gp-palettecolor */ -void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) -{ - bGPDpalettecolor *palcolor; - - /* error checking */ - if (ELEM(NULL, palette, palette->colors.first, active)) { - return; - } - - /* loop over colors deactivating all */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_ACTIVE; - } - - /* set as active one */ - active->flag |= PC_COLOR_ACTIVE; -} - -/* delete the active gp-palettecolor */ -void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) -{ - /* error checking */ - if (ELEM(NULL, palette, palcolor)) { - return; - } - - /* free */ - BLI_freelinkN(&palette->colors, palcolor); -} diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c new file mode 100644 index 00000000000..2ba738902a0 --- /dev/null +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -0,0 +1,679 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/gpencil_modifier.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_lattice.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_modifiertypes.h" + +static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = { NULL }; + +/* *************************************************** */ +/* Geometry Utilities */ + +/* calculate stroke normal using some points */ +void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3]) +{ + if (gps->totpoints < 3) { + zero_v3(r_normal); + return; + } + + bGPDspoint *points = gps->points; + int totpoints = gps->totpoints; + + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float vec1[3]; + float vec2[3]; + + /* initial vector (p0 -> p1) */ + sub_v3_v3v3(vec1, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(vec2, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(r_normal, vec1, vec2); + + /* Normalize vector */ + normalize_v3(r_normal); +} + +/* Get points of stroke always flat to view not affected by camera view or view position */ +static void gpencil_stroke_project_2d(const bGPDspoint *points, int totpoints, vec2f *points2d) +{ + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; + + /* local X axis (p0 -> p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + const bGPDspoint *pt = &points[i]; + float loc[3]; + + /* Get local space using first point as origin */ + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + vec2f *point = &points2d[i]; + point->x = dot_v3v3(loc, locx); + point->y = dot_v3v3(loc, locy); + } + +} + +/* Stroke Simplify ------------------------------------- */ + +/* Reduce a series of points to a simplified version, but + * maintains the general shape of the series + * + * Ramer - Douglas - Peucker algorithm + * by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + */ +static void gpencil_rdp_stroke(bGPDstroke *gps, vec2f *points2d, float epsilon) +{ + vec2f *old_points2d = points2d; + int totpoints = gps->totpoints; + char *marked = NULL; + char work; + + int start = 1; + int end = gps->totpoints - 2; + + marked = MEM_callocN(totpoints, "GP marked array"); + marked[start] = 1; + marked[end] = 1; + + work = 1; + int totmarked = 0; + /* while still reducing */ + while (work) { + int ls, le; + work = 0; + + ls = start; + le = start + 1; + + /* while not over interval */ + while (ls < end) { + int max_i = 0; + float v1[2]; + /* divided to get more control */ + float max_dist = epsilon / 10.0f; + + /* find the next marked point */ + while (marked[le] == 0) { + le++; + } + + /* perpendicular vector to ls-le */ + v1[1] = old_points2d[le].x - old_points2d[ls].x; + v1[0] = old_points2d[ls].y - old_points2d[le].y; + + for (int i = ls + 1; i < le; i++) { + float mul; + float dist; + float v2[2]; + + v2[0] = old_points2d[i].x - old_points2d[ls].x; + v2[1] = old_points2d[i].y - old_points2d[ls].y; + + if (v2[0] == 0 && v2[1] == 0) { + continue; + } + + mul = (float)(v1[0] * v2[0] + v1[1] * v2[1]) / (float)(v2[0] * v2[0] + v2[1] * v2[1]); + + dist = mul * mul * (v2[0] * v2[0] + v2[1] * v2[1]); + + if (dist > max_dist) { + max_dist = dist; + max_i = i; + } + } + + if (max_i != 0) { + work = 1; + marked[max_i] = 1; + totmarked++; + } + + ls = le; + le = ls + 1; + } + } + + /* adding points marked */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = MEM_dupallocN(gps->dvert); + + /* resize gps */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((marked[i]) || (i == 0) || (i == totpoints - 1)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); + MEM_SAFE_FREE(marked); +} + +/* Simplify stroke using Ramer-Douglas-Peucker algorithm */ +void BKE_gpencil_simplify_stroke(bGPDstroke *gps, float factor) +{ + /* first create temp data and convert points to 2D */ + vec2f *points2d = MEM_mallocN(sizeof(vec2f) * gps->totpoints, "GP Stroke temp 2d points"); + + gpencil_stroke_project_2d(gps->points, gps->totpoints, points2d); + + gpencil_rdp_stroke(gps, points2d, factor); + + MEM_SAFE_FREE(points2d); +} + +/* Simplify alternate vertex of stroke except extrems */ +void BKE_gpencil_simplify_fixed(bGPDstroke *gps) +{ + if (gps->totpoints < 5) { + return; + } + + /* save points */ + bGPDspoint *old_points = MEM_dupallocN(gps->points); + MDeformVert *old_dvert = MEM_dupallocN(gps->dvert); + + /* resize gps */ + int newtot = (gps->totpoints - 2) / 2; + if (((gps->totpoints - 2) % 2) > 0) { + newtot++; + } + newtot += 2; + + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + int j = 0; + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt_src = &old_points[i]; + bGPDspoint *pt = &gps->points[j]; + + MDeformVert *dvert_src = &old_dvert[i]; + MDeformVert *dvert = &gps->dvert[j]; + + if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) { + memcpy(pt, pt_src, sizeof(bGPDspoint)); + memcpy(dvert, dvert_src, sizeof(MDeformVert)); + j++; + } + else { + BKE_gpencil_free_point_weights(dvert_src); + } + } + + gps->totpoints = j; + + MEM_SAFE_FREE(old_points); + MEM_SAFE_FREE(old_dvert); +} + +/* *************************************************** */ +/* Modifier Utilities */ + +/* Lattice Modifier ---------------------------------- */ +/* Usually, evaluation of the lattice modifier is self-contained. + * However, since GP's modifiers operate on a per-stroke basis, + * we need to these two extra functions that called before/after + * each loop over all the geometry being evaluated. + */ + +/* init lattice deform data */ +void BKE_gpencil_lattice_init(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (md->type == eGpencilModifierType_Lattice) { + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + Object *latob = NULL; + + latob = mmd->object; + if ((!latob) || (latob->type != OB_LATTICE)) { + return; + } + if (mmd->cache_data) { + end_latt_deform((struct LatticeDeformData *)mmd->cache_data); + } + + /* init deform data */ + mmd->cache_data = (struct LatticeDeformData *)init_latt_deform(latob, ob); + } + } +} + +/* clear lattice deform data */ +void BKE_gpencil_lattice_clear(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (md->type == eGpencilModifierType_Lattice) { + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + if ((mmd) && (mmd->cache_data)) { + end_latt_deform((struct LatticeDeformData *)mmd->cache_data); + mmd->cache_data = NULL; + } + } + } +} + +/* *************************************************** */ +/* Modifier Methods - Evaluation Loops, etc. */ + +/* check if exist geometry modifiers */ +bool BKE_gpencil_has_geometry_modifiers(Object *ob) +{ + GpencilModifierData *md; + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti && mti->generateStrokes) { + return true; + } + } + return false; +} + +/* apply stroke modifiers */ +void BKE_gpencil_stroke_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *UNUSED(gpf), bGPDstroke *gps, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) + { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (GPENCIL_MODIFIER_EDIT(md, is_edit)) { + continue; + } + + if (mti && mti->deformStroke) { + mti->deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +/* apply stroke geometry modifiers */ +void BKE_gpencil_geometry_modifiers(Depsgraph *depsgraph, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bool is_render) +{ + GpencilModifierData *md; + bGPdata *gpd = ob->data; + const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) + { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (GPENCIL_MODIFIER_EDIT(md, is_edit)) { + continue; + } + + if (mti->generateStrokes) { + mti->generateStrokes(md, depsgraph, ob, gpl, gpf); + } + } + } +} + +/* *************************************************** */ + +void BKE_gpencil_eval_geometry(Depsgraph *depsgraph, + bGPdata *gpd) +{ + DEG_debug_print_eval(depsgraph, __func__, gpd->id.name, gpd); + int ctime = (int)DEG_get_ctime(depsgraph); + + /* update active frame */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + } + + /* TODO: Move "derived_gpf" logic here from DRW_gpencil_populate_datablock()? + * This would be better than inventing our own logic for this stuff... + */ + + /* TODO: Move the following code to "BKE_gpencil_eval_done()" (marked as an exit node) + * later when there's more happening here. For now, let's just keep this in here to avoid + * needing to have one more node slowing down evaluation... + */ + if (DEG_is_active(depsgraph)) { + bGPdata *gpd_orig = (bGPdata *)DEG_get_original_id(&gpd->id); + + /* sync "actframe" changes back to main-db too, + * so that editing tools work with copy-on-write + * when the current frame changes + */ + for (bGPDlayer *gpl = gpd_orig->layers.first; gpl; gpl = gpl->next) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, ctime, GP_GETFRAME_USE_PREV); + } + } +} + +void BKE_gpencil_modifier_init(void) +{ + /* Initialize modifier types */ + gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */ +} + +GpencilModifierData *BKE_gpencil_modifier_new(int type) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type); + GpencilModifierData *md = MEM_callocN(mti->struct_size, mti->struct_name); + + /* note, this name must be made unique later */ + BLI_strncpy(md->name, DATA_(mti->name), sizeof(md->name)); + + md->type = type; + md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render | eGpencilModifierMode_Expanded; + md->flag = eGpencilModifierFlag_StaticOverride_Local; + + if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) + md->mode |= eGpencilModifierMode_Editmode; + + if (mti->initData) mti->initData(md); + + return md; +} + +static void modifier_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_min(id); + } +} + +void BKE_gpencil_modifier_free_ex(GpencilModifierData *md, const int flag) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (mti->foreachIDLink) { + mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL); + } + else if (mti->foreachObjectLink) { + mti->foreachObjectLink(md, NULL, (GreasePencilObjectWalkFunc)modifier_free_data_id_us_cb, NULL); + } + } + + if (mti->freeData) mti->freeData(md); + if (md->error) MEM_freeN(md->error); + + MEM_freeN(md); +} + +void BKE_gpencil_modifier_free(GpencilModifierData *md) +{ + BKE_gpencil_modifier_free_ex(md, 0); +} + +/* check unique name */ +bool BKE_gpencil_modifier_unique_name(ListBase *modifiers, GpencilModifierData *gmd) +{ + if (modifiers && gmd) { + const GpencilModifierTypeInfo *gmti = BKE_gpencil_modifierType_getInfo(gmd->type); + return BLI_uniquename(modifiers, gmd, DATA_(gmti->name), '.', offsetof(GpencilModifierData, name), sizeof(gmd->name)); + } + return false; +} + +bool BKE_gpencil_modifier_dependsOnTime(GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + return mti->dependsOnTime && mti->dependsOnTime(md); +} + +const GpencilModifierTypeInfo *BKE_gpencil_modifierType_getInfo(GpencilModifierType type) +{ + /* type unsigned, no need to check < 0 */ + if (type < NUM_GREASEPENCIL_MODIFIER_TYPES && modifier_gpencil_types[type]->name[0] != '\0') { + return modifier_gpencil_types[type]; + } + else { + return NULL; + } +} + +void BKE_gpencil_modifier_copyData_generic(const GpencilModifierData *md_src, GpencilModifierData *md_dst) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md_src->type); + + /* md_dst may have alredy be fully initialized with some extra allocated data, + * we need to free it now to avoid memleak. */ + if (mti->freeData) { + mti->freeData(md_dst); + } + + const size_t data_size = sizeof(GpencilModifierData); + const char *md_src_data = ((const char *)md_src) + data_size; + char *md_dst_data = ((char *)md_dst) + data_size; + BLI_assert(data_size <= (size_t)mti->struct_size); + memcpy(md_dst_data, md_src_data, (size_t)mti->struct_size - data_size); +} + +static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(id); + } +} + +void BKE_gpencil_modifier_copyData_ex(GpencilModifierData *md, GpencilModifierData *target, const int flag) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + target->mode = md->mode; + target->flag = md->flag; + + if (mti->copyData) { + mti->copyData(md, target); + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (mti->foreachIDLink) { + mti->foreachIDLink(target, NULL, gpencil_modifier_copy_data_id_us_cb, NULL); + } + else if (mti->foreachObjectLink) { + mti->foreachObjectLink(target, NULL, (GreasePencilObjectWalkFunc)gpencil_modifier_copy_data_id_us_cb, NULL); + } + } +} + +void BKE_gpencil_modifier_copyData(GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_ex(md, target, 0); +} + +GpencilModifierData *BKE_gpencil_modifiers_findByType(Object *ob, GpencilModifierType type) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) + if (md->type == type) + break; + + return md; +} + +void BKE_gpencil_modifiers_foreachIDLink(Object *ob, GreasePencilIDWalkFunc walk, void *userData) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->foreachIDLink) mti->foreachIDLink(md, ob, walk, userData); + else if (mti->foreachObjectLink) { + /* each Object can masquerade as an ID, so this should be OK */ + GreasePencilObjectWalkFunc fp = (GreasePencilObjectWalkFunc)walk; + mti->foreachObjectLink(md, ob, fp, userData); + } + } +} + +void BKE_gpencil_modifiers_foreachTexLink(Object *ob, GreasePencilTexWalkFunc walk, void *userData) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + + for (; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->foreachTexLink) + mti->foreachTexLink(md, ob, walk, userData); + } +} + +GpencilModifierData *BKE_gpencil_modifiers_findByName(Object *ob, const char *name) +{ + return BLI_findstring(&(ob->greasepencil_modifiers), name, offsetof(GpencilModifierData, name)); +} + +/* helper function for per-instance positioning */ +void BKE_gpencil_instance_modifier_instance_tfm(InstanceGpencilModifierData *mmd, const int elem_idx[3], float r_mat[4][4]) +{ + float offset[3], rot[3], scale[3]; + int ri = mmd->rnd[0]; + float factor; + + offset[0] = mmd->offset[0] * elem_idx[0]; + offset[1] = mmd->offset[1] * elem_idx[1]; + offset[2] = mmd->offset[2] * elem_idx[2]; + + /* rotation */ + if (mmd->flag & GP_INSTANCE_RANDOM_ROT) { + factor = mmd->rnd_rot * mmd->rnd[ri]; + mul_v3_v3fl(rot, mmd->rot, factor); + add_v3_v3(rot, mmd->rot); + } + else { + copy_v3_v3(rot, mmd->rot); + } + + /* scale */ + if (mmd->flag & GP_INSTANCE_RANDOM_SIZE) { + factor = mmd->rnd_size * mmd->rnd[ri]; + mul_v3_v3fl(scale, mmd->scale, factor); + add_v3_v3(scale, mmd->scale); + } + else { + copy_v3_v3(scale, mmd->scale); + } + + /* advance random index */ + mmd->rnd[0]++; + if (mmd->rnd[0] > 19) { + mmd->rnd[0] = 1; + } + + /* calculate matrix */ + loc_eul_size_to_mat4(r_mat, offset, rot, scale); +} diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.c index 1c2575dfa52..3a4bf53e22d 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.c @@ -37,6 +37,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" @@ -45,7 +47,6 @@ #include "DNA_screen_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" -#include "DNA_brush_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -127,6 +128,9 @@ static void icon_free_data(int icon_id, Icon *icon) else if (icon->obj_type == ICON_DATA_PREVIEW) { ((PreviewImage *)(icon->obj))->icon_id = 0; } + else if (icon->obj_type == ICON_DATA_GPLAYER) { + ((bGPDlayer *)(icon->obj))->runtime.icon_id = 0; + } else if (icon->obj_type == ICON_DATA_GEOM) { ((struct Icon_Geom *)(icon->obj))->icon_id = 0; } @@ -598,6 +602,44 @@ int BKE_icon_id_ensure(struct ID *id) return icon_id_ensure_create_icon(id); } + +static int icon_gplayer_color_ensure_create_icon(bGPDlayer *gpl) +{ + BLI_assert(BLI_thread_is_main()); + + /* NOTE: The color previews for GP Layers don't really need + * to be "rendered" to image per se (as it will just be a plain + * colored rectangle), we need to define icon data here so that + * we can store a pointer to the layer data in icon->obj. + */ + Icon *icon = icon_create(gpl->runtime.icon_id, ICON_DATA_GPLAYER, gpl); + icon->flag = ICON_FLAG_MANAGED; + + return gpl->runtime.icon_id; +} + +int BKE_icon_gplayer_color_ensure(bGPDlayer *gpl) +{ + /* Never handle icons in non-main thread! */ + BLI_assert(BLI_thread_is_main()); + + if (!gpl || G.background) { + return 0; + } + + if (gpl->runtime.icon_id) + return gpl->runtime.icon_id; + + gpl->runtime.icon_id = get_next_free_id(); + + if (!gpl->runtime.icon_id) { + printf("%s: Internal error - not enough IDs\n", __func__); + return 0; + } + + return icon_gplayer_color_ensure_create_icon(gpl); +} + /** * Return icon id of given preview, or create new icon if not found. */ diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 351214ed72b..1d70d9db1e9 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1073,6 +1073,7 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[INDEX_ID_IP] = &(main->ipo); lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ lb[INDEX_ID_KE] = &(main->key); + lb[INDEX_ID_PAL] = &(main->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */ lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ lb[INDEX_ID_NT] = &(main->nodetree); lb[INDEX_ID_IM] = &(main->image); diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 93fdd3349bf..473dd787a69 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -417,7 +417,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call SEQ_END } - CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); for (CollectionObject *cob = scene->master_collection->gobject.first; cob; cob = cob->next) { CALLBACK_INVOKE(cob->ob, IDWALK_CB_USER); @@ -478,6 +477,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (toolsett->uvsculpt) { library_foreach_paint(&data, &toolsett->uvsculpt->paint); } + if (toolsett->gp_paint) { + library_foreach_paint(&data, &toolsett->gp_paint->paint); + } } if (scene->rigidbody_world) { @@ -641,6 +643,10 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call if (material->texpaintslot != NULL) { CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); } + if (material->gp_style != NULL) { + CALLBACK_INVOKE(material->gp_style->sima, IDWALK_CB_USER); + CALLBACK_INVOKE(material->gp_style->ima, IDWALK_CB_USER); + } break; } @@ -758,6 +764,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); + if (brush->gpencil_settings) { + CALLBACK_INVOKE(brush->gpencil_settings->material, IDWALK_CB_USER); + } library_foreach_mtex(&data, &brush->mtex); library_foreach_mtex(&data, &brush->mask_mtex); break; @@ -941,10 +950,15 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call case ID_GD: { bGPdata *gpencil = (bGPdata *) id; - - for (bGPDlayer *gp_layer = gpencil->layers.first; gp_layer; gp_layer = gp_layer->next) { - CALLBACK_INVOKE(gp_layer->parent, IDWALK_CB_NOP); + /* materials */ + for (i = 0; i < gpencil->totcol; i++) { + CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); } + + for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; gplayer = gplayer->next) { + CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); + } + break; } @@ -1072,7 +1086,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return true; #endif case ID_BR: - return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE); + return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE, ID_MA); case ID_PA: return ELEM(id_type_used, ID_OB, ID_GR, ID_TE); case ID_MC: @@ -1083,6 +1097,8 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) return (ELEM(id_type_used, ID_TE, ID_OB)); case ID_LP: return ELEM(id_type_used, ID_IM); + case ID_GD: + return ELEM(id_type_used, ID_MA); case ID_WS: return ELEM(id_type_used, ID_SCR, ID_SCE); case ID_IM: @@ -1091,7 +1107,6 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) case ID_SO: case ID_AR: case ID_AC: - case ID_GD: case ID_WM: case ID_PAL: case ID_PC: diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 28d75811185..03ec26c07d0 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -43,6 +43,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_customdata_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ID.h" #include "DNA_meta_types.h" #include "DNA_node_types.h" @@ -58,6 +59,7 @@ #include "BKE_animsys.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_library.h" @@ -103,10 +105,30 @@ void BKE_material_free(Material *ma) MEM_SAFE_FREE(ma->texpaintslot); + MEM_SAFE_FREE(ma->gp_style); + BKE_icon_id_delete((ID *)ma); BKE_previewimg_free(&ma->preview); } +void BKE_material_init_gpencil_settings(Material *ma) +{ + if ((ma) && (ma->gp_style == NULL)) { + ma->gp_style = MEM_callocN(sizeof(MaterialGPencilStyle), "Grease Pencil Material Settings"); + + MaterialGPencilStyle *gp_style = ma->gp_style; + /* set basic settings */ + gp_style->stroke_rgba[3] = 1.0f; + gp_style->pattern_gridsize = 0.1f; + gp_style->gradient_radius = 0.5f; + ARRAY_SET_ITEMS(gp_style->mix_rgba, 1.0f, 1.0f, 1.0f, 0.2f); + ARRAY_SET_ITEMS(gp_style->gradient_scale, 1.0f, 1.0f); + ARRAY_SET_ITEMS(gp_style->texture_scale, 1.0f, 1.0f); + gp_style->texture_opacity = 1.0f; + gp_style->texture_pixsize = 100.0f; + } +} + void BKE_material_init(Material *ma) { BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ma, id)); @@ -124,6 +146,7 @@ void BKE_material_init(Material *ma) ma->preview = NULL; ma->alpha_threshold = 0.5f; + } Material *BKE_material_add(Main *bmain, const char *name) @@ -137,6 +160,19 @@ Material *BKE_material_add(Main *bmain, const char *name) return ma; } +Material *BKE_material_add_gpencil(Main *bmain, const char *name) +{ + Material *ma; + + ma = BKE_material_add(bmain, name); + + /* grease pencil settings */ + BKE_material_init_gpencil_settings(ma); + + return ma; +} + + /** * Only copy internal data of Material ID from source to already allocated/initialized destination. * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. @@ -164,6 +200,10 @@ void BKE_material_copy_data(Main *bmain, Material *ma_dst, const Material *ma_sr ma_dst->texpaintslot = MEM_dupallocN(ma_src->texpaintslot); } + if (ma_src->gp_style != NULL) { + ma_dst->gp_style = MEM_dupallocN(ma_src->gp_style); + } + BLI_listbase_clear(&ma_dst->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -199,6 +239,7 @@ Material *BKE_material_localize(Material *ma) man->texpaintslot = NULL; man->preview = NULL; + /* man->gp_style = NULL; */ /* XXX: We probably don't want to clear here, or else we may get problems with COW later? */ BLI_listbase_clear(&man->gpumaterial); /* TODO Duplicate Engine Settings and set runtime to NULL */ @@ -218,6 +259,7 @@ Material ***give_matarar(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -231,6 +273,10 @@ Material ***give_matarar(Object *ob) mb = ob->data; return &(mb->mat); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->mat); + } return NULL; } @@ -239,6 +285,7 @@ short *give_totcolp(Object *ob) Mesh *me; Curve *cu; MetaBall *mb; + bGPdata *gpd; if (ob->type == OB_MESH) { me = ob->data; @@ -252,6 +299,10 @@ short *give_totcolp(Object *ob) mb = ob->data; return &(mb->totcol); } + else if (ob->type == OB_GPENCIL) { + gpd = ob->data; + return &(gpd->totcol); + } return NULL; } @@ -286,6 +337,8 @@ short *give_totcolp_id(ID *id) return &(((Curve *)id)->totcol); case ID_MB: return &(((MetaBall *)id)->totcol); + case ID_GD: + return &(((bGPdata *)id)->totcol); default: break; } @@ -307,6 +360,9 @@ static void material_data_index_remove_id(ID *id, short index) case ID_MB: /* meta-elems don't have materials atm */ break; + case ID_GD: + BKE_gpencil_material_index_remove((bGPdata *)id, index); + break; default: break; } @@ -487,6 +543,21 @@ Material *give_current_material(Object *ob, short act) return ma; } +MaterialGPencilStyle *BKE_material_gpencil_settings_get(Object *ob, short act) +{ + Material *ma = give_current_material(ob, act); + if (ma != NULL) { + if (ma->gp_style == NULL) { + BKE_material_init_gpencil_settings(ma); + } + + return ma->gp_style; + } + else { + return NULL; + } +} + Material *give_node_material(Material *ma) { if (ma && ma->use_nodes && ma->nodetree) { @@ -727,6 +798,9 @@ void BKE_material_remap_object(Object *ob, const unsigned int *remap) else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_curve_material_remap(ob->data, remap, ob->totcol); } + if (ob->type == OB_GPENCIL) { + BKE_gpencil_material_remap(ob->data, remap, ob->totcol); + } else { /* add support for this object data! */ BLI_assert(matar == NULL); @@ -924,7 +998,7 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) } /* check indices from mesh */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_GPENCIL)) { material_data_index_remove_id((ID *)ob->data, actcol - 1); if (ob->runtime.curve_cache) { BKE_displist_free(&ob->runtime.curve_cache->disp); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 21b5bb89f19..c88de006eba 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -41,6 +41,7 @@ #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" @@ -53,6 +54,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_smoke_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -86,6 +88,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_fcurve.h" +#include "BKE_gpencil_modifier.h" #include "BKE_icons.h" #include "BKE_key.h" #include "BKE_lamp.h" @@ -110,12 +113,14 @@ #include "BKE_rigidbody.h" #include "BKE_scene.h" #include "BKE_sequencer.h" +#include "BKE_shader_fx.h" #include "BKE_speaker.h" #include "BKE_softbody.h" #include "BKE_subsurf.h" #include "BKE_material.h" #include "BKE_camera.h" #include "BKE_image.h" +#include "BKE_gpencil.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -185,11 +190,15 @@ void BKE_object_free_curve_cache(Object *ob) void BKE_object_free_modifiers(Object *ob, const int flag) { ModifierData *md; + GpencilModifierData *gp_md; while ((md = BLI_pophead(&ob->modifiers))) { modifier_free_ex(md, flag); } + while ((gp_md = BLI_pophead(&ob->greasepencil_modifiers))) { + BKE_gpencil_modifier_free_ex(gp_md, flag); + } /* particle modifiers were freed, so free the particlesystems as well */ BKE_object_free_particlesystems(ob); @@ -200,6 +209,15 @@ void BKE_object_free_modifiers(Object *ob, const int flag) BKE_object_free_derived_caches(ob); } +void BKE_object_free_shaderfx(Object *ob, const int flag) +{ + ShaderFxData *fx; + + while ((fx = BLI_pophead(&ob->shader_fx))) { + BKE_shaderfx_free_ex(fx, flag); + } +} + void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) { /* reset functionality */ @@ -222,6 +240,29 @@ void BKE_object_modifier_hook_reset(Object *ob, HookModifierData *hmd) } } +void BKE_object_modifier_gpencil_hook_reset(Object *ob, HookGpencilModifierData *hmd) +{ + if (hmd->object == NULL) { + return; + } + /* reset functionality */ + bPoseChannel *pchan = BKE_pose_channel_find_name(hmd->object->pose, hmd->subtarget); + + if (hmd->subtarget[0] && pchan) { + float imat[4][4], mat[4][4]; + + /* calculate the world-space matrix for the pose-channel target first, then carry on as usual */ + mul_m4_m4m4(mat, hmd->object->obmat, pchan->pose_mat); + + invert_m4_m4(imat, mat); + mul_m4_m4m4(hmd->parentinv, imat, ob->obmat); + } + else { + invert_m4_m4(hmd->object->imat, hmd->object->obmat); + mul_m4_m4m4(hmd->parentinv, hmd->object->imat, ob->obmat); + } +} + bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti; @@ -428,6 +469,7 @@ void BKE_object_free(Object *ob) /* BKE__free shall never touch to ID->us. Never ever. */ BKE_object_free_modifiers(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); + BKE_object_free_shaderfx(ob, LIB_ID_CREATE_NO_USER_REFCOUNT); MEM_SAFE_FREE(ob->mat); MEM_SAFE_FREE(ob->matbits); @@ -653,6 +695,7 @@ static const char *get_obdata_defname(int type) case OB_ARMATURE: return DATA_("Armature"); case OB_SPEAKER: return DATA_("Speaker"); case OB_EMPTY: return DATA_("Empty"); + case OB_GPENCIL: return DATA_("GPencil"); default: printf("get_obdata_defname: Internal error, bad type: %d\n", type); return DATA_("Empty"); @@ -677,6 +720,7 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) case OB_ARMATURE: return BKE_armature_add(bmain, name); case OB_SPEAKER: return BKE_speaker_add(bmain, name); case OB_LIGHTPROBE:return BKE_lightprobe_add(bmain, name); + case OB_GPENCIL: return BKE_gpencil_data_addnew(bmain, name); case OB_EMPTY: return NULL; default: printf("%s: Internal error, bad type: %d\n", __func__, type); @@ -810,7 +854,7 @@ Object *BKE_object_add( /** * Add a new object, using another one as a reference * - * /param ob_src object to use to determine the collections of the new object. + * \param ob_src object to use to determine the collections of the new object. */ Object *BKE_object_add_from( Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -828,6 +872,41 @@ Object *BKE_object_add_from( return ob; } +/** + * Add a new object, but assign the given datablock as the ob->data + * for the newly created object. + * + * \param data The datablock to assign as ob->data for the new object. + * This is assumed to be of the correct type. + * \param do_id_user If true, id_us_plus() will be called on data when + * assigning it to the object. + */ +Object *BKE_object_add_for_data( + Main *bmain, ViewLayer *view_layer, + int type, const char *name, ID *data, bool do_id_user) +{ + Object *ob; + Base *base; + LayerCollection *layer_collection; + + /* same as object_add_common, except we don't create new ob->data */ + ob = BKE_object_add_only_object(bmain, type, name); + ob->data = data; + if (do_id_user) id_us_plus(data); + + BKE_view_layer_base_deselect_all(view_layer); + DEG_id_tag_update_ex(bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_select(view_layer, base); + + return ob; +} + + void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag) { SoftBody *sb = ob_src->soft; @@ -1153,6 +1232,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, const int flag) { ModifierData *md; + GpencilModifierData *gmd; + ShaderFxData *fx; /* Do not copy runtime data. */ BKE_object_runtime_reset(ob_dst); @@ -1179,6 +1260,24 @@ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, con BLI_addtail(&ob_dst->modifiers, nmd); } + BLI_listbase_clear(&ob_dst->greasepencil_modifiers); + + for (gmd = ob_src->greasepencil_modifiers.first; gmd; gmd = gmd->next) { + GpencilModifierData *nmd = BKE_gpencil_modifier_new(gmd->type); + BLI_strncpy(nmd->name, gmd->name, sizeof(nmd->name)); + BKE_gpencil_modifier_copyData_ex(gmd, nmd, flag_subdata); + BLI_addtail(&ob_dst->greasepencil_modifiers, nmd); + } + + BLI_listbase_clear(&ob_dst->shader_fx); + + for (fx = ob_src->shader_fx.first; fx; fx = fx->next) { + ShaderFxData *nfx = BKE_shaderfx_new(fx->type); + BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name)); + BKE_shaderfx_copyData_ex(fx, nfx, flag_subdata); + BLI_addtail(&ob_dst->shader_fx, nfx); + } + if (ob_src->pose) { copy_object_pose(ob_dst, ob_src, flag_subdata); /* backwards compat... non-armatures can get poses in older files? */ @@ -1210,6 +1309,10 @@ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, con BLI_listbase_clear((ListBase *)&ob_dst->drawdata); BLI_listbase_clear(&ob_dst->pc_ids); + /* grease pencil: clean derived data */ + if (ob_dst->type == OB_GPENCIL) + BKE_gpencil_free_derived_frames(ob_dst->data); + ob_dst->avs = ob_src->avs; ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); @@ -1469,6 +1572,11 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size) ob->empty_drawsize *= size; break; } + case OB_GPENCIL: + { + ob->empty_drawsize *= size; + break; + } case OB_FONT: { Curve *cu = ob->data; @@ -2452,7 +2560,7 @@ void BKE_object_minmax(Object *ob, float min_r[3], float max_r[3], const bool us float size[3]; copy_v3_v3(size, ob->size); - if (ob->type == OB_EMPTY) { + if ((ob->type == OB_EMPTY) || (ob->type == OB_GPENCIL)) { mul_v3_fl(size, ob->empty_drawsize); } @@ -3694,6 +3802,88 @@ bool BKE_object_modifier_use_time(Object *ob, ModifierData *md) return false; } +bool BKE_object_modifier_gpencil_use_time(Object *ob, GpencilModifierData *md) +{ + if (BKE_gpencil_modifier_dependsOnTime(md)) { + return true; + } + + /* Check whether modifier is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "grease_pencil_modifiers[\"%s\"]", md->name); + + /* action - check for F-Curves with paths containing 'grease_pencil_modifiers[' */ + if (adt->action) { + for (fcu = (FCurve *)adt->action->curves.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + /* This here allows modifier properties to get driven and still update properly + * + */ + for (fcu = (FCurve *)adt->drivers.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + return false; +} + +bool BKE_object_shaderfx_use_time(Object *ob, ShaderFxData *fx) +{ + if (BKE_shaderfx_dependsOnTime(fx)) { + return true; + } + + /* Check whether effect is animated. */ + /* TODO (Aligorith): this should be handled as part of build_animdata() */ + if (ob->adt) { + AnimData *adt = ob->adt; + FCurve *fcu; + + char pattern[MAX_NAME + 32]; + BLI_snprintf(pattern, sizeof(pattern), "shader_effects[\"%s\"]", fx->name); + + /* action - check for F-Curves with paths containing string[' */ + if (adt->action) { + for (fcu = (FCurve *)adt->action->curves.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + /* This here allows properties to get driven and still update properly + * + */ + for (fcu = (FCurve *)adt->drivers.first; + fcu != NULL; + fcu = (FCurve *)fcu->next) + { + if (fcu->rna_path && strstr(fcu->rna_path, pattern)) + return true; + } + } + + return false; +} + /* set "ignore cache" flag for all caches on this object */ static void object_cacheIgnoreClear(Object *ob, int state) { diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 5c9e53aaa56..a6b0e57e55c 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -53,6 +53,7 @@ #include "BKE_object.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_gpencil.h" /** \name Misc helpers * \{ */ @@ -402,12 +403,17 @@ static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) */ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) { - if (BKE_object_is_in_editmode_vgroup(ob)) - object_defgroup_remove_edit_mode(ob, defgroup); - else - object_defgroup_remove_object_mode(ob, defgroup); + if ((ob) && (ob->type == OB_GPENCIL)) { + BKE_gpencil_vgroup_remove(ob, defgroup); + } + else { + if (BKE_object_is_in_editmode_vgroup(ob)) + object_defgroup_remove_edit_mode(ob, defgroup); + else + object_defgroup_remove_object_mode(ob, defgroup); - BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + } } /** diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 3e72de3909f..3641df26496 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -62,6 +62,7 @@ #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_scene.h" +#include "BKE_gpencil.h" #include "MEM_guardedalloc.h" @@ -324,6 +325,9 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph, case OB_MBALL: BKE_mball_batch_cache_dirty(ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; + case OB_GPENCIL: + BKE_gpencil_batch_cache_dirty(ob->data); + break; } } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 07aa21f44ff..cb26f7e9f3e 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -41,13 +41,19 @@ #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "DNA_space_types.h" +#include "DNA_gpencil_types.h" #include "DNA_workspace_types.h" #include "BLI_bitmap.h" +#include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utils.h" #include "BLI_math_vector.h" #include "BLI_listbase.h" +#include "BLT_translation.h" + +#include "BKE_animsys.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_deform.h" @@ -55,6 +61,7 @@ #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -151,6 +158,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->imapaint.paint; case ePaintSculptUV: return &ts->uvsculpt->paint; + case ePaintGpencil: + return &ts->gp_paint->paint; case ePaintInvalid: return NULL; default: @@ -176,6 +185,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->wpaint->paint; case OB_MODE_TEXTURE_PAINT: return &ts->imapaint.paint; + case OB_MODE_GPENCIL_PAINT: + return &ts->gp_paint->paint; case OB_MODE_EDIT: if (ts->use_uv_sculpt) return &ts->uvsculpt->paint; @@ -430,13 +441,11 @@ PaletteColor *BKE_palette_color_add(Palette *palette) return color; } - bool BKE_palette_is_empty(const struct Palette *palette) { return BLI_listbase_is_empty(&palette->colors); } - /* are we in vertex paint or weight pain face select mode? */ bool BKE_paint_select_face_test(Object *ob) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index b50dc37af81..7085b515ec1 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -173,6 +173,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->uvsculpt = MEM_dupallocN(ts->uvsculpt); BKE_paint_copy(&ts->uvsculpt->paint, &ts->uvsculpt->paint, flag); } + if (ts->gp_paint) { + ts->gp_paint = MEM_dupallocN(ts->gp_paint); + BKE_paint_copy(&ts->gp_paint->paint, &ts->gp_paint->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->imapaint.paintcursor = NULL; @@ -180,15 +184,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->particle.scene = NULL; ts->particle.object = NULL; - /* duplicate Grease Pencil Drawing Brushes */ - BLI_listbase_clear(&ts->gp_brushes); - for (bGPDbrush *brush = toolsettings->gp_brushes.first; brush; brush = brush->next) { - bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); - BLI_addtail(&ts->gp_brushes, newbrush); - } - /* duplicate Grease Pencil interpolation curve */ ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo); + /* duplicate Grease Pencil multiframe fallof */ + ts->gp_sculpt.cur_falloff = curvemapping_copy(ts->gp_sculpt.cur_falloff); return ts; } @@ -213,16 +212,20 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->uvsculpt->paint); MEM_freeN(toolsettings->uvsculpt); } + if (toolsettings->gp_paint) { + BKE_paint_free(&toolsettings->gp_paint->paint); + MEM_freeN(toolsettings->gp_paint); + } BKE_paint_free(&toolsettings->imapaint.paint); - /* free Grease Pencil Drawing Brushes */ - BKE_gpencil_free_brushes(&toolsettings->gp_brushes); - BLI_freelistN(&toolsettings->gp_brushes); - /* free Grease Pencil interpolation curve */ if (toolsettings->gp_interpolate.custom_ipo) { curvemapping_free(toolsettings->gp_interpolate.custom_ipo); } + /* free Grease Pencil multiframe falloff curve */ + if (toolsettings->gp_sculpt.cur_falloff) { + curvemapping_free(toolsettings->gp_sculpt.cur_falloff); + } MEM_freeN(toolsettings); } @@ -428,9 +431,9 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) } /* NOTE: part of SCE_COPY_LINK_DATA and SCE_COPY_FULL operations - * are done outside of blenkernel with ED_objects_single_users! */ + * are done outside of blenkernel with ED_object_single_users! */ - /* camera */ + /* camera */ if (ELEM(type, SCE_COPY_LINK_DATA, SCE_COPY_FULL)) { ID_NEW_REMAP(sce_copy->camera); } @@ -683,6 +686,19 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->imapaint.normal_angle = 80; sce->toolsettings->imapaint.seam_bleed = 2; + /* alloc grease pencil drawing brushes */ + sce->toolsettings->gp_paint = MEM_callocN(sizeof(GpPaint), "GpPaint"); + + /* grease pencil multiframe falloff curve */ + sce->toolsettings->gp_sculpt.cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + CurveMapping *gp_falloff_curve = sce->toolsettings->gp_sculpt.cur_falloff; + curvemapping_set_defaults(gp_falloff_curve, 1, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(gp_falloff_curve); + curvemap_reset(gp_falloff_curve->cm, + &gp_falloff_curve->clipr, + CURVE_PRESET_GAUSS, + CURVEMAP_SLOPE_POSITIVE); + sce->physics_settings.gravity[0] = 0.0f; sce->physics_settings.gravity[1] = 0.0f; sce->physics_settings.gravity[2] = -9.81f; @@ -760,46 +776,65 @@ void BKE_scene_init(Scene *sce) { GP_BrushEdit_Settings *gset = &sce->toolsettings->gp_sculpt; GP_EditBrush_Data *gp_brush; + float curcolor_add[3], curcolor_sub[3]; + ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f); + ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; gp_brush->size = 50; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH]; gp_brush->size = 25; gp_brush->strength = 0.3f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST]; gp_brush->size = 50; gp_brush->strength = 0.3f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH]; gp_brush->size = 50; gp_brush->strength = 0.5f; // XXX? - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMIZE]; gp_brush->size = 25; gp_brush->strength = 0.5f; - gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(gp_brush->curcolor_add, curcolor_add); + copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub); } /* GP Stroke Placement */ @@ -808,6 +843,10 @@ void BKE_scene_init(Scene *sce) sce->toolsettings->gpencil_seq_align = GP_PROJECT_VIEWSPACE; sce->toolsettings->gpencil_ima_align = GP_PROJECT_VIEWSPACE; + /* Annotations */ + sce->toolsettings->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + sce->toolsettings->annotate_thickness = 3; + sce->orientation_index_custom = -1; /* Master Collection */ diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c new file mode 100644 index 00000000000..c028c2184fd --- /dev/null +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -0,0 +1,245 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/shader_fx.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" + +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_gpencil.h" +#include "BKE_shader_fx.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "FX_shader_types.h" + +static ShaderFxTypeInfo *shader_fx_types[NUM_SHADER_FX_TYPES] = { NULL }; + +/* *************************************************** */ +/* Methods - Evaluation Loops, etc. */ + +/* check if exist grease pencil effects */ +bool BKE_shaderfx_has_gpencil(Object *ob) +{ + ShaderFxData *fx; + for (fx = ob->shader_fx.first; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + if (fxi->type == eShaderFxType_GpencilType) { + return true; + } + } + return false; +} + +void BKE_shaderfx_init(void) +{ + /* Initialize shaders */ + shaderfx_type_init(shader_fx_types); /* FX_shader_util.c */ +} + +ShaderFxData *BKE_shaderfx_new(int type) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(type); + ShaderFxData *fx = MEM_callocN(fxi->struct_size, fxi->struct_name); + + /* note, this name must be made unique later */ + BLI_strncpy(fx->name, DATA_(fxi->name), sizeof(fx->name)); + + fx->type = type; + fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render | eShaderFxMode_Expanded; + fx->flag = eShaderFxFlag_StaticOverride_Local; + + if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) + fx->mode |= eShaderFxMode_Editmode; + + if (fxi->initData) fxi->initData(fx); + + return fx; +} + +static void shaderfx_free_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_min(id); + } +} + +void BKE_shaderfx_free_ex(ShaderFxData *fx, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(fx, NULL, shaderfx_free_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(fx, NULL, (ShaderFxObjectWalkFunc)shaderfx_free_data_id_us_cb, NULL); + } + } + + if (fxi->freeData) fxi->freeData(fx); + if (fx->error) MEM_freeN(fx->error); + + MEM_freeN(fx); +} + +void BKE_shaderfx_free(ShaderFxData *fx) +{ + BKE_shaderfx_free_ex(fx, 0); +} + +/* check unique name */ +bool BKE_shaderfx_unique_name(ListBase *shaders, ShaderFxData *fx) +{ + if (shaders && fx) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + return BLI_uniquename(shaders, fx, DATA_(fxi->name), '.', offsetof(ShaderFxData, name), sizeof(fx->name)); + } + return false; +} + +bool BKE_shaderfx_dependsOnTime(ShaderFxData *fx) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + return fxi->dependsOnTime && fxi->dependsOnTime(fx); +} + +const ShaderFxTypeInfo *BKE_shaderfxType_getInfo(ShaderFxType type) +{ + /* type unsigned, no need to check < 0 */ + if (type < NUM_SHADER_FX_TYPES && shader_fx_types[type]->name[0] != '\0') { + return shader_fx_types[type]; + } + else { + return NULL; + } +} + +void BKE_shaderfx_copyData_generic(const ShaderFxData *fx_src, ShaderFxData *fx_dst) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx_src->type); + + /* fx_dst may have alredy be fully initialized with some extra allocated data, + * we need to free it now to avoid memleak. */ + if (fxi->freeData) { + fxi->freeData(fx_dst); + } + + const size_t data_size = sizeof(ShaderFxData); + const char *fx_src_data = ((const char *)fx_src) + data_size; + char *fx_dst_data = ((char *)fx_dst) + data_size; + BLI_assert(data_size <= (size_t)fxi->struct_size); + memcpy(fx_dst_data, fx_src_data, (size_t)fxi->struct_size - data_size); +} + +static void shaderfx_copy_data_id_us_cb(void *UNUSED(userData), Object *UNUSED(ob), ID **idpoin, int cb_flag) +{ + ID *id = *idpoin; + if (id != NULL && (cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(id); + } +} + +void BKE_shaderfx_copyData_ex(ShaderFxData *fx, ShaderFxData *target, const int flag) +{ + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + target->mode = fx->mode; + target->flag = fx->flag; + + if (fxi->copyData) { + fxi->copyData(fx, target); + } + + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + if (fxi->foreachIDLink) { + fxi->foreachIDLink(target, NULL, shaderfx_copy_data_id_us_cb, NULL); + } + else if (fxi->foreachObjectLink) { + fxi->foreachObjectLink(target, NULL, (ShaderFxObjectWalkFunc)shaderfx_copy_data_id_us_cb, NULL); + } + } +} + +void BKE_shaderfx_copyData(ShaderFxData *fx, ShaderFxData *target) +{ + BKE_shaderfx_copyData_ex(fx, target, 0); +} + +ShaderFxData *BKE_shaderfx_findByType(Object *ob, ShaderFxType type) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) + if (fx->type == type) + break; + + return fx; +} + +void BKE_shaderfx_foreachIDLink(Object *ob, ShaderFxIDWalkFunc walk, void *userData) +{ + ShaderFxData *fx = ob->shader_fx.first; + + for (; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + + if (fxi->foreachIDLink) fxi->foreachIDLink(fx, ob, walk, userData); + else if (fxi->foreachObjectLink) { + /* each Object can masquerade as an ID, so this should be OK */ + ShaderFxObjectWalkFunc fp = (ShaderFxObjectWalkFunc)walk; + fxi->foreachObjectLink(fx, ob, fp, userData); + } + } +} + +ShaderFxData *BKE_shaderfx_findByName(Object *ob, const char *name) +{ + return BLI_findstring(&(ob->shader_fx), name, offsetof(ShaderFxData, name)); +} diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index ec16a6854c4..ef2ff10b5c6 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -78,6 +78,9 @@ MINLINE void zero_v3_int(int r[3]); MINLINE void copy_v2_v2_int(int r[2], const int a[2]); MINLINE void copy_v3_v3_int(int r[3], const int a[3]); MINLINE void copy_v4_v4_int(int r[4], const int a[4]); +/* int <-> float */ +MINLINE void copy_v2fl_v2i(float r[2], const int a[2]); +MINLINE void round_v2i_v2fl(int r[2], const float a[2]); /* double -> float */ MINLINE void copy_v2fl_v2db(float r[2], const double a[2]); MINLINE void copy_v3fl_v3db(float r[3], const double a[3]); diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h index 612151b7ea2..f7dea562393 100644 --- a/source/blender/blenlib/BLI_rand.h +++ b/source/blender/blenlib/BLI_rand.h @@ -64,6 +64,9 @@ void BLI_rng_shuffle_array(struct RNG *rng, void *data, unsigned int elem /** Note that skipping is as slow as generating n numbers! */ void BLI_rng_skip(struct RNG *rng, int n) ATTR_NONNULL(1); +/* fill an array with random numbers */ +void BLI_array_frand(float *ar, int count, unsigned int seed); + /** Return a pseudo-random (hash) float from an integer value */ float BLI_hash_frand(unsigned int seed) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/listbase.c b/source/blender/blenlib/intern/listbase.c index 568448327bd..80b8a8d041c 100644 --- a/source/blender/blenlib/intern/listbase.c +++ b/source/blender/blenlib/intern/listbase.c @@ -578,6 +578,9 @@ void *BLI_findstring(const ListBase *listbase, const char *id, const int offset) Link *link = NULL; const char *id_iter; + if (id == NULL) + return NULL; + for (link = listbase->first; link; link = link->next) { id_iter = ((const char *)link) + offset; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 189b94a6f13..c4535eacefa 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -192,6 +192,19 @@ MINLINE void copy_v4_v4_int(int r[4], const int a[4]) r[3] = a[3]; } +/* int <-> float */ +MINLINE void round_v2i_v2fl(int r[2], const float a[2]) +{ + r[0] = (int)roundf(a[0]); + r[1] = (int)roundf(a[1]); +} + +MINLINE void copy_v2fl_v2i(float r[2], const int a[2]) +{ + r[0] = (float)a[0]; + r[1] = (float)a[1]; +} + /* double -> float */ MINLINE void copy_v2fl_v2db(float r[2], const double a[2]) { diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.c index 9e56ce6b2cf..8613a0ea6dd 100644 --- a/source/blender/blenlib/intern/rand.c +++ b/source/blender/blenlib/intern/rand.c @@ -265,6 +265,18 @@ void BLI_rng_skip(RNG *rng, int n) /***/ +/* fill an array with random numbers */ +void BLI_array_frand(float *ar, int count, unsigned int seed) +{ + RNG rng; + + BLI_rng_srandom(&rng, seed); + + for (int i = 0; i < count; i++) { + ar[i] = BLI_rng_get_float(&rng); + } +} + float BLI_hash_frand(unsigned int seed) { RNG rng; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 816a527d829..293114c4b79 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -72,6 +72,8 @@ #include "DNA_genfile.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_ipo_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" @@ -130,6 +132,8 @@ #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_global.h" // for G +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_layer.h" #include "BKE_library.h" // for which_libbase #include "BKE_library_idmap.h" @@ -153,6 +157,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_sequencer.h" +#include "BKE_shader_fx.h" #include "BKE_outliner_treehash.h" #include "BKE_sound.h" #include "BKE_colortools.h" @@ -266,6 +271,8 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname); #ifdef USE_COLLECTION_COMPAT_28 static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection *sc); #endif +static void direct_link_animdata(FileData *fd, AnimData *adt); +static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt); /* this function ensures that reports are printed, * in the case of libraray linking errors this is important! @@ -2368,6 +2375,7 @@ static void direct_link_curvemapping(FileData *fd, CurveMapping *cumap) } /* ************ READ Brush *************** */ + /* library brush linking after fileread */ static void lib_link_brush(FileData *fd, Main *main) { @@ -2383,6 +2391,11 @@ static void lib_link_brush(FileData *fd, Main *main) brush->toggle_brush = newlibadr(fd, brush->id.lib, brush->toggle_brush); brush->paint_curve = newlibadr_us(fd, brush->id.lib, brush->paint_curve); + /* link default grease pencil palette */ + if (brush->gpencil_settings != NULL) { + brush->gpencil_settings->material = newlibadr_us(fd, brush->id.lib, brush->gpencil_settings->material); + } + brush->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -2394,6 +2407,7 @@ static void direct_link_brush(FileData *fd, Brush *brush) /* fallof curve */ brush->curve = newdataadr(fd, brush->curve); + brush->gradient = newdataadr(fd, brush->gradient); if (brush->curve) @@ -2401,11 +2415,29 @@ static void direct_link_brush(FileData *fd, Brush *brush) else BKE_brush_curve_preset(brush, CURVE_PRESET_SHARP); + /* grease pencil */ + brush->gpencil_settings = newdataadr(fd, brush->gpencil_settings); + if (brush->gpencil_settings != NULL) { + brush->gpencil_settings->curve_sensitivity = newdataadr(fd, brush->gpencil_settings->curve_sensitivity); + brush->gpencil_settings->curve_strength = newdataadr(fd, brush->gpencil_settings->curve_strength); + brush->gpencil_settings->curve_jitter = newdataadr(fd, brush->gpencil_settings->curve_jitter); + + if (brush->gpencil_settings->curve_sensitivity) + direct_link_curvemapping(fd, brush->gpencil_settings->curve_sensitivity); + + if (brush->gpencil_settings->curve_strength) + direct_link_curvemapping(fd, brush->gpencil_settings->curve_strength); + + if (brush->gpencil_settings->curve_jitter) + direct_link_curvemapping(fd, brush->gpencil_settings->curve_jitter); + } + brush->preview = NULL; brush->icon_imbuf = NULL; } /* ************ READ Palette *************** */ + static void lib_link_palette(FileData *fd, Main *main) { /* only link ID pointers */ @@ -2420,6 +2452,7 @@ static void lib_link_palette(FileData *fd, Main *main) static void direct_link_palette(FileData *fd, Palette *palette) { + /* palette itself has been read */ link_list(fd, &palette->colors); } @@ -4147,6 +4180,17 @@ static void lib_link_material(FileData *fd, Main *main) ma->nodetree->id.lib = ma->id.lib; } + /* relink grease pencil settings */ + if (ma->gp_style != NULL) { + MaterialGPencilStyle *gp_style = ma->gp_style; + if (gp_style->sima != NULL) { + gp_style->sima = newlibadr_us(fd, ma->id.lib, gp_style->sima); + } + if (gp_style->ima != NULL) { + gp_style->ima = newlibadr_us(fd, ma->id.lib, gp_style->ima); + } + } + ma->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -4167,6 +4211,8 @@ static void direct_link_material(FileData *fd, Material *ma) ma->preview = direct_link_preview_image(fd, ma->preview); BLI_listbase_clear(&ma->gpumaterial); + + ma->gp_style = newdataadr(fd, ma->gp_style); } /* ************ READ PARTICLE SETTINGS ***************** */ @@ -4802,7 +4848,7 @@ static void direct_link_latt(FileData *fd, Lattice *lt) /* ************ READ OBJECT ***************** */ -static void lib_link_modifiers__linkModifiers( +static void lib_link_modifiers_common( void *userData, Object *ob, ID **idpoin, int cb_flag) { FileData *fd = userData; @@ -4812,9 +4858,10 @@ static void lib_link_modifiers__linkModifiers( id_us_plus_no_lib(*idpoin); } } + static void lib_link_modifiers(FileData *fd, Object *ob) { - modifiers_foreachIDLink(ob, lib_link_modifiers__linkModifiers, fd); + modifiers_foreachIDLink(ob, lib_link_modifiers_common, fd); /* If linking from a library, clear 'local' static override flag. */ if (ob->id.lib != NULL) { @@ -4825,6 +4872,30 @@ static void lib_link_modifiers(FileData *fd, Object *ob) } +static void lib_link_gpencil_modifiers(FileData *fd, Object *ob) +{ + BKE_gpencil_modifiers_foreachIDLink(ob, lib_link_modifiers_common, fd); + + /* If linking from a library, clear 'local' static override flag. */ + if (ob->id.lib != NULL) { + for (GpencilModifierData *mod = ob->greasepencil_modifiers.first; mod != NULL; mod = mod->next) { + mod->flag &= ~eGpencilModifierFlag_StaticOverride_Local; + } + } +} + +static void lib_link_shaderfxs(FileData *fd, Object *ob) +{ + BKE_shaderfx_foreachIDLink(ob, lib_link_modifiers_common, fd); + + /* If linking from a library, clear 'local' static override flag. */ + if (ob->id.lib != NULL) { + for (ShaderFxData *fx = ob->shader_fx.first; fx != NULL; fx = fx->next) { + fx->flag &= ~eShaderFxFlag_StaticOverride_Local; + } + } +} + static void lib_link_object(FileData *fd, Main *main) { bool warn = false; @@ -4961,6 +5032,8 @@ static void lib_link_object(FileData *fd, Main *main) lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem); lib_link_modifiers(fd, ob); + lib_link_gpencil_modifiers(fd, ob); + lib_link_shaderfxs(fd, ob); if (ob->rigidbody_constraint) { ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1); @@ -5356,6 +5429,61 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) } } +static void direct_link_gpencil_modifiers(FileData *fd, ListBase *lb) +{ + GpencilModifierData *md; + + link_list(fd, lb); + + for (md = lb->first; md; md = md->next) { + md->error = NULL; + + /* if modifiers disappear, or for upward compatibility */ + if (NULL == BKE_gpencil_modifierType_getInfo(md->type)) + md->type = eModifierType_None; + + if (md->type == eGpencilModifierType_Lattice) { + LatticeGpencilModifierData *gpmd = (LatticeGpencilModifierData*)md; + gpmd->cache_data = NULL; + } + else if (md->type == eGpencilModifierType_Hook) { + HookGpencilModifierData *hmd = (HookGpencilModifierData *)md; + + hmd->curfalloff = newdataadr(fd, hmd->curfalloff); + if (hmd->curfalloff) { + direct_link_curvemapping(fd, hmd->curfalloff); + } + } + 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 */ + curvemapping_initialize(gpmd->curve_thickness); + } + } + + } +} + +static void direct_link_shaderfxs(FileData *fd, ListBase *lb) +{ + ShaderFxData *fx; + + link_list(fd, lb); + + for (fx = lb->first; fx; fx = fx->next) { + fx->error = NULL; + + /* if shader disappear, or for upward compatibility */ + if (NULL == BKE_shaderfxType_getInfo(fx->type)) + fx->type = eShaderFxType_None; + + } +} + static void direct_link_object(FileData *fd, Object *ob) { PartEff *paf; @@ -5399,6 +5527,8 @@ static void direct_link_object(FileData *fd, Object *ob) /* do it here, below old data gets converted */ direct_link_modifiers(fd, &ob->modifiers); + direct_link_gpencil_modifiers(fd, &ob->greasepencil_modifiers); + direct_link_shaderfxs(fd, &ob->shader_fx); link_list(fd, &ob->effect); paf= ob->effect.first; @@ -5879,6 +6009,7 @@ static void lib_link_scene(FileData *fd, Main *main) link_paint(fd, sce, &sce->toolsettings->wpaint->paint); link_paint(fd, sce, &sce->toolsettings->imapaint.paint); link_paint(fd, sce, &sce->toolsettings->uvsculpt->paint); + link_paint(fd, sce, &sce->toolsettings->gp_paint->paint); if (sce->toolsettings->sculpt) sce->toolsettings->sculpt->gravity_object = @@ -6137,6 +6268,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_paint_helper(fd, (Paint**)&sce->toolsettings->vpaint); direct_link_paint_helper(fd, (Paint**)&sce->toolsettings->wpaint); direct_link_paint_helper(fd, (Paint**)&sce->toolsettings->uvsculpt); + direct_link_paint_helper(fd, (Paint**)&sce->toolsettings->gp_paint); direct_link_paint(fd, &sce->toolsettings->imapaint.paint); @@ -6146,28 +6278,16 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->particle.object = NULL; sce->toolsettings->gp_sculpt.paintcursor = NULL; - /* relink grease pencil drawing brushes */ - link_list(fd, &sce->toolsettings->gp_brushes); - for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { - brush->cur_sensitivity = newdataadr(fd, brush->cur_sensitivity); - if (brush->cur_sensitivity) { - direct_link_curvemapping(fd, brush->cur_sensitivity); - } - brush->cur_strength = newdataadr(fd, brush->cur_strength); - if (brush->cur_strength) { - direct_link_curvemapping(fd, brush->cur_strength); - } - brush->cur_jitter = newdataadr(fd, brush->cur_jitter); - if (brush->cur_jitter) { - direct_link_curvemapping(fd, brush->cur_jitter); - } - } - /* relink grease pencil interpolation curves */ sce->toolsettings->gp_interpolate.custom_ipo = newdataadr(fd, sce->toolsettings->gp_interpolate.custom_ipo); if (sce->toolsettings->gp_interpolate.custom_ipo) { direct_link_curvemapping(fd, sce->toolsettings->gp_interpolate.custom_ipo); } + /* relink grease pencil multiframe falloff curve */ + sce->toolsettings->gp_sculpt.cur_falloff = newdataadr(fd, sce->toolsettings->gp_sculpt.cur_falloff); + if (sce->toolsettings->gp_sculpt.cur_falloff) { + direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_falloff); + } } if (sce->ed) { @@ -6405,11 +6525,24 @@ static void direct_link_scene(FileData *fd, Scene *sce) /* relink's grease pencil data's refs */ static void lib_link_gpencil(FileData *fd, Main *main) { + /* Relink all datablock linked by GP datablock */ for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { if (gpd->id.tag & LIB_TAG_NEED_LINK) { + /* Layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* Layer -> Parent References */ + gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent); + } + + /* Datablock Stuff */ IDP_LibLinkProperty(gpd->id.properties, fd); lib_link_animdata(fd, &gpd->id, gpd->adt); + /* materials */ + for (int a = 0; a < gpd->totcol; a++) { + gpd->mat[a] = newlibadr_us(fd, gpd->id.lib, gpd->mat[a]); + } + gpd->id.tag &= ~LIB_TAG_NEED_LINK; } } @@ -6431,36 +6564,49 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) gpd->adt = newdataadr(fd, gpd->adt); direct_link_animdata(fd, gpd->adt); - /* relink palettes */ + /* relink palettes (old palettes deprecated, only to convert old files) */ link_list(fd, &gpd->palettes); - for (palette = gpd->palettes.first; palette; palette = palette->next) { - link_list(fd, &palette->colors); + if (gpd->palettes.first != NULL) { + for (palette = gpd->palettes.first; palette; palette = palette->next) { + link_list(fd, &palette->colors); + } } + /* clear drawing cache */ + gpd->runtime.batch_cache_data = NULL; + + /* materials */ + gpd->mat = newdataadr(fd, gpd->mat); + test_pointer_array(fd, (void **)&gpd->mat); + /* relink layers */ link_list(fd, &gpd->layers); for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* parent */ - gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent); /* relink frames */ link_list(fd, &gpl->frames); + gpl->actframe = newdataadr(fd, gpl->actframe); + gpl->runtime.derived_data = NULL; + gpl->runtime.icon_id = 0; + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { /* relink strokes (and their points) */ link_list(fd, &gpf->strokes); for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* relink stroke points array */ gps->points = newdataadr(fd, gps->points); + /* relink weight data */ + 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_CACHES; - /* the color pointer is not saved, so need to be recalculated using the color name */ - gps->palcolor = NULL; - gps->flag |= GP_STROKE_RECALC_COLOR; } } } @@ -8618,6 +8764,11 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) user->walk_navigation.jump_height = 0.4f; /* m */ 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; + } } static void do_versions(FileData *fd, Library *lib, Main *main) @@ -8696,8 +8847,8 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_action(fd, main); lib_link_vfont(fd, main); lib_link_nodetree(fd, main); /* has to be done after scene/materials, this will verify group nodes */ - lib_link_brush(fd, main); lib_link_palette(fd, main); + lib_link_brush(fd, main); lib_link_paint_curve(fd, main); lib_link_particlesettings(fd, main); lib_link_movieclip(fd, main); @@ -9434,6 +9585,9 @@ static void expand_brush(FileData *fd, Main *mainvar, Brush *brush) expand_doit(fd, mainvar, brush->mask_mtex.tex); expand_doit(fd, mainvar, brush->clone.image); expand_doit(fd, mainvar, brush->paint_curve); + if (brush->gpencil_settings != NULL) { + expand_doit(fd, mainvar, brush->gpencil_settings->material); + } } static void expand_material(FileData *fd, Main *mainvar, Material *ma) @@ -9445,6 +9599,12 @@ static void expand_material(FileData *fd, Main *mainvar, Material *ma) if (ma->nodetree) expand_nodetree(fd, mainvar, ma->nodetree); + + if (ma->gp_style) { + MaterialGPencilStyle *gp_style = ma->gp_style; + expand_doit(fd, mainvar, gp_style->sima); + expand_doit(fd, mainvar, gp_style->ima); + } } static void expand_lamp(FileData *fd, Main *mainvar, Lamp *la) @@ -9621,6 +9781,24 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) modifiers_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data); } + /* expand_object_expandModifier() */ + if (ob->greasepencil_modifiers.first) { + struct { FileData *fd; Main *mainvar; } data; + data.fd = fd; + data.mainvar = mainvar; + + BKE_gpencil_modifiers_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data); + } + + /* expand_object_expandShaderFx() */ + if (ob->shader_fx.first) { + struct { FileData *fd; Main *mainvar; } data; + data.fd = fd; + data.mainvar = mainvar; + + BKE_shaderfx_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data); + } + expand_pose(fd, mainvar, ob->pose); expand_doit(fd, mainvar, ob->poselib); expand_constraints(fd, mainvar, &ob->constraints); @@ -9899,8 +10077,18 @@ static void expand_linestyle(FileData *fd, Main *mainvar, FreestyleLineStyle *li static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd) { - if (gpd->adt) + if (gpd->adt) { expand_animdata(fd, mainvar, gpd->adt); + } + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + expand_doit(fd, mainvar, gpl->parent); + } + + for (int a = 0; a < gpd->totcol; a++) { + expand_doit(fd, mainvar, gpd->mat[a]); + } + } static void expand_workspace(FileData *fd, Main *mainvar, WorkSpace *workspace) diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 7a032dc3c90..f2f2e7d7881 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1685,7 +1685,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_VIEW3D: { View3D *v3d = (View3D *)sl; - v3d->flag2 |= V3D_SHOW_GPENCIL; + v3d->flag2 |= V3D_SHOW_ANNOTATION; break; } case SPACE_SEQ: @@ -1709,7 +1709,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - sclip->flag |= SC_SHOW_GPENCIL; + sclip->flag |= SC_SHOW_ANNOTATION; break; } } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 7a106611e64..fadf332c850 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -76,6 +76,9 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" #include "BLO_readfile.h" @@ -87,6 +90,64 @@ #include "MEM_guardedalloc.h" +/* ************************************************** */ +/* GP Palettes API (Deprecated) */ + +/* add a new gp-palette */ +static bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name) +{ + bGPDpalette *palette; + + /* check that list is ok */ + if (gpd == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); + + /* add to datablock */ + BLI_addtail(&gpd->palettes, palette); + + /* set basic settings */ + /* auto-name */ + BLI_strncpy(palette->info, name, sizeof(palette->info)); + BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), + sizeof(palette->info)); + + /* return palette */ + return palette; +} + +/* add a new gp-palettecolor */ +static bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name) +{ + bGPDpalettecolor *palcolor; + + /* check that list is ok */ + if (palette == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); + + /* add to datablock */ + BLI_addtail(&palette->colors, palcolor); + + /* set basic settings */ + copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); + ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); + + /* auto-name */ + BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); + BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), + sizeof(palcolor->info)); + + /* return palette color */ + return palcolor; +} + /** * Setup rotation stabilization from ancient single track spec. * Former Version of 2D stabilization used a single tracking marker to determine the rotation @@ -1344,8 +1405,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) ToolSettings *ts = scene->toolsettings; /* initialize use position for sculpt brushes */ ts->gp_sculpt.flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; - /* initialize selected vertices alpha factor */ - ts->gp_sculpt.alpha = 1.0f; /* new strength sculpt brush */ if (ts->gp_sculpt.brush[0].size >= 11) { @@ -1358,25 +1417,16 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; } } - /* create a default grease pencil drawing brushes set */ - if (!BLI_listbase_is_empty(&bmain->gpencil)) { - for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { - ToolSettings *ts = scene->toolsettings; - if (BLI_listbase_is_empty(&ts->gp_brushes)) { - BKE_gpencil_brush_init_presets(ts); - } - } - } /* Convert Grease Pencil to new palettes/brushes * Loop all strokes and create the palette and all colors */ for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { if (BLI_listbase_is_empty(&gpd->palettes)) { /* create palette */ - bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette", true); + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette"); for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* create color using layer name */ - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info, true); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info); if (palcolor != NULL) { /* set color attributes */ copy_v4_v4(palcolor->color, gpl->color); @@ -1386,7 +1436,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (gpl->flag & GP_LAYER_LOCKED) palcolor->flag |= PC_COLOR_LOCKED; if (gpl->flag & GP_LAYER_ONIONSKIN) palcolor->flag |= PC_COLOR_ONIONSKIN; if (gpl->flag & GP_LAYER_VOLUMETRIC) palcolor->flag |= PC_COLOR_VOLUMETRIC; - if (gpl->flag & GP_LAYER_HQ_FILL) palcolor->flag |= PC_COLOR_HQ_FILL; /* set layer opacity to 1 */ gpl->opacity = 1.0f; @@ -1399,8 +1448,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { /* set stroke to palette and force recalculation */ BLI_strncpy(gps->colorname, gpl->info, sizeof(gps->colorname)); - gps->palcolor = NULL; - gps->flag |= GP_STROKE_RECALC_COLOR; gps->thickness = gpl->thickness; /* set alpha strength to 1 */ @@ -1410,13 +1457,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* set thickness to 0 (now it is a factor to override stroke thickness) */ - gpl->thickness = 0.0f; } - /* set first color as active */ - if (palette->colors.first) - BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); } } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 5b0a12a0b4c..7339f1977ff 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -54,16 +54,19 @@ #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "DNA_genfile.h" +#include "DNA_gpencil_types.h" #include "DNA_workspace_types.h" #include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_customdata.h" +#include "BKE_colortools.h" #include "BKE_freestyle.h" #include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" #include "BKE_pointcache.h" @@ -73,6 +76,9 @@ #include "BKE_sequencer.h" #include "BKE_studiolight.h" #include "BKE_workspace.h" +#include "BKE_gpencil.h" +#include "BKE_paint.h" +#include "BKE_object.h" #include "BLO_readfile.h" #include "readfile.h" @@ -743,6 +749,7 @@ void do_versions_after_linking_280(Main *bmain) } } #endif + } /* NOTE: this version patch is intended for versions < 2.52.2, but was initially introduced in 2.27 already. @@ -839,7 +846,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (ntree->type == NTREE_SHADER) { for (bNode *node = ntree->nodes.first; node; node = node->next) { if (node->type == 194 /* SH_NODE_EEVEE_METALLIC */ && - STREQ(node->idname, "ShaderNodeOutputMetallic")) + STREQ(node->idname, "ShaderNodeOutputMetallic")) { BLI_strncpy(node->idname, "ShaderNodeEeveeMetallic", sizeof(node->idname)); error |= NTREE_DOVERSION_NEED_OUTPUT; @@ -851,14 +858,14 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } else if (node->type == 196 /* SH_NODE_OUTPUT_EEVEE_MATERIAL */ && - STREQ(node->idname, "ShaderNodeOutputEeveeMaterial")) + STREQ(node->idname, "ShaderNodeOutputEeveeMaterial")) { node->type = SH_NODE_OUTPUT_MATERIAL; BLI_strncpy(node->idname, "ShaderNodeOutputMaterial", sizeof(node->idname)); } else if (node->type == 194 /* SH_NODE_EEVEE_METALLIC */ && - STREQ(node->idname, "ShaderNodeEeveeMetallic")) + STREQ(node->idname, "ShaderNodeEeveeMetallic")) { node->type = SH_NODE_BSDF_PRINCIPLED; BLI_strncpy(node->idname, "ShaderNodeBsdfPrincipled", sizeof(node->idname)); @@ -869,10 +876,10 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END - if (error & NTREE_DOVERSION_NEED_OUTPUT) { - BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console"); - printf("You need to connect Principled and Eevee Specular shader nodes to new material output nodes.\n"); - } + if (error & NTREE_DOVERSION_NEED_OUTPUT) { + BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console"); + printf("You need to connect Principled and Eevee Specular shader nodes to new material output nodes.\n"); + } if (error & NTREE_DOVERSION_TRANSPARENCY_EMISSION) { BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console"); @@ -896,6 +903,68 @@ 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_BrushEdit_Settings", "int", "weighttype")) { + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { + /* sculpt brushes */ + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + if (gset) { + gset->weighttype = GP_EDITBRUSH_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_EditBrush_Data *gp_brush; + + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { + ToolSettings *ts = scene->toolsettings; + /* sculpt brushes */ + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + for (int i = 0; i < TOT_GP_EDITBRUSH_TYPES; ++i) { + gp_brush = &gset->brush[i]; + gp_brush->flag |= GP_EDITBRUSH_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->gpencil.first; gpd; gpd = gpd->id.next) { + ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f); + } + } + + /* Init grease pencil pixel size factor */ + if (!DNA_struct_elem_find(fd->filesdna, "bGPDdata", "int", "pixfactor")) { + for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; + } + } + + /* Grease pencil multiframe falloff curve */ + if (!DNA_struct_elem_find(fd->filesdna, "GP_BrushEdit_Settings", "CurveMapping", "cur_falloff")) { + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { + /* sculpt brushes */ + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + if ((gset) && (gset->cur_falloff == NULL)) { + gset->cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + curvemapping_initialize(gset->cur_falloff); + curvemap_reset(gset->cur_falloff->cm, + &gset->cur_falloff->clipr, + CURVE_PRESET_GAUSS, + CURVEMAP_SLOPE_POSITIVE); + } + } + } + } } #ifdef USE_COLLECTION_COMPAT_28 @@ -915,6 +984,26 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } #endif + 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]")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_grid_scale = 1.0f; // Scale + v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES; // NUmber of lines + v3d->overlay.gpencil_paper_opacity = 0.5f; + v3d->overlay.gpencil_grid_axis = V3D_GP_GRID_AXIS_Y; + v3d->overlay.gpencil_grid_opacity = 0.9f; + } + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 280, 6)) { if (DNA_struct_elem_find(fd->filesdna, "SpaceOops", "int", "filter") == false) { bScreen *sc; @@ -1017,6 +1106,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) tex->type = 0; } } + } if (!MAIN_VERSION_ATLEAST(bmain, 280, 11)) { @@ -1643,6 +1733,101 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_screen_view3d_shading_init(&scene->display.shading); } } + /* initialize grease pencil view data */ + if (!DNA_struct_elem_find(fd->filesdna, "SpaceView3D", "float", "vertex_opacity")) { + for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) { + for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->vertex_opacity = 1.0f; + v3d->flag3 |= V3D_GP_SHOW_EDIT_LINES; + } + } + } + } + } + + } + + if (!MAIN_VERSION_ATLEAST(bmain, 280, 22)) { + if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "annotate_v3d_align")) { + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { + scene->toolsettings->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + scene->toolsettings->annotate_thickness = 3; + } + } + if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "short", "line_change")) { + for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->line_change = gpl->thickness; + if ((gpl->thickness < 1) || (gpl->thickness > 10)) { + gpl->thickness = 3; + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_grid_scale")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_grid_scale = 1.0f; + } + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_paper_opacity")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_paper_opacity = 0.5f; + } + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_grid_opacity")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_grid_opacity = 0.5f; + } + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "int", "gpencil_grid_axis")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_grid_axis = V3D_GP_GRID_AXIS_Y; + } + } + } + } + } + if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "int", "gpencil_grid_lines")) { + for (bScreen *screen = bmain->screen.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) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES; + } + } + } + } + } + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index bd7334516ca..a86986e2e09 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -170,46 +170,77 @@ void BLO_update_defaults_startup_blend(Main *bmain) if (ts->gp_sculpt.brush[0].size == 0) { GP_BrushEdit_Settings *gset = &ts->gp_sculpt; GP_EditBrush_Data *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); + + /* default sculpt brush */ + gset->brushtype = GP_EDITBRUSH_TYPE_PUSH; + /* default weight paint brush */ + gset->weighttype = GP_EDITBRUSH_TYPE_WEIGHT; brush = &gset->brush[GP_EDITBRUSH_TYPE_SMOOTH]; brush->size = 25; brush->strength = 0.3f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_THICKNESS]; brush->size = 25; brush->strength = 0.5f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; brush->size = 25; brush->strength = 0.5f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; brush->size = 50; brush->strength = 0.3f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_PUSH]; brush->size = 25; brush->strength = 0.3f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_TWIST]; brush->size = 50; brush->strength = 0.3f; // XXX? - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_PINCH]; brush->size = 50; brush->strength = 0.5f; // XXX? - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); brush = &gset->brush[GP_EDITBRUSH_TYPE_RANDOMIZE]; brush->size = 25; brush->strength = 0.5f; - brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); + + brush = &gset->brush[GP_EDITBRUSH_TYPE_WEIGHT]; + brush->size = 25; + brush->strength = 0.5f; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF | GP_EDITBRUSH_FLAG_ENABLE_CURSOR; + copy_v3_v3(brush->curcolor_add, curcolor_add); + copy_v3_v3(brush->curcolor_sub, curcolor_sub); } ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE; @@ -217,6 +248,9 @@ void BLO_update_defaults_startup_blend(Main *bmain) ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE; ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE; + ts->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + ts->annotate_thickness = 3; + ParticleEditSettings *pset = &ts->particle; for (int a = 0; a < ARRAY_SIZE(pset->brush); a++) { pset->brush[a].strength = 0.5f; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 503f8b44ec3..3883e024ab7 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -118,6 +118,8 @@ #include "DNA_genfile.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_fileglobal_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" @@ -165,6 +167,7 @@ #include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_global.h" // for G +#include "BKE_gpencil_modifier.h" #include "BKE_idcode.h" #include "BKE_layer.h" #include "BKE_library.h" // for set_listbasepointers @@ -173,6 +176,7 @@ #include "BKE_node.h" #include "BKE_report.h" #include "BKE_sequencer.h" +#include "BKE_shader_fx.h" #include "BKE_subsurf.h" #include "BKE_modifier.h" #include "BKE_fcurve.h" @@ -1788,6 +1792,57 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) } } +static void write_gpencil_modifiers(WriteData *wd, ListBase *modbase) +{ + GpencilModifierData *md; + + if (modbase == NULL) { + return; + } + + for (md = modbase->first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + if (mti == NULL) { + return; + } + + writestruct_id(wd, DATA, mti->struct_name, 1, md); + + if (md->type == eGpencilModifierType_Thick) { + ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; + + if (gpmd->curve_thickness) { + write_curvemapping(wd, gpmd->curve_thickness); + } + } + else if (md->type == eGpencilModifierType_Hook) { + HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md; + + if (gpmd->curfalloff) { + write_curvemapping(wd, gpmd->curfalloff); + } + } + } +} + +static void write_shaderfxs(WriteData *wd, ListBase *fxbase) +{ + ShaderFxData *fx; + + if (fxbase == NULL) { + return; + } + + for (fx = fxbase->first; fx; fx = fx->next) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + if (fxi == NULL) { + return; + } + + writestruct_id(wd, DATA, fxi->struct_name, 1, fx); + } +} + static void write_object(WriteData *wd, Object *ob) { if (ob->id.us > 0 || wd->use_memfile) { @@ -1842,6 +1897,8 @@ static void write_object(WriteData *wd, Object *ob) write_particlesystems(wd, &ob->particlesystem); write_modifiers(wd, &ob->modifiers); + write_gpencil_modifiers(wd, &ob->greasepencil_modifiers); + write_shaderfxs(wd, &ob->shader_fx); writelist(wd, DATA, LinkData, &ob->pc_ids); writelist(wd, DATA, LodLevel, &ob->lodlevels); @@ -2260,6 +2317,11 @@ static void write_material(WriteData *wd, Material *ma) } write_previews(wd, ma->preview); + + /* grease pencil settings */ + if (ma->gp_style) { + writestruct(wd, DATA, MaterialGPencilStyle, 1, ma->gp_style); + } } } @@ -2463,24 +2525,18 @@ static void write_scene(WriteData *wd, Scene *sce) writestruct(wd, DATA, UvSculpt, 1, tos->uvsculpt); write_paint(wd, &tos->uvsculpt->paint); } - /* write grease-pencil drawing brushes to file */ - writelist(wd, DATA, bGPDbrush, &tos->gp_brushes); - for (bGPDbrush *brush = tos->gp_brushes.first; brush; brush = brush->next) { - if (brush->cur_sensitivity) { - write_curvemapping(wd, brush->cur_sensitivity); - } - if (brush->cur_strength) { - write_curvemapping(wd, brush->cur_strength); - } - if (brush->cur_jitter) { - write_curvemapping(wd, brush->cur_jitter); - } + if (tos->gp_paint) { + writestruct(wd, DATA, GpPaint, 1, tos->gp_paint); + write_paint(wd, &tos->gp_paint->paint); } /* write grease-pencil custom ipo curve to file */ if (tos->gp_interpolate.custom_ipo) { write_curvemapping(wd, tos->gp_interpolate.custom_ipo); } - + /* write grease-pencil multiframe falloff curve to file */ + if (tos->gp_sculpt.cur_falloff) { + write_curvemapping(wd, tos->gp_sculpt.cur_falloff); + } write_paint(wd, &tos->imapaint.paint); @@ -2654,6 +2710,8 @@ static void write_gpencil(WriteData *wd, bGPdata *gpd) write_animdata(wd, gpd->adt); } + writedata(wd, DATA, sizeof(void *) * gpd->totcol, gpd->mat); + /* write grease-pencil layers to file */ writelist(wd, DATA, bGPDlayer, &gpd->layers); for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { @@ -2664,15 +2722,10 @@ static void write_gpencil(WriteData *wd, bGPdata *gpd) writelist(wd, DATA, bGPDstroke, &gpf->strokes); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { writestruct(wd, DATA, bGPDspoint, gps->totpoints, gps->points); + write_dverts(wd, gps->totpoints, gps->dvert); } } } - - /* write grease-pencil palettes */ - writelist(wd, DATA, bGPDpalette, &gpd->palettes); - for (bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { - writelist(wd, DATA, bGPDpalettecolor, &palette->colors); - } } } @@ -3159,6 +3212,20 @@ static void write_brush(WriteData *wd, Brush *brush) if (brush->curve) { write_curvemapping(wd, brush->curve); } + + if (brush->gpencil_settings) { + writestruct(wd, DATA, BrushGpencilSettings, 1, brush->gpencil_settings); + + if (brush->gpencil_settings->curve_sensitivity) { + write_curvemapping(wd, brush->gpencil_settings->curve_sensitivity); + } + if (brush->gpencil_settings->curve_strength) { + write_curvemapping(wd, brush->gpencil_settings->curve_strength); + } + if (brush->gpencil_settings->curve_jitter) { + write_curvemapping(wd, brush->gpencil_settings->curve_jitter); + } + } if (brush->gradient) { writestruct(wd, DATA, ColorBand, 1, brush->gradient); } diff --git a/source/blender/collada/SceneExporter.cpp b/source/blender/collada/SceneExporter.cpp index 47d8f4f52bb..248c780102c 100644 --- a/source/blender/collada/SceneExporter.cpp +++ b/source/blender/collada/SceneExporter.cpp @@ -69,6 +69,7 @@ void SceneExporter::exportHierarchy(bContext *C, Depsgraph *depsgraph, Scene *sc case OB_CAMERA: case OB_LAMP: case OB_EMPTY: + case OB_GPENCIL: case OB_ARMATURE: base_objects.push_back(ob); break; @@ -122,6 +123,7 @@ void SceneExporter::writeNodes(bContext *C, Depsgraph *depsgraph, Object *ob, Sc case OB_CAMERA: case OB_LAMP: case OB_EMPTY: + case OB_GPENCIL: case OB_ARMATURE: if (bc_is_marked(cob)) child_objects.push_back(cob); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 1b68a73bbd7..e20b589bf22 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -77,6 +77,8 @@ extern "C" { #include "BKE_curve.h" #include "BKE_effect.h" #include "BKE_fcurve.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_idcode.h" #include "BKE_key.h" #include "BKE_lattice.h" @@ -93,6 +95,7 @@ extern "C" { #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_rigidbody.h" +#include "BKE_shader_fx.h" #include "BKE_sound.h" #include "BKE_tracking.h" #include "BKE_world.h" @@ -514,6 +517,18 @@ void DepsgraphNodeBuilder::build_object(int base_index, data.builder = this; modifiers_foreachIDLink(object, modifier_walk, &data); } + /* Grease Pencil Modifiers. */ + if (object->greasepencil_modifiers.first != NULL) { + BuilderWalkUserData data; + data.builder = this; + BKE_gpencil_modifiers_foreachIDLink(object, modifier_walk, &data); + } + /* Shadr FX. */ + if (object->shader_fx.first != NULL) { + BuilderWalkUserData data; + data.builder = this; + BKE_shaderfx_foreachIDLink(object, modifier_walk, &data); + } /* Constraints. */ if (object->constraints.first != NULL) { BuilderWalkUserData data; @@ -538,10 +553,6 @@ void DepsgraphNodeBuilder::build_object(int base_index, if (object->particlesystem.first != NULL) { build_particles(object); } - /* Grease pencil. */ - if (object->gpd != NULL) { - build_gpencil(object->gpd); - } /* Proxy object to copy from. */ if (object->proxy_from != NULL) { build_object(-1, object->proxy_from, DEG_ID_LINKED_INDIRECTLY); @@ -592,6 +603,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) case OB_SURF: case OB_MBALL: case OB_LATTICE: + case OB_GPENCIL: build_object_data_geometry(object); /* TODO(sergey): Only for until we support granular * update of curves. @@ -1213,6 +1225,20 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata) op_node->set_as_entry(); break; } + + case ID_GD: + { + /* GPencil evaluation operations. */ + op_node = add_operation_node(obdata, + DEG_NODE_TYPE_GEOMETRY, + function_bind(BKE_gpencil_eval_geometry, + _1, + (bGPdata *)obdata_cow), + DEG_OPCODE_PLACEHOLDER, + "Geometry Eval"); + op_node->set_as_entry(); + break; + } default: BLI_assert(!"Should not happen"); break; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc index f1db05b7220..3d7b2d6d232 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc @@ -134,10 +134,6 @@ void DepsgraphNodeBuilder::build_view_layer( if (scene->nodetree != NULL) { build_compositor(scene); } - /* Grease pencil. */ - if (scene->gpd != NULL) { - build_gpencil(scene->gpd); - } /* Cache file. */ LISTBASE_FOREACH (CacheFile *, cachefile, &bmain_->cachefiles) { build_cachefile(cachefile); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 1a0c621ab43..c9a00b0bf0f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -85,10 +85,12 @@ extern "C" { #include "BKE_material.h" #include "BKE_mball.h" #include "BKE_modifier.h" +#include "BKE_gpencil_modifier.h" #include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_rigidbody.h" +#include "BKE_shader_fx.h" #include "BKE_sound.h" #include "BKE_tracking.h" #include "BKE_world.h" @@ -525,6 +527,18 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) data.builder = this; modifiers_foreachIDLink(object, modifier_walk, &data); } + /* Grease Pencil Modifiers. */ + if (object->greasepencil_modifiers.first != NULL) { + BuilderWalkUserData data; + data.builder = this; + BKE_gpencil_modifiers_foreachIDLink(object, modifier_walk, &data); + } + /* Shader FX. */ + if (object->shader_fx.first != NULL) { + BuilderWalkUserData data; + data.builder = this; + BKE_shaderfx_foreachIDLink(object, modifier_walk, &data); + } /* Constraints. */ if (object->constraints.first != NULL) { BuilderWalkUserData data; @@ -570,10 +584,6 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) if (object->particlesystem.first != NULL) { build_particles(object); } - /* Grease pencil. */ - if (object->gpd != NULL) { - build_gpencil(object->gpd); - } /* Proxy object to copy from. */ if (object->proxy_from != NULL) { build_object(NULL, object->proxy_from); @@ -630,6 +640,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) case OB_SURF: case OB_MBALL: case OB_LATTICE: + case OB_GPENCIL: { build_object_data_geometry(object); break; @@ -1798,6 +1809,42 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) } } } + /* Grease Pencil Modifiers */ + if (object->greasepencil_modifiers.first != NULL) { + ModifierUpdateDepsgraphContext ctx = {}; + ctx.scene = scene_; + ctx.object = object; + LISTBASE_FOREACH(GpencilModifierData *, md, &object->greasepencil_modifiers) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo((GpencilModifierType)md->type); + if (mti->updateDepsgraph) { + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); + ctx.node = reinterpret_cast< ::DepsNodeHandle* >(&handle); + mti->updateDepsgraph(md, &ctx); + } + if (BKE_object_modifier_gpencil_use_time(object, md)) { + TimeSourceKey time_src_key; + add_relation(time_src_key, obdata_ubereval_key, "Time Source"); + } + } + } + /* Shader FX */ + if (object->shader_fx.first != NULL) { + ModifierUpdateDepsgraphContext ctx = {}; + ctx.scene = scene_; + ctx.object = object; + LISTBASE_FOREACH(ShaderFxData *, fx, &object->shader_fx) { + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo((ShaderFxType)fx->type); + if (fxi->updateDepsgraph) { + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); + ctx.node = reinterpret_cast< ::DepsNodeHandle* >(&handle); + fxi->updateDepsgraph(fx, &ctx); + } + if (BKE_object_shaderfx_use_time(object, fx)) { + TimeSourceKey time_src_key; + add_relation(time_src_key, obdata_ubereval_key, "Time Source"); + } + } + } /* Materials. */ if (object->totcol) { for (int a = 1; a <= object->totcol; a++) { @@ -1895,13 +1942,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) } /* Link object data evaluation node to exit operation. */ OperationKey obdata_geom_eval_key(obdata, - DEG_NODE_TYPE_GEOMETRY, - DEG_OPCODE_PLACEHOLDER, - "Geometry Eval"); + DEG_NODE_TYPE_GEOMETRY, + DEG_OPCODE_PLACEHOLDER, + "Geometry Eval"); OperationKey obdata_geom_done_key(obdata, - DEG_NODE_TYPE_GEOMETRY, - DEG_OPCODE_PLACEHOLDER, - "Eval Done"); + DEG_NODE_TYPE_GEOMETRY, + DEG_OPCODE_PLACEHOLDER, + "Eval Done"); add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done"); @@ -1917,35 +1964,67 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) Curve *cu = (Curve *)obdata; if (cu->bevobj != NULL) { ComponentKey bevob_geom_key(&cu->bevobj->id, - DEG_NODE_TYPE_GEOMETRY); + DEG_NODE_TYPE_GEOMETRY); add_relation(bevob_geom_key, - obdata_geom_eval_key, - "Curve Bevel Geometry"); + obdata_geom_eval_key, + "Curve Bevel Geometry"); ComponentKey bevob_key(&cu->bevobj->id, - DEG_NODE_TYPE_TRANSFORM); + DEG_NODE_TYPE_TRANSFORM); add_relation(bevob_key, - obdata_geom_eval_key, - "Curve Bevel Transform"); + obdata_geom_eval_key, + "Curve Bevel Transform"); build_object(NULL, cu->bevobj); } if (cu->taperobj != NULL) { ComponentKey taperob_key(&cu->taperobj->id, - DEG_NODE_TYPE_GEOMETRY); + DEG_NODE_TYPE_GEOMETRY); add_relation(taperob_key, obdata_geom_eval_key, "Curve Taper"); build_object(NULL, cu->taperobj); } if (cu->textoncurve != NULL) { ComponentKey textoncurve_key(&cu->textoncurve->id, - DEG_NODE_TYPE_GEOMETRY); + DEG_NODE_TYPE_GEOMETRY); add_relation(textoncurve_key, - obdata_geom_eval_key, - "Text on Curve"); + obdata_geom_eval_key, + "Text on Curve"); build_object(NULL, cu->textoncurve); } break; } case ID_LT: break; + case ID_GD: /* Grease Pencil */ + { + bGPdata *gpd = (bGPdata *)obdata; + + /* Geometry cache needs to be recalculated on frame change + * (e.g. to fix crashes after scrubbing the timeline when + * onion skinning is enabled, since the ghosts need to be + * re-added to the cache once scrubbing ends) + */ + TimeSourceKey time_key; + ComponentKey geometry_key(obdata, DEG_NODE_TYPE_GEOMETRY); + add_relation(time_key, + geometry_key, + "GP Frame Change"); + + /* Geometry cache also needs to be recalculated when Material + * settings change (e.g. when fill.opacity changes on/off, + * we need to rebuild the bGPDstroke->triangles caches) + */ + for (int i = 0; i < gpd->totcol; i++) { + Material *ma = gpd->mat[i]; + if ((ma != NULL) && (ma->gp_style != NULL)) { + OperationKey material_key(&ma->id, + DEG_NODE_TYPE_SHADING, + DEG_OPCODE_MATERIAL_UPDATE); + add_relation(material_key, + geometry_key, + "Material -> GP Data"); + } + } + break; + } default: BLI_assert(!"Should not happen"); break; @@ -2228,6 +2307,11 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDDepsNode *id_node if (id_type == ID_ME && comp_node->type == DEG_NODE_TYPE_GEOMETRY) { rel_flag &= ~DEPSREL_FLAG_NO_FLUSH; } + /* materials need update grease pencil objects */ + if (id_type == ID_MA) { + rel_flag &= ~DEPSREL_FLAG_NO_FLUSH; + } + /* Notes on exceptions: * - Parameters component is where drivers are living. Changing any * of the (custom) properties in the original datablock (even the diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc index f069c63f138..78d1a930eb7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc @@ -123,10 +123,6 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene, ViewLayer *view_la if (scene->nodetree != NULL) { build_compositor(scene); } - /* Grease pencil. */ - if (scene->gpd != NULL) { - build_gpencil(scene->gpd); - } /* Masks. */ LISTBASE_FOREACH (Mask *, mask, &bmain_->mask) { build_mask(mask); diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 4229f8bf9a2..e4659a7a94d 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -100,6 +100,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, case OB_FONT: case OB_LATTICE: case OB_MBALL: + case OB_GPENCIL: *component_type = DEG_NODE_TYPE_GEOMETRY; break; case OB_ARMATURE: @@ -112,11 +113,17 @@ void depsgraph_geometry_tag_to_component(const ID *id, case ID_ME: *component_type = DEG_NODE_TYPE_GEOMETRY; break; - case ID_PA: + case ID_PA: /* Particles */ return; case ID_LP: *component_type = DEG_NODE_TYPE_PARAMETERS; break; + case ID_GD: + *component_type = DEG_NODE_TYPE_GEOMETRY; + break; + case ID_PAL: /* Palettes */ + *component_type = DEG_NODE_TYPE_PARAMETERS; + break; default: break; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 6ec7b03501b..d672645dea0 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -123,6 +123,13 @@ set(SRC engines/workbench/solid_mode.c engines/workbench/transparent_mode.c engines/external/external_engine.c + engines/gpencil/gpencil_engine.h + engines/gpencil/gpencil_engine.c + engines/gpencil/gpencil_render.c + engines/gpencil/gpencil_cache_utils.c + engines/gpencil/gpencil_draw_utils.c + engines/gpencil/gpencil_draw_cache_impl.c + engines/gpencil/gpencil_shader_fx.c DRW_engine.h intern/DRW_render.h @@ -303,6 +310,33 @@ data_to_c_simple(modes/shaders/particle_strand_frag.glsl SRC) data_to_c_simple(modes/shaders/particle_strand_vert.glsl SRC) data_to_c_simple(modes/shaders/volume_velocity_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_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_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_swirl_frag.glsl SRC) +data_to_c_simple(engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl SRC) + + list(APPEND INC ) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 4d4b486d247..6d8e8a69e29 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -126,6 +126,9 @@ void DRW_draw_depth_loop( struct Depsgraph *depsgraph, struct ARegion *ar, struct View3D *v3d); +/* grease pencil render */ +void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph); + /* This is here because GPUViewport needs it */ void DRW_pass_free(struct DRWPass *pass); struct DRWInstanceDataList *DRW_instance_data_list_create(void); diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c new file mode 100644 index 00000000000..4e01c42d33d --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -0,0 +1,296 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_cache_utils.c + * \ingroup draw + */ + +#include "DRW_render.h" + +#include "BKE_global.h" + +#include "ED_gpencil.h" +#include "ED_view3d.h" + +#include "DNA_gpencil_types.h" +#include "DNA_view3d_types.h" + +#include "gpencil_engine.h" + +#include "draw_cache_impl.h" + + /* add a gpencil object to cache to defer drawing */ +tGPencilObjectCache *gpencil_object_cache_add(tGPencilObjectCache *cache_array, Object *ob, bool is_temp, + int *gp_cache_size, int *gp_cache_used) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + tGPencilObjectCache *cache_elem = NULL; + RegionView3D *rv3d = draw_ctx->rv3d; + 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; + } + + /* zero out all pointers */ + cache_elem = &cache_array[*gp_cache_used]; + memset(cache_elem, 0, sizeof(*cache_elem)); + + /* save object */ + cache_elem->ob = ob; + cache_elem->temp_ob = is_temp; + cache_elem->idx = *gp_cache_used; + + cache_elem->init_grp = 0; + cache_elem->end_grp = -1; + + /* calculate zdepth from point of view */ + float zdepth = 0.0; + if (rv3d) { + if (rv3d->is_persp) { + zdepth = ED_view3d_calc_zfac(rv3d, ob->loc, NULL); + } + else { + zdepth = -dot_v3v3(rv3d->viewinv[2], ob->loc); + } + } + 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->loc, plane_cam); + } + } + cache_elem->zdepth = zdepth; + /* increase slots used in cache */ + (*gp_cache_used)++; + + return cache_array; +} + +/* get current cache data */ +static GpencilBatchCache *gpencil_batch_get_element(Object *ob) +{ + bGPdata *gpd = ob->data; + if (gpd->runtime.batch_cache_data == NULL) { + gpd->runtime.batch_cache_data = BLI_ghash_str_new("GP batch cache data"); + return NULL; + } + + return (GpencilBatchCache *) BLI_ghash_lookup(gpd->runtime.batch_cache_data, ob->id.name); +} + +/* verify if cache is valid */ +static bool gpencil_batch_cache_valid(Object *ob, bGPdata *gpd, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_get_element(ob); + + if (cache == NULL) { + return false; + } + + cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); + + if (cfra != cache->cache_frame) { + return false; + } + + if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) { + return false; + } + + if (cache->is_editmode) { + return false; + } + + if (cache->is_dirty) { + return false; + } + + return true; +} + +/* resize the cache to the number of slots */ +static void gpencil_batch_cache_resize(GpencilBatchCache *cache, int slots) +{ + cache->cache_size = slots; + cache->batch_stroke = MEM_recallocN(cache->batch_stroke, sizeof(struct Gwn_Batch *) * slots); + cache->batch_fill = MEM_recallocN(cache->batch_fill, sizeof(struct Gwn_Batch *) * slots); + cache->batch_edit = MEM_recallocN(cache->batch_edit, sizeof(struct Gwn_Batch *) * slots); + cache->batch_edlin = MEM_recallocN(cache->batch_edlin, sizeof(struct Gwn_Batch *) * slots); +} + +/* check size and increase if no free slots */ +void gpencil_batch_cache_check_free_slots(Object *ob) +{ + GpencilBatchCache *cache = gpencil_batch_get_element(ob); + + /* the memory is reallocated by chunks, not for one slot only to improve speed */ + if (cache->cache_idx >= cache->cache_size) { + cache->cache_size += GPENCIL_MIN_BATCH_SLOTS_CHUNK; + gpencil_batch_cache_resize(cache, cache->cache_size); + } +} + +/* cache init */ +static void gpencil_batch_cache_init(Object *ob, int cfra) +{ + GpencilBatchCache *cache = gpencil_batch_get_element(ob); + bGPdata *gpd = ob->data; + + if (G.debug_value >= 664) { + printf("gpencil_batch_cache_init: %s\n", ob->id.name); + } + + if (!cache) { + cache = MEM_callocN(sizeof(*cache), __func__); + BLI_ghash_insert(gpd->runtime.batch_cache_data, ob->id.name, cache); + } + else { + memset(cache, 0, sizeof(*cache)); + } + + cache->cache_size = GPENCIL_MIN_BATCH_SLOTS_CHUNK; + cache->batch_stroke = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Stroke"); + cache->batch_fill = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Fill"); + cache->batch_edit = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edit"); + cache->batch_edlin = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edlin"); + + cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd); + gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY; + + cache->cache_idx = 0; + cache->is_dirty = true; + cache->cache_frame = cfra; +} + +/* clear cache */ +static void gpencil_batch_cache_clear(GpencilBatchCache *cache, bGPdata *gpd) +{ + if (!cache) { + return; + } + + if (cache->cache_size == 0) { + return; + } + + if (G.debug_value >= 664) { + printf("gpencil_batch_cache_clear: %s\n", gpd->id.name); + } + + if (cache->cache_size > 0) { + for (int i = 0; i < cache->cache_size; i++) { + GPU_BATCH_DISCARD_SAFE(cache->batch_stroke[i]); + GPU_BATCH_DISCARD_SAFE(cache->batch_fill[i]); + GPU_BATCH_DISCARD_SAFE(cache->batch_edit[i]); + GPU_BATCH_DISCARD_SAFE(cache->batch_edlin[i]); + } + MEM_SAFE_FREE(cache->batch_stroke); + MEM_SAFE_FREE(cache->batch_fill); + MEM_SAFE_FREE(cache->batch_edit); + MEM_SAFE_FREE(cache->batch_edlin); + } + + MEM_SAFE_FREE(cache); +} + +/* get cache */ +GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra) +{ + bGPdata *gpd = ob->data; + + if (!gpencil_batch_cache_valid(ob, gpd, cfra)) { + if (G.debug_value >= 664) { + printf("gpencil_batch_cache: %s\n", gpd->id.name); + } + + GpencilBatchCache *cache = gpencil_batch_get_element(ob); + if (cache) { + gpencil_batch_cache_clear(cache, gpd); + BLI_ghash_remove(gpd->runtime.batch_cache_data, ob->id.name, NULL, NULL); + } + gpencil_batch_cache_init(ob, cfra); + } + + return gpencil_batch_get_element(ob); +} + +/* set cache as dirty */ +void DRW_gpencil_batch_cache_dirty(bGPdata *gpd) +{ + if (gpd->runtime.batch_cache_data == NULL) { + return; + } + + GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data); + while (!BLI_ghashIterator_done(ihash)) { + GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash); + if (cache) { + cache->is_dirty = true; + } + BLI_ghashIterator_step(ihash); + } + BLI_ghashIterator_free(ihash); +} + +/* free batch cache */ +void DRW_gpencil_batch_cache_free(bGPdata *gpd) +{ + if (gpd->runtime.batch_cache_data == NULL) { + return; + } + + GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data); + while (!BLI_ghashIterator_done(ihash)) { + GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash); + if (cache) { + gpencil_batch_cache_clear(cache, gpd); + } + BLI_ghashIterator_step(ihash); + } + BLI_ghashIterator_free(ihash); + + /* free hash */ + if (gpd->runtime.batch_cache_data) { + BLI_ghash_free(gpd->runtime.batch_cache_data, NULL, NULL); + gpd->runtime.batch_cache_data = 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 new file mode 100644 index 00000000000..dd5db85f3f4 --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_draw_cache_impl.c @@ -0,0 +1,739 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file draw/engines/gpencil/gpencil_draw_cache_impl.c + * \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_gpencil.h" +#include "BKE_action.h" + +#include "DRW_render.h" + +#include "GPU_immediate.h" +#include "GPU_draw.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, float matrix[4][4], const bGPDspoint *pt, int idx, + uint pos_id, uint color_id, + uint thickness_id, uint uvdata_id, short thickness, + const float ink[4]) +{ + float viewfpt[3]; + + 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 */ + mul_v3_m4v3(viewfpt, matrix, &pt->x); + 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 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], 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); +} + +/* create batch geometry data for points stroke shader */ +GPUBatch *DRW_gpencil_get_point_geom(bGPDstroke *gps, short thickness, const float ink[4]) +{ + static GPUVertFormat format = { 0 }; + static uint pos_id, color_id, size_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); + size_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, gps->totpoints); + + /* draw stroke curve */ + const bGPDspoint *pt = gps->points; + int idx = 0; + 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(vbo, color_id, idx, col); + GPU_vertbuf_attr_set(vbo, size_id, idx, &thick); + + /* 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); + + GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); + idx++; + } + + return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +/* create batch geometry data for stroke shader */ +GPUBatch *DRW_gpencil_get_stroke_geom(bGPDframe *gpf, 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; + + 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, totpoints + cyclic_add + 2); + + /* draw stroke curve */ + const bGPDspoint *pt = points; + int idx = 0; + 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(vbo, gpf->runtime.viewmatrix, &points[totpoints - 1], idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + idx++; + } + else { + gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[1], idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + idx++; + } + } + /* set point */ + gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, pt, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + idx++; + } + + if (gps->flag & GP_STROKE_CYCLIC && totpoints > 2) { + /* draw line to first point to complete the cycle */ + gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[0], idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + idx++; + /* now add adjacency point (not drawn) */ + gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[1], idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + idx++; + } + /* last adjacency point (not drawn) */ + else { + gpencil_set_stroke_point(vbo, gpf->runtime.viewmatrix, &points[totpoints - 2], idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, ink); + } + + return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP_ADJ, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +/* create batch geometry data for current buffer stroke shader */ +GPUBatch *DRW_gpencil_get_buffer_stroke_geom(bGPdata *gpd, float matrix[4][4], short thickness) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + View3D *v3d = draw_ctx->v3d; + ARegion *ar = draw_ctx->ar; + RegionView3D *rv3d = draw_ctx->rv3d; + ToolSettings *ts = scene->toolsettings; + Object *ob = draw_ctx->obact; + + tGPspoint *points = gpd->runtime.sbuffer; + int totpoints = gpd->runtime.sbuffer_size; + + 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, totpoints + 2); + + /* draw stroke curve */ + const tGPspoint *tpt = points; + bGPDspoint pt, pt2; + int idx = 0; + + /* get origin to reproject point */ + float origin[3]; + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, origin); + + for (int i = 0; i < totpoints; i++, tpt++) { + ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt); + ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); + + /* first point for adjacency (not drawn) */ + if (i == 0) { + if (totpoints > 1) { + ED_gpencil_tpoint_to_point(ar, origin, &points[1], &pt2); + gpencil_set_stroke_point(vbo, matrix, &pt2, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); + } + else { + gpencil_set_stroke_point(vbo, matrix, &pt, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); + } + idx++; + } + /* set point */ + gpencil_set_stroke_point(vbo, matrix, &pt, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); + idx++; + } + + /* last adjacency point (not drawn) */ + if (totpoints > 2) { + ED_gpencil_tpoint_to_point(ar, origin, &points[totpoints - 2], &pt2); + gpencil_set_stroke_point(vbo, matrix, &pt2, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); + } + else { + gpencil_set_stroke_point(vbo, matrix, &pt, idx, + pos_id, color_id, thickness_id, uvdata_id, thickness, gpd->runtime.scolor); + } + + 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 *DRW_gpencil_get_buffer_point_geom(bGPdata *gpd, float matrix[4][4], short thickness) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + View3D *v3d = draw_ctx->v3d; + ARegion *ar = draw_ctx->ar; + RegionView3D *rv3d = draw_ctx->rv3d; + ToolSettings *ts = scene->toolsettings; + Object *ob = draw_ctx->obact; + + tGPspoint *points = gpd->runtime.sbuffer; + int totpoints = gpd->runtime.sbuffer_size; + + 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, 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(v3d, scene, ob, gpl, ts->gpencil_v3d_align, origin); + + for (int i = 0; i < totpoints; i++, tpt++) { + ED_gpencil_tpoint_to_point(ar, origin, tpt, &pt); + ED_gp_project_point_to_plane(ob, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &pt); + + /* set point */ + gpencil_set_stroke_point(vbo, matrix, &pt, idx, + pos_id, color_id, thickness_id, uvdata_id, + 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 fill shader */ +GPUBatch *DRW_gpencil_get_buffer_fill_geom(bGPdata *gpd) +{ + if (gpd == NULL) { + return NULL; + } + + const tGPspoint *points = gpd->runtime.sbuffer; + int totpoints = gpd->runtime.sbuffer_size; + if (totpoints < 3) { + return NULL; + } + + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + View3D *v3d = draw_ctx->v3d; + ARegion *ar = draw_ctx->ar; + 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(v3d, 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(ar, 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); +} + +/* create batch geometry data for stroke shader */ +GPUBatch *DRW_gpencil_get_fill_geom(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_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { + DRW_gpencil_triangulate_stroke_fill(gps); + ED_gpencil_calc_stroke_uv(ob, gps); + } + + BLI_assert(gps->tot_triangles >= 1); + + static GPUVertFormat format = { 0 }; + static uint pos_id, color_id, text_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); + text_id = GPU_vertformat_attr_add(&format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + } + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, gps->tot_triangles * 3); + + /* Draw all triangles for filling the polygon (cache must be calculated before) */ + bGPDtriangle *stroke_triangle = gps->triangles; + int idx = 0; + for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { + for (int j = 0; j < 3; j++) { + gpencil_set_fill_point( + vbo, idx, &gps->points[stroke_triangle->verts[j]], color, stroke_triangle->uv[j], + pos_id, color_id, text_id); + idx++; + } + } + + return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +/* Draw selected verts for strokes being edited */ +GPUBatch *DRW_gpencil_get_edit_geom(bGPDstroke *gps, float alpha, short dflag) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob->data; + bool is_weight_paint = (gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE); + + 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; + + 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); + color_id = GPU_vertformat_attr_add(&format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + size_id = GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + } + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, 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; + + int idx = 0; + float fcolor[4]; + float fsize = 0; + for (int i = 0; i < gps->totpoints; i++, pt++, dvert++) { + /* weight paint */ + if (is_weight_paint) { + float weight = BKE_gpencil_vgroup_use_index(dvert, vgindex); + CLAMP(weight, 0.0f, 1.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 (pt->flag & GP_SPOINT_SELECT) { + copy_v4_v4(fcolor, selectColor); + fsize = vsize; + } + else { + copy_v4_v4(fcolor, gps->runtime.tmp_stroke_rgba); + fsize = bsize; + } + } + + GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor); + GPU_vertbuf_attr_set(vbo, size_id, idx, &fsize); + GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); + idx++; + } + + return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +/* Draw lines for strokes being edited */ +GPUBatch *DRW_gpencil_get_edlin_geom(bGPDstroke *gps, float alpha, short UNUSED(dflag)) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Object *ob = draw_ctx->obact; + bGPdata *gpd = ob->data; + 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); + + 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, gps->totpoints); + + /* Draw all the stroke lines (selected or not) */ + bGPDspoint *pt = gps->points; + + /* GPXX: for some converted files, this struct could be null + * maybe we can remove this and move to versioning code after + * merge */ + if (gps->dvert == NULL) { + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + } + + MDeformVert *dvert = gps->dvert; + + int idx = 0; + float fcolor[4]; + for (int i = 0; i < gps->totpoints; i++, pt++, dvert++) { + /* weight paint */ + if (is_weight_paint) { + float weight = BKE_gpencil_vgroup_use_index(dvert, vgindex); + CLAMP(weight, 0.0f, 1.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) { + copy_v4_v4(fcolor, selectColor); + } + else { + copy_v4_v4(fcolor, linecolor); + } + } + + GPU_vertbuf_attr_set(vbo, color_id, idx, fcolor); + GPU_vertbuf_attr_set(vbo, pos_id, idx, &pt->x); + idx++; + } + + return GPU_batch_create_ex(GPU_PRIM_LINE_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +static void set_grid_point(GPUVertBuf *vbo, int idx, float col_grid[4], + uint pos_id, uint color_id, + float v1, float v2, int axis) +{ + GPU_vertbuf_attr_set(vbo, color_id, idx, col_grid); + + float pos[3]; + /* Set the grid in the selected axis (default is always Y axis) */ + if (axis & V3D_GP_GRID_AXIS_X) { + pos[0] = 0.0f; + pos[1] = v1; + pos[2] = v2; + } + else if (axis & V3D_GP_GRID_AXIS_Z) { + pos[0] = v1; + pos[1] = v2; + pos[2] = 0.0f; + } + else + { + pos[0] = v1; + pos[1] = 0.0f; + pos[2] = v2; + } + + + + GPU_vertbuf_attr_set(vbo, pos_id, idx, pos); +} + +/* Draw grid lines */ +GPUBatch *DRW_gpencil_get_grid(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + ToolSettings *ts = scene->toolsettings; + View3D *v3d = draw_ctx->v3d; + + float col_grid[4]; + + /* verify we have something to draw and valid values */ + if (v3d->overlay.gpencil_grid_lines < 1) { + v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES; + } + + if (v3d->overlay.gpencil_grid_scale == 0.0f) { + v3d->overlay.gpencil_grid_scale = 1.0f; + } + + if (v3d->overlay.gpencil_grid_opacity < 0.1f) { + v3d->overlay.gpencil_grid_opacity = 0.1f; + } + + UI_GetThemeColor3fv(TH_GRID, col_grid); + col_grid[3] = v3d->overlay.gpencil_grid_opacity; + + /* if use locked axis, copy value */ + int axis = v3d->overlay.gpencil_grid_axis; + if ((v3d->overlay.gpencil_grid_axis & V3D_GP_GRID_AXIS_LOCK) == 0) { + + axis = v3d->overlay.gpencil_grid_axis; + } + else { + switch (ts->gp_sculpt.lock_axis) { + case GP_LOCKAXIS_X: + { + axis = V3D_GP_GRID_AXIS_X; + break; + } + case GP_LOCKAXIS_NONE: + case GP_LOCKAXIS_Y: + { + axis = V3D_GP_GRID_AXIS_Y; + break; + } + case GP_LOCKAXIS_Z: + { + axis = V3D_GP_GRID_AXIS_Z; + break; + } + } + } + + const char *grid_unit = NULL; + const int gridlines = v3d->overlay.gpencil_grid_lines; + const float grid_scale = v3d->overlay.gpencil_grid_scale * ED_scene_grid_scale(scene, &grid_unit); + const float grid = grid_scale; + const float space = (grid_scale / gridlines); + + 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 = a * space; + + set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, -line, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, -line, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, +line, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, +line, axis); + idx++; + + set_grid_point(vbo, idx, col_grid, pos_id, color_id, -line, -grid, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, -line, +grid, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, +line, -grid, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, +line, +grid, axis); + idx++; + } + /* center lines */ + set_grid_point(vbo, idx, col_grid, pos_id, color_id, -grid, 0.0f, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, +grid, 0.0f, axis); + idx++; + + set_grid_point(vbo, idx, col_grid, pos_id, color_id, 0.0f, -grid, axis); + idx++; + set_grid_point(vbo, idx, col_grid, pos_id, color_id, 0.0f, +grid, 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_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c new file mode 100644 index 00000000000..76cb1405a71 --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c @@ -0,0 +1,1336 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_draw_utils.c + * \ingroup draw + */ + +#include "BLI_polyfill_2d.h" + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "BKE_brush.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_image.h" +#include "BKE_material.h" + +#include "ED_gpencil.h" +#include "ED_view3d.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_view3d_types.h" +#include "DNA_gpencil_modifier_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" + +/* fill type to communicate to shader */ +#define SOLID 0 +#define GRADIENT 1 +#define RADIAL 2 +#define CHESS 3 +#define TEXTURE 4 +#define PATTERN 5 + +/* 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; +} + +/* 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], bool expand) +{ + 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]; + } + } + /* 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 gpencil_calc_stroke_fill_uv( + const float(*points2d)[2], int totpoints, 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]; + } +} + +/* Get points of stroke always flat to view not affected by camera view or view position */ +static void gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction) +{ + const bGPDspoint *pt0 = &points[0]; + const bGPDspoint *pt1 = &points[1]; + const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; + + float locx[3]; + float locy[3]; + float loc3[3]; + float normal[3]; + + /* local X axis (p0 -> p1) */ + sub_v3_v3v3(locx, &pt1->x, &pt0->x); + + /* point vector at 3/4 */ + sub_v3_v3v3(loc3, &pt3->x, &pt0->x); + + /* vector orthogonal to polygon plane */ + cross_v3_v3v3(normal, locx, loc3); + + /* local Y axis (cross to normal/x axis) */ + cross_v3_v3v3(locy, normal, locx); + + /* Normalize vectors */ + normalize_v3(locx); + normalize_v3(locy); + + /* Get all points in local space */ + for (int i = 0; i < totpoints; i++) { + const bGPDspoint *pt = &points[i]; + float loc[3]; + + /* Get local space using first point as origin */ + sub_v3_v3v3(loc, &pt->x, &pt0->x); + + points2d[i][0] = dot_v3v3(loc, locx); + points2d[i][1] = dot_v3v3(loc, locy); + } + + /* Concave (-1), Convex (1), or Autodetect (0)? */ + *r_direction = (int)locy[2]; +} + +/* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ +void DRW_gpencil_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 */ + 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 */ + gpencil_calc_2d_bounding_box(points2d, gps->totpoints, minv, maxv, false); + /* calc uv data */ + gpencil_calc_stroke_fill_uv(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(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]]); + } + } + 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_CACHES) { + gps->flag &= ~GP_STROKE_RECALC_CACHES; + } + + /* clear memory */ + MEM_SAFE_FREE(tmp_triangles); + MEM_SAFE_FREE(points2d); + MEM_SAFE_FREE(uv); +} + +/* recalc the internal geometry caches for fill and uvs */ +static void DRW_gpencil_recalc_geometry_caches(Object *ob, MaterialGPencilStyle *gp_style, bGPDstroke *gps) +{ + if (gps->flag & GP_STROKE_RECALC_CACHES) { + /* 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->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0))) + { + DRW_gpencil_triangulate_stroke_fill(gps); + } + } + + /* calc uv data along the stroke */ + ED_gpencil_calc_stroke_uv(ob, gps); + + /* clear flag */ + gps->flag &= ~GP_STROKE_RECALC_CACHES; + } +} + +/* create shading group for filling */ +static DRWShadingGroup *DRW_gpencil_shgroup_fill_create( + GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, + GPUShader *shader, bGPdata *gpd, MaterialGPencilStyle *gp_style, int id) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + + /* e_data.gpencil_fill_sh */ + DRWShadingGroup *grp = DRW_shgroup_create(shader, pass); + + 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_CHESSBOARD: + stl->shgroups[id].fill_style = CHESS; + 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); + + stl->shgroups[id].texture_mix = gp_style->flag & GP_STYLE_COLOR_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); + + DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1); + /* image texture */ + if ((gp_style->flag & GP_STYLE_COLOR_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, GL_TEXTURE_2D, true, 0.0); + DRW_shgroup_uniform_texture(grp, "myTexture", texture); + + 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; +} + +/* create shading group for strokes */ +DRWShadingGroup *DRW_gpencil_shgroup_stroke_create( + GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob, + bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + const float *viewport_size = DRW_viewport_size_get(); + + /* 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_float(grp, "pixelsize", &U.pixelsize, 1); + + /* avoid wrong values */ + if ((gpd) && (gpd->pixfactor == 0)) { + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; + } + + /* object scale and depth */ + if ((ob) && (id > -1)) { + stl->shgroups[id].obj_scale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f; + 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); + } + 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); + } + } + + if ((gpd) && (id > -1)) { + DRW_shgroup_uniform_int(grp, "xraymode", (const int *) &gpd->xray_mode, 1); + } + else { + /* for drawing always on front */ + DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); + } + + /* 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, GL_TEXTURE_2D, true, 0.0f); + DRW_shgroup_uniform_texture(grp, "myTexture", texture); + + 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 volumetrics */ +static DRWShadingGroup *DRW_gpencil_shgroup_point_create( + GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob, + bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + const float *viewport_size = DRW_viewport_size_get(); + + /* 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_float(grp, "pixelsize", &U.pixelsize, 1); + + /* avoid wrong values */ + if ((gpd) && (gpd->pixfactor == 0)) { + gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; + } + + /* object scale and depth */ + if ((ob) && (id > -1)) { + stl->shgroups[id].obj_scale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f; + 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); + } + 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); + } + } + + if (gpd) { + DRW_shgroup_uniform_int(grp, "xraymode", (const int *)&gpd->xray_mode, 1); + } + else { + /* for drawing always on front */ + DRW_shgroup_uniform_int(grp, "xraymode", &stl->storage->xray, 1); + } + + /* 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, GL_TEXTURE_2D, true, 0.0f); + DRW_shgroup_uniform_texture(grp, "myTexture", texture); + + 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 shading group to pass */ +static void gpencil_add_fill_shgroup( + GpencilBatchCache *cache, DRWShadingGroup *fillgrp, + Object *ob, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, + const float tintcolor[4], const bool onion, const bool custonion) +{ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(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] * gpl->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { + 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; + } + } + if (cache->is_dirty) { + gpencil_batch_cache_check_free_slots(ob); + cache->batch_fill[cache->cache_idx] = DRW_gpencil_get_fill_geom(ob, gps, color); + } + DRW_shgroup_call_add(fillgrp, cache->batch_fill[cache->cache_idx], gpf->runtime.viewmatrix); + } + } +} + +/* add stroke shading group to pass */ +static void gpencil_add_stroke_shgroup(GpencilBatchCache *cache, DRWShadingGroup *strokegrp, + 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_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + /* set color using base color, tint color and opacity */ + 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 (cache->is_dirty) { + gpencil_batch_cache_check_free_slots(ob); + if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) { + cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_stroke_geom(gpf, gps, sthickness, ink); + } + else { + cache->batch_stroke[cache->cache_idx] = DRW_gpencil_get_point_geom(gps, sthickness, ink); + } + } + DRW_shgroup_call_add(strokegrp, cache->batch_stroke[cache->cache_idx], gpf->runtime.viewmatrix); +} + +/* add edit points shading group to pass */ +static void gpencil_add_editpoints_shgroup( + GPENCIL_StorageList *stl, GpencilBatchCache *cache, ToolSettings *UNUSED(ts), Object *ob, + bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(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); + + /* line of the original stroke */ + if (cache->is_dirty) { + gpencil_batch_cache_check_free_slots(ob); + cache->batch_edlin[cache->cache_idx] = DRW_gpencil_get_edlin_geom(gps, edit_alpha, gpd->flag); + } + if (cache->batch_edlin[cache->cache_idx]) { + if ((obact) && (obact == ob) && + ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (v3d->flag3 & V3D_GP_SHOW_EDIT_LINES)) + { + DRW_shgroup_call_add( + stl->g_data->shgrps_edit_line, + cache->batch_edlin[cache->cache_idx], + gpf->runtime.viewmatrix); + } + } + /* 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 (cache->is_dirty) { + gpencil_batch_cache_check_free_slots(ob); + cache->batch_edit[cache->cache_idx] = DRW_gpencil_get_edit_geom(gps, edit_alpha, gpd->flag); + } + if (cache->batch_edit[cache->cache_idx]) { + if ((obact) && (obact == ob)) { + /* edit pass */ + DRW_shgroup_call_add( + stl->g_data->shgrps_edit_point, + cache->batch_edit[cache->cache_idx], + gpf->runtime.viewmatrix); + } + } + } + } + } +} + +/* function to draw strokes for onion only */ +static void gpencil_draw_onion_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) +{ + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + Depsgraph *depsgraph = DRW_context_state_get()->depsgraph; + + float viewmatrix[4][4]; + + /* get parent matrix and save as static data */ + ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix); + copy_m4_m4(gpf->runtime.viewmatrix, viewmatrix); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + 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; + } + + stl->shgroups[id].shgrps_fill = NULL; + if ((gps->totpoints > 1) && (gp_style->mode == GP_STYLE_MODE_LINE)) { + stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create( + e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, true); + } + else { + stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create( + e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, true); + } + + /* stroke */ + gpencil_add_stroke_shgroup( + cache, stl->shgroups[id].shgrps_stroke, ob, gpl, gpf, gps, opacity, tintcolor, true, custonion); + + stl->storage->shgroup_id++; + cache->cache_idx++; + } +} + + +/* main function to draw strokes */ +static void gpencil_draw_strokes( + GpencilBatchCache *cache, GPENCIL_e_data *e_data, void *vedata, ToolSettings *ts, Object *ob, + bGPdata *gpd, bGPDlayer *gpl, bGPDframe *src_gpf, bGPDframe *derived_gpf, + const float opacity, const float tintcolor[4], const bool custonion) +{ + 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, *src_gps; + DRWShadingGroup *fillgrp; + DRWShadingGroup *strokegrp; + float viewmatrix[4][4]; + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + const bool playing = (bool)stl->storage->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->flag3 & 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 */ + ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, viewmatrix); + copy_m4_m4(derived_gpf->runtime.viewmatrix, viewmatrix); + + /* apply geometry modifiers */ + if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) { + if (!stl->storage->simplify_modif) { + if (BKE_gpencil_has_geometry_modifiers(ob)) { + BKE_gpencil_geometry_modifiers(depsgraph, ob, gpl, derived_gpf, stl->storage->is_render); + } + } + } + + if (src_gpf) { + src_gps = src_gpf->strokes.first; + } + else { + src_gps = NULL; + } + + for (gps = derived_gpf->strokes.first; gps; gps = gps->next) { + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(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; + } + /* limit the number of shading groups */ + if (stl->storage->shgroup_id >= GPENCIL_MAX_SHGROUPS) { + continue; + } + + /* be sure recalc all chache in source stroke to avoid recalculation when frame change + * and improve fps */ + if (src_gps) { + DRW_gpencil_recalc_geometry_caches(ob, gp_style, src_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)) + { + continue; + } + } + + if ((gpl->actframe->framenum == derived_gpf->framenum) || + (!is_multiedit) || (overlay_multiedit)) + { + int id = stl->storage->shgroup_id; + if (gps->totpoints > 0) { + if ((gps->totpoints > 2) && (!stl->storage->simplify_fill) && + ((gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) && + ((gps->flag & GP_STROKE_NOFILL) == 0)) + { + stl->shgroups[id].shgrps_fill = DRW_gpencil_shgroup_fill_create( + e_data, vedata, psl->stroke_pass, e_data->gpencil_fill_sh, gpd, gp_style, id); + } + else { + stl->shgroups[id].shgrps_fill = NULL; + } + if ((gp_style->mode == GP_STYLE_MODE_LINE) && (gps->totpoints > 1)) { + stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_stroke_create( + e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh, ob, gpd, gp_style, id, false); + } + else { + stl->shgroups[id].shgrps_stroke = DRW_gpencil_shgroup_point_create( + e_data, vedata, psl->stroke_pass, e_data->gpencil_point_sh, ob, gpd, gp_style, id, false); + } + } + else { + stl->shgroups[id].shgrps_fill = NULL; + stl->shgroups[id].shgrps_stroke = NULL; + } + stl->storage->shgroup_id++; + + fillgrp = stl->shgroups[id].shgrps_fill; + strokegrp = stl->shgroups[id].shgrps_stroke; + + /* copy color to temp fields to apply temporal changes in the stroke */ + copy_v4_v4(gps->runtime.tmp_stroke_rgba, gp_style->stroke_rgba); + copy_v4_v4(gps->runtime.tmp_fill_rgba, gp_style->fill_rgba); + + /* apply modifiers (only modify geometry, but not create ) */ + if ((cache->is_dirty) && (ob->greasepencil_modifiers.first) && (!is_multiedit)) { + if (!stl->storage->simplify_modif) { + BKE_gpencil_stroke_modifiers(depsgraph, ob, gpl, derived_gpf, gps, stl->storage->is_render); + } + } + + /* fill */ + if ((fillgrp) && (!stl->storage->simplify_fill)) { + gpencil_add_fill_shgroup( + cache, fillgrp, ob, gpl, derived_gpf, gps, tintcolor, false, custonion); + } + /* stroke */ + if (strokegrp) { + gpencil_add_stroke_shgroup( + cache, strokegrp, ob, gpl, derived_gpf, gps, opacity, tintcolor, false, custonion); + } + } + + /* edit points (only in edit mode and not play animation not render) */ + if ((src_gps) && (!playing) && (!is_render)) { + if (!stl->g_data->shgrps_edit_line) { + stl->g_data->shgrps_edit_line = DRW_shgroup_create(e_data->gpencil_line_sh, psl->edit_pass); + } + 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); + } + + gpencil_add_editpoints_shgroup(stl, cache, ts, ob, gpd, gpl, derived_gpf, src_gps); + } + + if (src_gps) { + src_gps = src_gps->next; + } + + cache->cache_idx++; + } +} + + /* draw stroke in drawing buffer */ +void DRW_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; + Brush *brush = BKE_brush_getactive_gpencil(ts); + bGPdata *gpd = ob->data; + MaterialGPencilStyle *gp_style = NULL; + + float obscale = (ob->size[0] + ob->size[1] + ob->size[2]) / 3.0f; + + /* use the brush material */ + Material *ma = BKE_gpencil_get_material_from_brush(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_material_gpencil_settings_get(ob, ob->actcol); + } + + /* 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 (ED_gpencil_session_active() && (gpd->runtime.sbuffer_size > 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; + /* if only one point, don't need to draw buffer because the user has no time to see it */ + if (gpd->runtime.sbuffer_size > 1) { + if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) { + stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create( + e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false); + } + else { + stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create( + e_data, vedata, psl->drawing_pass, e_data->gpencil_point_sh, NULL, gpd, gp_style, -1, false); + } + + /* 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 = DRW_gpencil_get_buffer_stroke_geom( + gpd, stl->storage->unit_matrix, lthick); + } + else { + stl->g_data->batch_buffer_stroke = DRW_gpencil_get_buffer_point_geom( + gpd, stl->storage->unit_matrix, lthick); + } + + DRW_shgroup_call_add( + stl->g_data->shgrps_drawing_stroke, + stl->g_data->batch_buffer_stroke, + stl->storage->unit_matrix); + + if ((gpd->runtime.sbuffer_size >= 3) && (gpd->runtime.sfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) && + ((gpd->runtime.sbuffer_sflag & GP_STROKE_NOFILL) == 0)) + { + /* 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); + stl->g_data->batch_buffer_fill = DRW_gpencil_get_buffer_fill_geom(gpd); + DRW_shgroup_call_add( + stl->g_data->shgrps_drawing_fill, + stl->g_data->batch_buffer_fill, + stl->storage->unit_matrix); + } + } + } + } +} + +/* 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); +} + +/* draw onion-skinning for a layer */ +static void gpencil_draw_onionskins( + GpencilBatchCache *cache, GPENCIL_e_data *e_data, 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; + int mode = 0; + bool colflag = false; + bGPDframe *gpf_loop = NULL; + int last = gpf->framenum; + + colflag = (bool)gpd->onion_flag & GP_ONION_GHOST_PREVCOL; + + + /* ------------------------------- + * 1) Draw Previous Frames First + * ------------------------------- */ + step = gpd->gstep; + mode = gpd->onion_mode; + + 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; + } + /* 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_ABSOLUTE) && (gpd->onion_flag & GP_ONION_LOOP)) { + gpf_loop = gf; + } + + gpencil_get_onion_alpha(color, gpd); + gpencil_draw_onion_strokes(cache, e_data, vedata, ob, gpd, gpl, gf, color[3], color, colflag); + } + /* ------------------------------- + * 2) Now draw next frames + * ------------------------------- */ + step = gpd->gstep_next; + mode = gpd->onion_mode; + + 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; + } + /* 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, e_data, 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, e_data, vedata, ob, gpd, gpl, + gpf_loop, color[3], color, colflag); + } + } +} + +/* populate a datablock for multiedit (no onions, no modifiers) */ +void DRW_gpencil_populate_multiedit(GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob, bGPdata *gpd) +{ + bGPDframe *gpf = NULL; + + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); + ToolSettings *ts = scene->toolsettings; + cache->cache_idx = 0; + + /* check if playing animation */ + bool playing = (bool)stl->storage->playing; + + /* 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; + + /* 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, ts, ob, gpd, gpl, gpf, gpf, + gpl->opacity, gpl->tintcolor, false); + } + } + } + else { + gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); + if (gpf) { + gpencil_draw_strokes( + cache, e_data, vedata, ts, ob, gpd, gpl, gpf, gpf, + gpl->opacity, gpl->tintcolor, false); + } + } + + } + + cache->is_dirty = false; +} + +/* helper for populate a complete grease pencil datablock */ +void DRW_gpencil_populate_datablock(GPENCIL_e_data *e_data, void *vedata, Scene *scene, Object *ob, bGPdata *gpd) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + int cfra_eval = (int)DEG_get_ctime(draw_ctx->depsgraph); + ToolSettings *ts = scene->toolsettings; + bGPDframe *derived_gpf = NULL; + const bool main_onion = v3d != NULL ? ((v3d->flag3 & V3D_GP_SHOW_ONION_SKIN) == 0) : true; + const bool no_onion = (bool)(gpd->flag & GP_DATA_STROKE_WEIGHTMODE) || main_onion; + const bool overlay = v3d != NULL ? (bool)((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) : true; + + /* check if playing animation */ + bool playing = (bool)stl->storage->playing; + + GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval); + cache->cache_idx = 0; + + /* init general modifiers data */ + if (!stl->storage->simplify_modif) { + if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) { + BKE_gpencil_lattice_init(ob); + } + } + /* 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; + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); + if (gpf == NULL) + continue; + + /* create GHash if need */ + if (gpl->runtime.derived_data == NULL) { + gpl->runtime.derived_data = (GHash *)BLI_ghash_str_new(gpl->info); + } + + derived_gpf = BLI_ghash_lookup(gpl->runtime.derived_data, ob->id.name); + if (derived_gpf == NULL) { + cache->is_dirty = true; + } + if (cache->is_dirty) { + if (derived_gpf != NULL) { + /* first clear temp data */ + BKE_gpencil_free_frame_runtime_data(derived_gpf); + BLI_ghash_remove(gpl->runtime.derived_data, ob->id.name, NULL, NULL); + } + /* create new data */ + derived_gpf = BKE_gpencil_frame_duplicate(gpf); + BLI_ghash_insert(gpl->runtime.derived_data, ob->id.name, derived_gpf); + } + + /* draw onion skins */ + if ((gpd->flag & GP_DATA_SHOW_ONIONSKINS) && + (!no_onion) && (overlay) && + (gpl->onion_flag & GP_LAYER_ONIONSKIN) && + ((!playing) || (gpd->onion_flag & GP_ONION_GHOST_ALWAYS))) + { + if ((!stl->storage->is_render) || + ((stl->storage->is_render) && (gpd->onion_flag & GP_ONION_GHOST_ALWAYS))) + { + gpencil_draw_onionskins(cache, e_data, vedata, ob, gpd, gpl, gpf); + } + } + + /* draw normal strokes */ + gpencil_draw_strokes( + cache, e_data, vedata, ts, ob, gpd, gpl, gpf, derived_gpf, + gpl->opacity, gpl->tintcolor, false); + + } + + /* clear any lattice data */ + if ((cache->is_dirty) && (ob->greasepencil_modifiers.first)) { + BKE_gpencil_lattice_clear(ob); + } + + cache->is_dirty = false; +} + +/* Helper for gpencil_instance_modifiers() + * See also MOD_gpencilinstance.c -> bakeModifier() + */ +static void gp_instance_modifier_make_instances(GPENCIL_StorageList *stl, Object *ob, InstanceGpencilModifierData *mmd) +{ + /* reset random */ + mmd->rnd[0] = 1; + + /* Generate instances */ + for (int x = 0; x < mmd->count[0]; x++) { + for (int y = 0; y < mmd->count[1]; y++) { + for (int z = 0; z < mmd->count[2]; z++) { + Object *newob; + + const int elem_idx[3] = {x, y, z}; + float mat[4][4]; + int sh; + + /* original strokes are at index = 0,0,0 */ + if ((x == 0) && (y == 0) && (z == 0)) { + continue; + } + + /* compute transform for instance */ + BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat); + + /* add object to cache */ + newob = MEM_dupallocN(ob); + mul_m4_m4m4(newob->obmat, ob->obmat, mat); + + /* apply scale */ + ARRAY_SET_ITEMS(newob->size, mat[0][0], mat[1][1], mat[2][2]); + + /* apply shift */ + sh = x; + if (mmd->lock_axis == GP_LOCKAXIS_Y) { + sh = y; + } + if (mmd->lock_axis == GP_LOCKAXIS_Z) { + sh = z; + } + madd_v3_v3fl(newob->obmat[3], mmd->shift, sh); + + /* add temp object to cache */ + stl->g_data->gp_object_cache = gpencil_object_cache_add( + stl->g_data->gp_object_cache, newob, true, + &stl->g_data->gp_cache_size, &stl->g_data->gp_cache_used); + } + } + } +} + +/* create instances using instance modifiers */ +void gpencil_instance_modifiers(GPENCIL_StorageList *stl, Object *ob) +{ + if ((ob) && (ob->data)) { + bGPdata *gpd = ob->data; + if (GPENCIL_ANY_EDIT_MODE(gpd)) { + return; + } + } + + for (GpencilModifierData *md = ob->greasepencil_modifiers.first; md; md = md->next) { + if (((md->mode & eGpencilModifierMode_Realtime) && (stl->storage->is_render == false)) || + ((md->mode & eGpencilModifierMode_Render) && (stl->storage->is_render == true))) + { + if (md->type == eGpencilModifierType_Instance) { + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + + /* Only add instances if the "Make Objects" flag is set + * FIXME: This is a workaround for z-ordering weirdness when all instances are in the same object + */ + if (mmd->flag & GP_INSTANCE_MAKE_OBJECTS) { + gp_instance_modifier_make_instances(stl, ob, mmd); + } + } + } + } +} diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c new file mode 100644 index 00000000000..71c99b2bcdf --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -0,0 +1,794 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_engine.c + * \ingroup draw + */ +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "BKE_camera.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_gpencil.h" +#include "BKE_shader_fx.h" + +#include "DNA_gpencil_types.h" +#include "DNA_view3d_types.h" + +#include "draw_mode_engines.h" + +#include "UI_resources.h" + +#include "GPU_texture.h" + +#include "gpencil_engine.h" + +#include "ED_screen.h" +#include "ED_gpencil.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[]; + +/* *********** STATIC *********** */ +static GPENCIL_e_data e_data = {NULL}; /* Engine data */ + +/* *********** FUNCTIONS *********** */ + +/* create a multisample buffer if not present */ +void DRW_gpencil_multisample_ensure(GPENCIL_Data *vedata, int rect_w, int rect_h) +{ + 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) + }); + if (!GPU_framebuffer_check_valid(fbl->multisample_fb, NULL)) { + GPU_framebuffer_free(fbl->multisample_fb); + } + } + } + } +} + +static void GPENCIL_create_framebuffers(void *vedata) +{ + GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + + /* Go full 32bits for rendering */ + GPUTextureFormat 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 multiframe framebuffer for AA */ + if (stl->storage->multisamples > 0) { + DRW_gpencil_multisample_ensure(vedata, size[0], size[1]); + } + + /* temp textures */ + e_data.temp_depth_tx_a = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, + &draw_engine_gpencil_type); + e_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(e_data.temp_depth_tx_a), + GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_a) + }); + + e_data.temp_depth_tx_b = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, + &draw_engine_gpencil_type); + e_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(e_data.temp_depth_tx_b), + GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_b) + }); + + /* used for rim FX effect */ + e_data.temp_depth_tx_rim = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, + &draw_engine_gpencil_type); + e_data.temp_color_tx_rim = DRW_texture_pool_query_2D(size[0], size[1], fb_format, + &draw_engine_gpencil_type); + GPU_framebuffer_ensure_config(&fbl->temp_fb_rim, { + GPU_ATTACHMENT_TEXTURE(e_data.temp_depth_tx_rim), + GPU_ATTACHMENT_TEXTURE(e_data.temp_color_tx_rim), + }); + + /* background framebuffer to speed up drawing process (always 16 bits) */ + e_data.background_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, + &draw_engine_gpencil_type); + e_data.background_color_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_RGBA32F, + &draw_engine_gpencil_type); + GPU_framebuffer_ensure_config(&fbl->background_fb, { + GPU_ATTACHMENT_TEXTURE(e_data.background_depth_tx), + GPU_ATTACHMENT_TEXTURE(e_data.background_color_tx) + }); + } +} + +static void GPENCIL_create_shaders(void) +{ + /* normal fill shader */ + if (!e_data.gpencil_fill_sh) { + e_data.gpencil_fill_sh = DRW_shader_create( + datatoc_gpencil_fill_vert_glsl, NULL, + datatoc_gpencil_fill_frag_glsl, NULL); + } + + /* normal stroke shader using geometry to display lines (line mode) */ + if (!e_data.gpencil_stroke_sh) { + e_data.gpencil_stroke_sh = DRW_shader_create( + datatoc_gpencil_stroke_vert_glsl, + datatoc_gpencil_stroke_geom_glsl, + datatoc_gpencil_stroke_frag_glsl, + NULL); + } + + /* dot/rectangle mode for normal strokes using geometry */ + if (!e_data.gpencil_point_sh) { + e_data.gpencil_point_sh = DRW_shader_create( + datatoc_gpencil_point_vert_glsl, + datatoc_gpencil_point_geom_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( + datatoc_gpencil_edit_point_vert_glsl, + datatoc_gpencil_edit_point_geom_glsl, + datatoc_gpencil_edit_point_frag_glsl, NULL); + } + + /* used for edit lines for edit modes */ + if (!e_data.gpencil_line_sh) { + e_data.gpencil_line_sh = GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR); + } + + /* 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); + } + + /* 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); + } + + /* 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 (!e_data.gpencil_paper_sh) { + e_data.gpencil_paper_sh = DRW_shader_create_fullscreen(datatoc_gpencil_paper_frag_glsl, NULL); + } +} + +void GPENCIL_engine_init(void *vedata) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + /* init storage */ + if (!stl->storage) { + stl->storage = MEM_callocN(sizeof(GPENCIL_Storage), "GPENCIL_Storage"); + + /* unit matrix */ + unit_m4(stl->storage->unit_matrix); + } + + stl->storage->multisamples = U.gpencil_multisamples; + + /* create framebuffers */ + GPENCIL_create_framebuffers(vedata); + + /* create shaders */ + GPENCIL_create_shaders(); + GPENCIL_create_fx_shaders(&e_data); + + /* blank texture used if no texture defined for fill shader */ + if (!e_data.gpencil_blank_texture) { + float rect[16][16][4] = {{{0.0f}}}; + e_data.gpencil_blank_texture = DRW_texture_create_2D(16, 16, GPU_RGBA8, DRW_TEX_FILTER, (float *)rect); + } +} + +static void GPENCIL_engine_free(void) +{ + /* 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_fullscreen_sh); + DRW_SHADER_FREE_SAFE(e_data.gpencil_simple_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); +} + +void GPENCIL_cache_init(void *vedata) +{ + 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; + + /* 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; + gp_style = BKE_material_gpencil_settings_get(obact, obact->actcol); + } + + 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 */ + } + stl->storage->tonemapping = 0; + + stl->g_data->shgrps_edit_line = NULL; + stl->g_data->shgrps_edit_point = NULL; + + if (!stl->shgroups) { + /* Alloc maximum size because count strokes is very slow and can be very complex due onion skinning. + I tried to allocate only one block and using realloc, increasing the size when read a new strokes + in cache_finish, but the realloc produce weird things on screen, so we keep as is while we found + a better solution + */ + stl->shgroups = MEM_mallocN(sizeof(GPENCIL_shgroup) * GPENCIL_MAX_SHGROUPS, "GPENCIL_shgroup"); + } + + /* 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; + + { + /* Stroke pass */ + psl->stroke_pass = DRW_pass_create( + "GPencil Stroke Pass", + DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND); + stl->storage->shgroup_id = 0; + + /* edit pass */ + psl->edit_pass = DRW_pass_create( + "GPencil Edit Pass", + DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND); + + /* detect if playing animation */ + stl->storage->playing = 0; + if (draw_ctx->evil_C) { + stl->storage->playing = ED_screen_animation_playing(CTX_wm_manager(draw_ctx->evil_C)) != NULL ? 1 : 0; + } + + 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->playing == 1) { + 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; + } + } + + /* save render state */ + stl->storage->is_render = DRW_state_is_image_render(); + stl->storage->is_mat_preview = (bool)stl->storage->is_render && STREQ(scene->id.name + 2, "preview"); + + /* save simplify flags (can change while drawing, so it's better to save) */ + stl->storage->simplify_fill = GP_SIMPLIFY_FILL(scene, stl->storage->playing); + stl->storage->simplify_modif = GP_SIMPLIFY_MODIF(scene, stl->storage->playing); + + /* 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; + } + + /* detect if painting session */ + if ((obact_gpd) && + (obact_gpd->flag & GP_DATA_STROKE_PAINTMODE) && + (stl->storage->playing == 0)) + { + if (((obact_gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) && + (obact_gpd->runtime.sbuffer_size > 1)) + { + 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; + } + + 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; + } + } + } + else { + stl->storage->stroke_style = GP_STYLE_STROKE_STYLE_SOLID; + stl->storage->color_type = GPENCIL_COLOR_SOLID; + } + + /* drawing buffer pass for drawing the stroke that is beeing 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 | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); + + /* 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 | 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_add(mix_shgrp, quad, NULL); + DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeColor", &e_data.input_color_tx); + DRW_shgroup_uniform_texture_ref(mix_shgrp, "strokeDepth", &e_data.input_depth_tx); + DRW_shgroup_uniform_int(mix_shgrp, "tonemapping", &stl->storage->tonemapping, 1); + + /* mix pass no blend used to copy between passes. A separated pass is required + * because if mix_pass is used, the acumulation 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_add(mix_shgrp_noblend, quad, NULL); + DRW_shgroup_uniform_texture_ref(mix_shgrp_noblend, "strokeColor", &e_data.input_color_tx); + DRW_shgroup_uniform_texture_ref(mix_shgrp_noblend, "strokeDepth", &e_data.input_depth_tx); + DRW_shgroup_uniform_int(mix_shgrp_noblend, "tonemapping", &stl->storage->tonemapping, 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 | 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_add(background_shgrp, quad, NULL); + DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeColor", &e_data.background_color_tx); + DRW_shgroup_uniform_texture_ref(background_shgrp, "strokeDepth", &e_data.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); + DRWShadingGroup *paper_shgrp = DRW_shgroup_create(e_data.gpencil_paper_sh, psl->paper_pass); + DRW_shgroup_call_add(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); + } + + /* grid pass */ + if (v3d) { + psl->grid_pass = DRW_pass_create( + "GPencil Grid Pass", + DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); + stl->g_data->shgrps_grid = DRW_shgroup_create(e_data.gpencil_line_sh, psl->grid_pass); + } + + /* create effects passes */ + GPENCIL_create_fx_passes(psl); + } +} + +void GPENCIL_cache_populate(void *vedata, Object *ob) +{ + /* object must be visible */ + if (!DRW_check_object_visible_within_active_context(ob)) { + 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; + + /* object datablock (this is not draw now) */ + if (ob->type == OB_GPENCIL && ob->data) { + bGPdata *gpd = (bGPdata *)ob->data; + if ((stl->g_data->session_flag & GP_DRW_PAINT_READY) == 0) { + + /* if render set as dirty */ + if (stl->storage->is_render == true) { + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } + + /* allocate memory for saving gp objects for drawing later */ + stl->g_data->gp_object_cache = gpencil_object_cache_add(stl->g_data->gp_object_cache, ob, false, + &stl->g_data->gp_cache_size, &stl->g_data->gp_cache_used); + + /* generate instances as separate cache objects for instance modifiers + * with the "Make as Objects" option enabled + */ + if (!stl->storage->simplify_modif) { + gpencil_instance_modifiers(stl, ob); + } + } + /* draw current painting strokes */ + DRW_gpencil_populate_buffer_strokes(&e_data, vedata, ts, ob); + + /* grid */ + if ((v3d) && + ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (v3d->flag3 & V3D_GP_SHOW_GRID) && + (ob->type == OB_GPENCIL) && (ob == draw_ctx->obact)) + { + stl->g_data->batch_grid = DRW_gpencil_get_grid(); + DRW_shgroup_call_add(stl->g_data->shgrps_grid, + stl->g_data->batch_grid, + ob->obmat); + } + } +} + +void GPENCIL_cache_finish(void *vedata) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + bool is_multiedit = false; + + /* if painting session, don't need to do more */ + if (stl->g_data->session_flag & GP_DRW_PAINT_PAINTING) { + return; + } + + /* Draw all pending objects */ + if (stl->g_data->gp_cache_used > 0) { + for (int i = 0; i < stl->g_data->gp_cache_used; i++) { + Object *ob = stl->g_data->gp_object_cache[i].ob; + bGPdata *gpd = ob->data; + + /* save init shading group */ + stl->g_data->gp_object_cache[i].init_grp = stl->storage->shgroup_id; + + /* fill shading groups */ + is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + if (!is_multiedit) { + DRW_gpencil_populate_datablock(&e_data, vedata, scene, ob, gpd); + } + else { + DRW_gpencil_populate_multiedit(&e_data, vedata, scene, ob, gpd); + } + + /* save end shading group */ + stl->g_data->gp_object_cache[i].end_grp = stl->storage->shgroup_id - 1; + /* if render set to dirty to refresh viewport */ + if (stl->storage->is_render == true) { + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } + /* FX passses */ + tGPencilObjectCache *cache = &stl->g_data->gp_object_cache[i]; + if (!is_multiedit) { + DRW_gpencil_fx_prepare(&e_data, vedata, cache); + } + } + } +} + +/* helper function to sort inverse gpencil objects using qsort */ +static int gpencil_object_cache_compare_zdepth(const void *a1, const void *a2) +{ + const tGPencilObjectCache *ps1 = a1, *ps2 = a2; + + if (ps1->zdepth < ps2->zdepth) return 1; + else if (ps1->zdepth > ps2->zdepth) return -1; + + 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(fbl->background_fb, clearcol, 1.0f); + stl->g_data->session_flag = GP_DRW_PAINT_FILLING; + } + /* repeat pass to fill temp texture */ + DRW_draw_pass(pass); + /* set default framebuffer again */ + GPU_framebuffer_bind(dfbl->default_fb); + } +} + +static void gpencil_free_obj_list(GPENCIL_StorageList *stl) +{ + /* Clear temp objects created for display instances only. These objects are created + * while the draw manager draw the scene, but only to hold the strokes data. + * see: gp_instance_modifier_make_instances() + * + * the normal objects are not freed because they are not tagged as temp objects + */ + for (int i = 0; i < stl->g_data->gp_cache_used; i++) { + Object *ob = stl->g_data->gp_object_cache[i].ob; + if (stl->g_data->gp_object_cache[i].temp_ob) { + MEM_SAFE_FREE(ob); + } + } + + /* free the cache itself */ + MEM_SAFE_FREE(stl->g_data->gp_object_cache); +} + +/* draw scene */ +void GPENCIL_draw_scene(void *ved) +{ + 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; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + GPENCIL_TextureList *txl = ((GPENCIL_Data *)vedata)->txl; + + int init_grp, end_grp; + tGPencilObjectCache *cache; + 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 = (bool)stl->storage->playing; + const bool is_render = stl->storage->is_render; + + /* paper pass to display a confortable area to draw over complex scenes with geometry */ + if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { + if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (v3d->flag3 & V3D_GP_SHOW_PAPER) && + (stl->g_data->gp_cache_used > 0)) + { + DRW_draw_pass(psl->paper_pass); + } + } + + /* 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); + + MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + + DRW_draw_pass(psl->background_pass); + DRW_draw_pass(psl->drawing_pass); + + MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, dfbl->default_fb, txl); + + /* free memory */ + gpencil_free_obj_list(stl); + + /* grid pass */ + if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { + if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (v3d->flag3 & V3D_GP_SHOW_GRID)) + { + DRW_draw_pass(psl->grid_pass); + } + } + + return; + } + + if (DRW_state_is_fbo()) { + /* attach temp textures */ + GPU_framebuffer_texture_attach(fbl->temp_fb_a, e_data.temp_depth_tx_a, 0, 0); + GPU_framebuffer_texture_attach(fbl->temp_fb_a, e_data.temp_color_tx_a, 0, 0); + GPU_framebuffer_texture_attach(fbl->temp_fb_b, e_data.temp_depth_tx_b, 0, 0); + GPU_framebuffer_texture_attach(fbl->temp_fb_b, e_data.temp_color_tx_b, 0, 0); + + GPU_framebuffer_texture_attach(fbl->background_fb, e_data.background_depth_tx, 0, 0); + GPU_framebuffer_texture_attach(fbl->background_fb, e_data.background_color_tx, 0, 0); + + /* 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 = &stl->g_data->gp_object_cache[i]; + Object *ob = cache->ob; + bGPdata *gpd = ob->data; + init_grp = cache->init_grp; + end_grp = cache->end_grp; + /* Render stroke in separated framebuffer */ + GPU_framebuffer_bind(fbl->temp_fb_a); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f); + + /* Stroke Pass: DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND | DRW_STATE_WRITE_DEPTH + * draw only a subset that usually start with a fill and end with stroke because the + * shading groups are created by pairs */ + if (end_grp >= init_grp) { + MULTISAMPLE_GP_SYNC_ENABLE(stl->storage->multisamples, fbl); + + DRW_draw_pass_subset( + psl->stroke_pass, + stl->shgroups[init_grp].shgrps_fill != NULL ? + stl->shgroups[init_grp].shgrps_fill : stl->shgroups[init_grp].shgrps_stroke, + stl->shgroups[end_grp].shgrps_stroke); + + MULTISAMPLE_GP_SYNC_DISABLE(stl->storage->multisamples, fbl, fbl->temp_fb_a, txl); + } + + /* Current buffer drawing */ + if ((!is_render) && (gpd->runtime.sbuffer_size > 0)) { + DRW_draw_pass(psl->drawing_pass); + } + /* fx passes */ + if (BKE_shaderfx_has_gpencil(ob)) { + stl->storage->tonemapping = 0; + DRW_gpencil_fx_draw(&e_data, vedata, cache); + } + + e_data.input_depth_tx = e_data.temp_depth_tx_a; + e_data.input_color_tx = e_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 = stl->storage->is_render ? 1 : 0; + + DRW_draw_pass(psl->mix_pass); + + /* prepare for fast drawing */ + if (!is_render) { + gpencil_prepare_fast_drawing(stl, dfbl, fbl, psl->mix_pass_noblend, clearcol); + } + } + /* edit points */ + if ((!is_render) && (!playing)) { + DRW_draw_pass(psl->edit_pass); + } + } + /* grid pass */ + if ((!is_render) && (obact) && (obact->type == OB_GPENCIL)) { + if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (v3d->flag3 & V3D_GP_SHOW_GRID)) + { + DRW_draw_pass(psl->grid_pass); + } + } + } + /* free memory */ + gpencil_free_obj_list(stl); + + /* detach temp textures */ + if (DRW_state_is_fbo()) { + GPU_framebuffer_texture_detach(fbl->temp_fb_a, e_data.temp_depth_tx_a); + GPU_framebuffer_texture_detach(fbl->temp_fb_a, e_data.temp_color_tx_a); + GPU_framebuffer_texture_detach(fbl->temp_fb_b, e_data.temp_depth_tx_b); + GPU_framebuffer_texture_detach(fbl->temp_fb_b, e_data.temp_color_tx_b); + + GPU_framebuffer_texture_detach(fbl->background_fb, e_data.background_depth_tx); + GPU_framebuffer_texture_detach(fbl->background_fb, e_data.background_color_tx); + + /* attach again default framebuffer after detach textures */ + if (!is_render) { + GPU_framebuffer_bind(dfbl->default_fb); + } + + /* 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; + } + } +} + +static const DrawEngineDataSize GPENCIL_data_size = DRW_VIEWPORT_DATA_SIZE(GPENCIL_Data); + +DrawEngineType draw_engine_gpencil_type = { + NULL, NULL, + N_("GpencilMode"), + &GPENCIL_data_size, + &GPENCIL_engine_init, + &GPENCIL_engine_free, + &GPENCIL_cache_init, + &GPENCIL_cache_populate, + &GPENCIL_cache_finish, + NULL, + &GPENCIL_draw_scene, + NULL, + NULL, + &GPENCIL_render_to_image, +}; diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h new file mode 100644 index 00000000000..24a627f1012 --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -0,0 +1,355 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_engine.h + * \ingroup draw + */ + +#ifndef __GPENCIL_ENGINE_H__ +#define __GPENCIL_ENGINE_H__ + +#include "GPU_batch.h" + +struct tGPspoint; +struct bGPDstroke; +struct ModifierData; +struct GPENCIL_Data; +struct GPENCIL_StorageList; +struct Object; +struct MaterialGPencilStyle; +struct RenderEngine; +struct RenderLayer; + + /* TODO: these could be system parameter in userprefs screen */ +#define GPENCIL_MAX_GP_OBJ 256 + +#define GPENCIL_CACHE_BLOCK_SIZE 8 +#define GPENCIL_MAX_SHGROUPS 65536 +#define GPENCIL_MIN_BATCH_SLOTS_CHUNK 16 + +#define GPENCIL_COLOR_SOLID 0 +#define GPENCIL_COLOR_TEXTURE 1 +#define GPENCIL_COLOR_PATTERN 2 + +#define GP_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) +#define GP_SIMPLIFY_ONPLAY(playing) (((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0)) +#define GP_SIMPLIFY_FILL(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL))) +#define GP_SIMPLIFY_MODIF(scene, playing) ((GP_SIMPLIFY_ONPLAY(playing) && (GP_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER))) + +#define GP_IS_CAMERAVIEW ((rv3d != NULL) && (rv3d->persp == RV3D_CAMOB && v3d->camera)) + + /* *********** OBJECTS CACHE *********** */ + + /* used to save gpencil objects */ +typedef struct tGPencilObjectCache { + struct Object *ob; + int init_grp, end_grp; + int idx; /*original index, can change after sort */ + + /* effects */ + DRWShadingGroup *fx_wave_sh; + DRWShadingGroup *fx_blur_sh; + DRWShadingGroup *fx_colorize_sh; + DRWShadingGroup *fx_pixel_sh; + DRWShadingGroup *fx_rim_sh; + DRWShadingGroup *fx_swirl_sh; + DRWShadingGroup *fx_flip_sh; + DRWShadingGroup *fx_light_sh; + + float zdepth; /* z-depth value to sort gp object */ + bool temp_ob; /* flag to tag temporary objects that must be removed after drawing loop */ +} tGPencilObjectCache; + + /* *********** 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; + float obj_scale; + struct DRWShadingGroup *shgrps_fill; + struct DRWShadingGroup *shgrps_stroke; +} GPENCIL_shgroup; + +typedef struct GPENCIL_Storage { + int shgroup_id; /* total elements */ + float unit_matrix[4][4]; + int stroke_style; + int color_type; + int mode; + int xray; + int keep_size; + float obj_scale; + float pixfactor; + int playing; + bool is_render; + bool is_mat_preview; + const float *pixsize; + float render_pixsize; + int tonemapping; + short multisamples; + + /* simplify settings*/ + bool simplify_fill; + bool simplify_modif; + bool simplify_fx; + + /* Render Matrices and data */ + float persmat[4][4], persinv[4][4]; + float viewmat[4][4], viewinv[4][4]; + float winmat[4][4], wininv[4][4]; + float view_vecs[2][4]; /* vec4[2] */ + + Object *camera; /* camera pointer for render mode */ +} GPENCIL_Storage; + +typedef struct GPENCIL_StorageList { + struct GPENCIL_Storage *storage; + struct g_data *g_data; + struct GPENCIL_shgroup *shgroups; +} GPENCIL_StorageList; + +typedef struct GPENCIL_PassList { + struct DRWPass *stroke_pass; + 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; + + /* effects */ + struct DRWPass *fx_shader_pass; + struct DRWPass *fx_shader_pass_blend; + +} GPENCIL_PassList; + +typedef struct GPENCIL_FramebufferList { + struct GPUFrameBuffer *main; + struct GPUFrameBuffer *temp_fb_a; + struct GPUFrameBuffer *temp_fb_b; + struct GPUFrameBuffer *temp_fb_rim; + struct GPUFrameBuffer *background_fb; + + struct GPUFrameBuffer *multisample_fb; +} GPENCIL_FramebufferList; + +typedef struct GPENCIL_TextureList { + struct GPUTexture *texture; + + /* multisample textures */ + struct GPUTexture *multisample_color; + struct GPUTexture *multisample_depth; + +} GPENCIL_TextureList; + +typedef struct GPENCIL_Data { + void *engine_type; /* Required */ + struct GPENCIL_FramebufferList *fbl; + 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; + + /* for buffer only one batch is nedeed because the drawing is only of one stroke */ + GPUBatch *batch_buffer_stroke; + GPUBatch *batch_buffer_fill; + + /* grid geometry */ + GPUBatch *batch_grid; + + int gp_cache_used; /* total objects in cache */ + int gp_cache_size; /* size of the cache */ + struct tGPencilObjectCache *gp_object_cache; + + int session_flag; + +} 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 { + /* 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_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_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_swirl_sh; + struct GPUShader *gpencil_fx_wave_sh; + + /* textures */ + struct GPUTexture *background_depth_tx; + struct GPUTexture *background_color_tx; + + struct GPUTexture *gpencil_blank_texture; + + /* 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_rim; + struct GPUTexture *temp_depth_tx_rim; +} GPENCIL_e_data; /* Engine data */ + +/* GPUBatch Cache */ +typedef struct GpencilBatchCache { + /* For normal strokes, a variable number of batch can be needed depending of number of strokes. + It could use the stroke number as total size, but when activate the onion skining, the number + can change, so the size is changed dinamically. + */ + GPUBatch **batch_stroke; + GPUBatch **batch_fill; + GPUBatch **batch_edit; + GPUBatch **batch_edlin; + + /* settings to determine if cache is invalid */ + bool is_dirty; + bool is_editmode; + int cache_frame; + + /* keep information about the size of the cache */ + int cache_size; /* total batch slots available */ + int cache_idx; /* current slot index */ +} GpencilBatchCache; + +/* general drawing functions */ +struct DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, struct DRWPass *pass, struct GPUShader *shader, + struct Object *ob, struct bGPdata *gpd, struct MaterialGPencilStyle *gp_style, int id, bool onion); +void DRW_gpencil_populate_datablock(struct GPENCIL_e_data *e_data, void *vedata, struct Scene *scene, struct Object *ob, struct bGPdata *gpd); +void DRW_gpencil_populate_buffer_strokes(struct GPENCIL_e_data *e_data, void *vedata, struct ToolSettings *ts, struct Object *ob); +void DRW_gpencil_populate_multiedit(struct GPENCIL_e_data *e_data, void *vedata, struct Scene *scene, struct Object *ob, struct bGPdata *gpd); +void DRW_gpencil_triangulate_stroke_fill(struct bGPDstroke *gps); + +void DRW_gpencil_multisample_ensure(struct GPENCIL_Data *vedata, int rect_w, int rect_h); + +/* create geometry functions */ +struct GPUBatch *DRW_gpencil_get_point_geom(struct bGPDstroke *gps, short thickness, const float ink[4]); +struct GPUBatch *DRW_gpencil_get_stroke_geom(struct bGPDframe *gpf, struct bGPDstroke *gps, short thickness, const float ink[4]); +struct GPUBatch *DRW_gpencil_get_fill_geom(struct Object *ob, struct bGPDstroke *gps, const float color[4]); +struct GPUBatch *DRW_gpencil_get_edit_geom(struct bGPDstroke *gps, float alpha, short dflag); +struct GPUBatch *DRW_gpencil_get_edlin_geom(struct bGPDstroke *gps, float alpha, short dflag); +struct GPUBatch *DRW_gpencil_get_buffer_stroke_geom(struct bGPdata *gpd, float matrix[4][4], short thickness); +struct GPUBatch *DRW_gpencil_get_buffer_fill_geom(struct bGPdata *gpd); +struct GPUBatch *DRW_gpencil_get_buffer_point_geom(struct bGPdata *gpd, float matrix[4][4], short thickness); +struct GPUBatch *DRW_gpencil_get_grid(void); + +/* object cache functions */ +struct tGPencilObjectCache *gpencil_object_cache_add(struct tGPencilObjectCache *cache_array, struct Object *ob, + bool is_temp, int *gp_cache_size, int *gp_cache_used); + +/* geometry batch cache functions */ +void gpencil_batch_cache_check_free_slots(struct Object *ob); +struct GpencilBatchCache *gpencil_batch_cache_get(struct Object *ob, int cfra); + +/* modifier functions */ +void gpencil_instance_modifiers(struct GPENCIL_StorageList *stl, struct Object *ob); + +/* 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); + +void DRW_gpencil_fx_prepare( + struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, + struct tGPencilObjectCache *cache); +void DRW_gpencil_fx_draw( + struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, + struct tGPencilObjectCache *cache); + +/* main functions */ +void GPENCIL_engine_init(void *vedata); +void GPENCIL_cache_init(void *vedata); +void GPENCIL_cache_populate(void *vedata, struct Object *ob); +void GPENCIL_cache_finish(void *vedata); +void GPENCIL_draw_scene(void *vedata); + +/* render */ +void GPENCIL_render_init(struct GPENCIL_Data *ved, struct RenderEngine *engine, struct Depsgraph *depsgraph); +void GPENCIL_render_to_image(void *vedata, struct RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect); + +/* Use of multisample framebuffers. */ +#define MULTISAMPLE_GP_SYNC_ENABLE(lvl, fbl) { \ + if ((lvl > 0) && (fbl->multisample_fb != NULL)) { \ + DRW_stats_query_start("GP Multisample Blit"); \ + GPU_framebuffer_bind(fbl->multisample_fb); \ + GPU_framebuffer_clear_color_depth(fbl->multisample_fb, (const float[4]){0.0f}, 1.0f); \ + DRW_stats_query_end(); \ + } \ +} + +#define MULTISAMPLE_GP_SYNC_DISABLE(lvl, fbl, fb, txl) { \ + if ((lvl > 0) && (fbl->multisample_fb != NULL)) { \ + DRW_stats_query_start("GP Multisample Resolve"); \ + GPU_framebuffer_bind(fb); \ + DRW_multisamples_resolve(txl->multisample_depth, txl->multisample_color, true); \ + DRW_stats_query_end(); \ + } \ +} + +#endif /* __GPENCIL_ENGINE_H__ */ diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c new file mode 100644 index 00000000000..d76ea56894f --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -0,0 +1,353 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_render.c + * \ingroup draw + */ +#include "BLI_rect.h" + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "BKE_camera.h" + +#include "DNA_gpencil_types.h" + +#include "DEG_depsgraph_query.h" + +#include "draw_mode_engines.h" + +#include "RE_pipeline.h" + +#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) +{ + GPENCIL_Data *vedata = (GPENCIL_Data *)ved; + GPENCIL_StorageList *stl = vedata->stl; + GPENCIL_FramebufferList *fbl = vedata->fbl; + + 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 multiframe framebuffer for AA */ + if (U.gpencil_multisamples > 0) { + int rect_w = (int)viewport_size[0]; + int rect_h = (int)viewport_size[1]; + DRW_gpencil_multisample_ensure(vedata, rect_w, rect_h); + } + + vedata->render_depth_tx = DRW_texture_pool_query_2D(size[0], size[1], GPU_DEPTH24_STENCIL8, + &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. */ + struct Object *camera = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re)); + float frame = BKE_scene_frame_get(scene); + RE_GetCameraWindow(engine->re, camera, frame, stl->storage->winmat); + RE_GetCameraModelMatrix(engine->re, camera, stl->storage->viewinv); + + invert_m4_m4(stl->storage->viewmat, stl->storage->viewinv); + mul_m4_m4m4(stl->storage->persmat, stl->storage->winmat, stl->storage->viewmat); + invert_m4_m4(stl->storage->persinv, stl->storage->persmat); + invert_m4_m4(stl->storage->wininv, stl->storage->winmat); + + DRW_viewport_matrix_override_set(stl->storage->persmat, DRW_MAT_PERS); + DRW_viewport_matrix_override_set(stl->storage->persinv, DRW_MAT_PERSINV); + DRW_viewport_matrix_override_set(stl->storage->winmat, DRW_MAT_WIN); + DRW_viewport_matrix_override_set(stl->storage->wininv, DRW_MAT_WININV); + DRW_viewport_matrix_override_set(stl->storage->viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_override_set(stl->storage->viewinv, DRW_MAT_VIEWINV); + + /* calculate pixel size for render */ + stl->storage->render_pixsize = get_render_pixelsize(stl->storage->persmat, viewport_size[0], viewport_size[1]); + /* INIT CACHE */ + GPENCIL_cache_init(vedata); +} + +/* 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)) +{ + if ((ob == NULL) || (DRW_check_object_visible_within_active_context(ob) == false)) { + return; + } + + if (ob->type == OB_GPENCIL) { + 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], 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_viewport_matrix_get(winmat, DRW_MAT_WIN); + DRW_viewport_matrix_get(invproj, DRW_MAT_WININV); + + /* 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, const rcti *rect) +{ + 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, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), rp->rect); + + bool is_persp = DRW_viewport_is_persp_get(); + + GPENCIL_render_update_vecs(vedata); + + /* 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) { + rp->rect[i] = rp->rect[i] * 2.0f - 1.0f; + rp->rect[i] = stl->storage->winmat[3][2] / (rp->rect[i] + stl->storage->winmat[2][2]); + } + else { + rp->rect[i] = -stl->storage->view_vecs[0][2] + rp->rect[i] * -stl->storage->view_vecs[1][2]; + } + } + } + } +} + +/* read combined render result */ +static void GPENCIL_render_result_combined(struct RenderLayer *rl, const char *viewname, GPENCIL_Data *vedata, const rcti *rect) +{ + 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, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), BLI_rcti_size_y(rect), 4, 0, rp->rect); +} + +/* helper to blend pixels */ +static void blend_pixel(float src[4], float dst[4]) +{ + float alpha = src[3]; + + /* use blend: GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA */ + dst[0] = (src[0] * alpha) + (dst[0] * (1.0f - alpha)); + dst[1] = (src[1] * alpha) + (dst[1] * (1.0f - alpha)); + dst[2] = (src[2] * alpha) + (dst[2] * (1.0f - alpha)); +} + +/* render grease pencil to image */ +void GPENCIL_render_to_image(void *vedata, RenderEngine *engine, struct RenderLayer *render_layer, const rcti *rect) +{ + 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"); + } + + 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); + + GPENCIL_cache_finish(vedata); + 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; + float tmp[4]; + + 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) { + copy_v4_v4(tmp, gp_pixel_rgba); + if (src_pixel_rgba[3] > 0.0f) { + /* copy source color on back */ + copy_v4_v4(gp_pixel_rgba, src_pixel_rgba); + /* 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 gp render */ + blend_pixel(tmp, gp_pixel_rgba); + /* blend object on top */ + blend_pixel(src_pixel_rgba, gp_pixel_rgba); + } + else { + /* blend gp render */ + blend_pixel(tmp, gp_pixel_rgba); + } + } + } + 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_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c new file mode 100644 index 00000000000..e453224020d --- /dev/null +++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c @@ -0,0 +1,848 @@ +/* + * Copyright 2017, Blender Foundation. + * + * 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. + * + * Contributor(s): Antonio Vazquez + * + */ + +/** \file blender/draw/engines/gpencil/gpencil_shader_fx.c + * \ingroup draw + */ +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" +#include "DNA_screen_types.h" +#include "DNA_view3d_types.h" +#include "DNA_camera_types.h" + +#include "BKE_gpencil.h" +#include "BKE_shader_fx.h" + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "BKE_camera.h" + +#include "ED_view3d.h" +#include "ED_gpencil.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_swirl_frag_glsl[]; +extern char datatoc_gpencil_fx_wave_frag_glsl[]; + +/* verify if this fx is active */ +static bool effect_is_active(Object *ob, ShaderFxData *fx, bool is_render) +{ + if (fx == NULL) { + return false; + } + + bGPdata *gpd = ob->data; + if (gpd == NULL) { + return false; + } + + bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); + if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit)) { + return false; + } + + if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) || + ((fx->mode & eShaderFxMode_Render) && (is_render == true))) + { + 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]) +{ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* 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; + } + + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + Camera *cam = (Camera *)camera->data; + + float fstop = cam->gpu_dof.fstop; + float focus_dist = BKE_camera_object_dof_distance(camera); + float focal_len = cam->lens; + + /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm + * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though + * because the shader reads coordinates in world space, which is in blender units. + * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */ + float scale = (scene->unit.system) ? scene->unit.scale_length : 1.0f; + float scale_camera = 0.001f / scale; + /* 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; + + 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); +} + +/* **************** 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 DRW_gpencil_fx_blur( + ShaderFxData *fx, int ob_idx, GPENCIL_e_data *e_data, + GPENCIL_Data *vedata, tGPencilObjectCache *cache) +{ + if (fx == NULL) { + 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; + + Object *ob = cache->ob; + bGPdata *gpd = (bGPdata *)ob->data; + + 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; + } + } + + struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); + + fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_blur_sh, + psl->fx_shader_pass_blend); + DRW_shgroup_call_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a); + DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); + + DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* Colorize FX */ +static void DRW_gpencil_fx_colorize( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +{ + if (fx == NULL) { + return; + } + ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + DRWShadingGroup *fx_shgrp; + + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_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 DRW_gpencil_fx_flip( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +{ + if (fx == NULL) { + return; + } + 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; + } + + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a); + DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1); + + DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* Light FX */ +static void DRW_gpencil_fx_light( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata, + tGPencilObjectCache *cache) +{ + if (fx == NULL) { + return; + } + Object *ob = cache->ob; + 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; + + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_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->loc[0]); + + /* 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 = (bGPdata *)ob->data; + if (!get_normal_vector(gpd, r_point, r_normal)) { + return; + } + mul_mat3_m4_v3(ob->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->loc, r_plane); + fxd->loc[3] = dt; /* use last element to save it */ + + DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1); + + DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1); + DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1); + + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* Pixelate FX */ +static void DRW_gpencil_fx_pixel( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata, + tGPencilObjectCache *cache) +{ + if (fx == NULL) { + return; + } + Object *ob = cache->ob; + PixelShaderFxData *fxd = (PixelShaderFxData *)fx; + + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + DRWShadingGroup *fx_shgrp; + bGPdata *gpd = (bGPdata *)ob->data; + + fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES; + + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_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", &ob->loc[0], 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* Rim FX */ +static void DRW_gpencil_fx_rim( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata, + tGPencilObjectCache *cache) +{ + if (fx == NULL) { + return; + } + Object *ob = cache->ob; + RimShaderFxData *fxd = (RimShaderFxData *)fx; + + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + DRWShadingGroup *fx_shgrp; + bGPdata *gpd = (bGPdata *)ob->data; + + struct GPUBatch *fxquad = DRW_cache_fullscreen_quad_get(); + /* prepare pass */ + fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_rim_prepare_sh, + psl->fx_shader_pass_blend); + DRW_shgroup_call_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_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", &ob->loc[0], 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_rim); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_rim); + DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2); + + DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &ob->loc[0], 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &e_data->temp_color_tx_rim); + 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; +} + +/* Swirl FX */ +static void DRW_gpencil_fx_swirl( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata, + tGPencilObjectCache *cache) +{ + if (fx == NULL) { + return; + } + Object *ob = cache->ob; + SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; + if (fxd->object == NULL) { + return; + } + + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + DRWShadingGroup *fx_shgrp; + bGPdata *gpd = (bGPdata *)ob->data; + + fxd->transparent = (int)fxd->flag & FX_SWIRL_MAKE_TRANSPARENT; + + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a); + + DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1); + + DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &fxd->object->loc[0], 1); + + 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); + + DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixelsize", &U.pixelsize, 1); + DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* Wave Distorsion FX */ +static void DRW_gpencil_fx_wave( + ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata) +{ + if (fx == NULL) { + return; + } + + WaveShaderFxData *fxd = (WaveShaderFxData *)fx; + + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + struct 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_add(fx_shgrp, fxquad, NULL); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a); + DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_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); + + fxd->runtime.fx_sh = fx_shgrp; +} + +/* ************************************************************** */ + +/* 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); + + e_data->gpencil_fx_rim_resolve_sh = DRW_shader_create_fullscreen( + datatoc_gpencil_fx_rim_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); + } +} + +/* 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_swirl_sh); + DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_wave_sh); +} + +/* 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 | + DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); +} + + +/* prepare fx shading groups */ +void DRW_gpencil_fx_prepare( + struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, + struct tGPencilObjectCache *cache) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + Object *ob = cache->ob; + int ob_idx = cache->idx; + + if (ob->shader_fx.first == NULL) { + return; + } + /* loop FX */ + for (ShaderFxData *fx = ob->shader_fx.first; fx; fx = fx->next) { + if (effect_is_active(ob, fx, stl->storage->is_render)) { + switch (fx->type) { + case eShaderFxType_Blur: + DRW_gpencil_fx_blur(fx, ob_idx, e_data, vedata, cache); + break; + case eShaderFxType_Colorize: + DRW_gpencil_fx_colorize(fx, e_data, vedata); + break; + case eShaderFxType_Flip: + DRW_gpencil_fx_flip(fx, e_data, vedata); + break; + case eShaderFxType_Light: + DRW_gpencil_fx_light(fx, e_data, vedata, cache); + break; + case eShaderFxType_Pixel: + DRW_gpencil_fx_pixel(fx, e_data, vedata, cache); + break; + case eShaderFxType_Rim: + DRW_gpencil_fx_rim(fx, e_data, vedata, cache); + break; + case eShaderFxType_Swirl: + DRW_gpencil_fx_swirl(fx, e_data, vedata, cache); + break; + case eShaderFxType_Wave: + DRW_gpencil_fx_wave(fx, e_data, vedata); + break; + default: + break; + } + } + } + +} + +/* helper to draw one FX pass and do ping-pong copy */ +static void gpencil_draw_fx_pass( + GPENCIL_e_data *e_data, + GPENCIL_PassList *psl, + GPENCIL_FramebufferList *fbl, + DRWShadingGroup *shgrp, bool blend) +{ + if (shgrp == NULL) { + return; + } + + static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 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 */ + e_data->input_depth_tx = e_data->temp_depth_tx_b; + e_data->input_color_tx = e_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( + struct GPENCIL_e_data *e_data, + struct GPENCIL_Data *vedata, + struct BlurShaderFxData *fxd) +{ + if (fxd->runtime.fx_sh == NULL) { + return; + } + + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + 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(e_data, psl, fbl, shgrp, true); + } + /* vertical */ + if (by > 0) { + fxd->blur[0] = 0; + fxd->blur[1] = by; + gpencil_draw_fx_pass(e_data, psl, fbl, shgrp, true); + } + } +} + +static void draw_gpencil_rim_blur( + struct GPENCIL_e_data *UNUSED(e_data), + struct GPENCIL_Data *vedata, + struct RimShaderFxData *fxd) +{ + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 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, + fxd->runtime.fx_sh_b, fxd->runtime.fx_sh_b); + + /* copy pass from b for ping-pong frame buffers */ + GPU_framebuffer_bind(fbl->temp_fb_rim); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_rim, clearcol, 1.0f); + DRW_draw_pass(psl->mix_pass_noblend); +} + +/* helper to draw RIM passes */ +static void draw_gpencil_rim_passes( + struct GPENCIL_e_data *e_data, + struct GPENCIL_Data *vedata, + struct RimShaderFxData *fxd) +{ + if (fxd->runtime.fx_sh_b == NULL) { + return; + } + + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + + static float clearcol[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + int bx = fxd->blur[0]; + int by = fxd->blur[1]; + + /* prepare mask */ + GPU_framebuffer_bind(fbl->temp_fb_rim); + GPU_framebuffer_clear_color_depth(fbl->temp_fb_rim, clearcol, 1.0f); + DRW_draw_pass_subset( + psl->fx_shader_pass_blend, + fxd->runtime.fx_sh, fxd->runtime.fx_sh); + + /* blur rim */ + e_data->input_depth_tx = e_data->temp_depth_tx_b; + e_data->input_color_tx = e_data->temp_color_tx_b; + + if ((fxd->samples > 0) && ((bx > 0) || (by > 0))) { + for (int x = 0; x < fxd->samples; x++) { + + /* horizontal */ + fxd->blur[0] = bx; + fxd->blur[1] = 0; + draw_gpencil_rim_blur(e_data, vedata, fxd); + + /* Vertical */ + fxd->blur[0] = 0; + fxd->blur[1] = by; + draw_gpencil_rim_blur(e_data, vedata, fxd); + + fxd->blur[0] = bx; + fxd->blur[1] = by; + } + } + /* 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 */ + e_data->input_depth_tx = e_data->temp_depth_tx_b; + e_data->input_color_tx = e_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 DRW_gpencil_fx_draw( + struct GPENCIL_e_data *e_data, + struct GPENCIL_Data *vedata, struct tGPencilObjectCache *cache) +{ + GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl; + GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl; + GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl; + Object *ob = cache->ob; + + /* loop FX modifiers */ + for (ShaderFxData *fx = ob->shader_fx.first; fx; fx = fx->next) { + if (effect_is_active(ob, fx, stl->storage->is_render)) { + switch (fx->type) { + case eShaderFxType_Blur: + { + BlurShaderFxData *fxd = (BlurShaderFxData *)fx; + draw_gpencil_blur_passes(e_data, vedata, fxd); + break; + } + case eShaderFxType_Colorize: + { + ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + case eShaderFxType_Flip: + { + FlipShaderFxData *fxd = (FlipShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + case eShaderFxType_Light: + { + LightShaderFxData *fxd = (LightShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + case eShaderFxType_Pixel: + { + PixelShaderFxData *fxd = (PixelShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + case eShaderFxType_Rim: + { + RimShaderFxData *fxd = (RimShaderFxData *)fx; + draw_gpencil_rim_passes(e_data, vedata, fxd); + break; + } + case eShaderFxType_Swirl: + { + SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + case eShaderFxType_Wave: + { + WaveShaderFxData *fxd = (WaveShaderFxData *)fx; + gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false); + break; + } + default: + break; + } + } + } +} 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 new file mode 100644 index 00000000000..1d66ba3d4d4 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_blur_frag.glsl @@ -0,0 +1,60 @@ +uniform mat4 ProjectionMatrix; +uniform mat4 ViewMatrix; + +uniform sampler2D strokeColor; +uniform sampler2D strokeDepth; + +uniform int blur[2]; + +uniform vec3 loc; +uniform float pixsize; /* rv3d->pixsize */ +uniform float pixelsize; /* U.pixelsize */ +uniform float pixfactor; + +float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor); +vec2 noffset = vec2(blur[0], blur[1]); + +out vec4 FragColor; + +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); + + /* apply blurring, using a 9-tap filter with predefined gaussian weights */ + /* depth */ + float outdepth = 0; + outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y + 1.0 * dy), 0).r * 0.0947416; + outdepth += texelFetch(strokeDepth, ivec2(uv.x - 0.0 * dx, uv.y + 1.0 * dy), 0).r * 0.118318; + outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y + 1.0 * dy), 0).r * 0.0947416; + outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y + 0.0 * dy), 0).r * 0.118318; + + outdepth += texelFetch(strokeDepth, ivec2(uv.x, uv.y), 0).r * 0.147761; + + outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y + 0.0 * dy), 0).r * 0.118318; + outdepth += texelFetch(strokeDepth, ivec2(uv.x - 1.0 * dx, uv.y - 1.0 * dy), 0).r * 0.0947416; + outdepth += texelFetch(strokeDepth, ivec2(uv.x + 0.0 * dx, uv.y - 1.0 * dy), 0).r * 0.118318; + outdepth += texelFetch(strokeDepth, ivec2(uv.x + 1.0 * dx, uv.y - 1.0 * dy), 0).r * 0.0947416; + + 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); +} 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 new file mode 100644 index 00000000000..7d0ce4a804e --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_colorize_frag.glsl @@ -0,0 +1,86 @@ +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_BITONE 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_BITONE: + { + 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 new file mode 100644 index 00000000000..94fb3405c79 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_flip_frag.glsl @@ -0,0 +1,37 @@ +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_light_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl new file mode 100644 index 00000000000..f3026c32fc8 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_light_frag.glsl @@ -0,0 +1,70 @@ +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 pixelsize; /* U.pixelsize */ +uniform float pixfactor; + +out vec4 FragColor; + +float defaultpixsize = pixsize * pixelsize * (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 new file mode 100644 index 00000000000..d1a57a9a1b6 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_pixel_frag.glsl @@ -0,0 +1,50 @@ +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 pixelsize; /* U.pixelsize */ +uniform float pixfactor; + +out vec4 FragColor; + +int uselines = size[2]; +float defaultpixsize = pixsize * pixelsize * (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 new file mode 100644 index 00000000000..fe35d3832e1 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_prepare_frag.glsl @@ -0,0 +1,64 @@ +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 pixelsize; /* U.pixelsize */ +uniform float pixfactor; + +float defaultpixsize = pixsize * pixelsize * (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 new file mode 100644 index 00000000000..5e5edbd8325 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_rim_resolve_frag.glsl @@ -0,0 +1,101 @@ +/* ******************************************************************* */ +/* 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_swirl_frag.glsl b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl new file mode 100644 index 00000000000..6ce64350b3d --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_swirl_frag.glsl @@ -0,0 +1,70 @@ +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 pixelsize; /* U.pixelsize */ +uniform float pixfactor; + +out vec4 FragColor; + +float defaultpixsize = pixsize * pixelsize * (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 pxradius = (ProjectionMatrix[3][3] == 0.0) ? (radius / (loc.z * defaultpixsize)) : (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 new file mode 100644 index 00000000000..882b2cf59f1 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/fx/gpencil_fx_wave_frag.glsl @@ -0,0 +1,40 @@ + +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; +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl new file mode 100644 index 00000000000..cbd7a461dd3 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_background_frag.glsl @@ -0,0 +1,12 @@ +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_edit_point_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl new file mode 100644 index 00000000000..b3bd8e488f2 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_frag.glsl @@ -0,0 +1,17 @@ +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 new file mode 100644 index 00000000000..0d2da00db66 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_geom.glsl @@ -0,0 +1,48 @@ +uniform mat4 ModelViewProjectionMatrix; +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; +} + +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, 0, 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 0); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x - size, sp0.y - size) / Viewport, 0, 1.0); + EmitVertex(); + + mTexCoord = vec2(1, 1); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x + size, sp0.y + size) / Viewport, 0, 1.0); + EmitVertex(); + + mTexCoord = vec2(1, 0); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x + size, sp0.y - size) / Viewport, 0, 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 new file mode 100644 index 00000000000..77fdf58bea0 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_edit_point_vert.glsl @@ -0,0 +1,15 @@ +uniform mat4 ModelViewProjectionMatrix; + +in vec3 pos; +in vec4 color; +in float size; + +out vec4 finalColor; +out float finalThickness; + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + finalColor = color; + finalThickness = size; +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl new file mode 100644 index 00000000000..35f47d6c418 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_frag.glsl @@ -0,0 +1,140 @@ +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 sampler2D myTexture; +uniform int texture_clamp; + +/* keep this list synchronized with list in gpencil_draw_utils.c */ +#define SOLID 0 +#define GRADIENT 1 +#define RADIAL 2 +#define CHESS 3 +#define TEXTURE 4 +#define PATTERN 5 + +#define GP_XRAY_FRONT 0 +#define GP_XRAY_3DSPACE 1 +#define GP_XRAY_BACK 2 + +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); + } + } +} + +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) ? texture2D(myTexture, rot_tex * texture_scale) : texture2D(myTexture, 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 chesscolor; + + /* 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); + } + /* chessboard */ + if (fill_type == CHESS) { + 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)) { + chesscolor = (texture_flip == 0) ? finalColor : color2; + } + else { + chesscolor = (texture_flip == 0) ? color2 : finalColor; + } + /* mix with texture */ + fragColor = (texture_mix == 1) ? mix(chesscolor, text_color, mix_factor) : chesscolor; + } + /* texture */ + if (fill_type == TEXTURE) { + fragColor = (texture_mix == 1) ? mix(text_color, finalColor, mix_factor) : text_color; + } + /* pattern */ + if (fill_type == PATTERN) { + fragColor = finalColor; + fragColor.a = min(text_color.a, finalColor.a); + } + } + + /* set zdepth */ + if (xraymode == GP_XRAY_FRONT) { + gl_FragDepth = 0.0; + } + if (xraymode == GP_XRAY_3DSPACE) { + gl_FragDepth = gl_FragCoord.z; + } + if (xraymode == GP_XRAY_BACK) { + gl_FragDepth = 0.999999; + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl new file mode 100644 index 00000000000..52da354a562 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_fill_vert.glsl @@ -0,0 +1,14 @@ +uniform mat4 ModelViewProjectionMatrix; + +in vec3 pos; +in vec4 color; +in vec2 texCoord; +out vec4 finalColor; +out vec2 texCoord_interp; + +void main(void) +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + finalColor = color; + texCoord_interp = texCoord; +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl new file mode 100644 index 00000000000..c2e3f787bec --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_paper_frag.glsl @@ -0,0 +1,9 @@ +uniform vec3 color; +uniform float opacity; + +out vec4 FragColor; + +void main() +{ + FragColor = vec4(color, 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 new file mode 100644 index 00000000000..0d6d2b22a55 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_frag.glsl @@ -0,0 +1,49 @@ +uniform int color_type; +uniform int mode; +uniform sampler2D myTexture; + +in vec4 mColor; +in vec2 mTexCoord; +out vec4 fragColor; + +#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 + +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 ((mode != GPENCIL_MODE_BOX) && (dist_squared > rad_squared)) + discard; + + vec4 tmp_color = texture2D(myTexture, mTexCoord); + + /* Solid */ + if (color_type == GPENCIL_COLOR_SOLID) { + fragColor = mColor; + } + /* texture */ + if (color_type == GPENCIL_COLOR_TEXTURE) { + fragColor = texture2D(myTexture, mTexCoord); + /* 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) { + vec4 text_color = texture2D(myTexture, 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); + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl new file mode 100644 index 00000000000..f092149430c --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_geom.glsl @@ -0,0 +1,82 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform vec2 Viewport; +uniform int xraymode; + +layout(points) in; +layout(triangle_strip, max_vertices = 4) out; + +in vec4 finalColor[1]; +in float finalThickness[1]; +in vec2 finaluvdata[1]; + +out vec4 mColor; +out vec2 mTexCoord; + +#define GP_XRAY_FRONT 0 +#define GP_XRAY_3DSPACE 1 +#define GP_XRAY_BACK 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 0.0; + } + if (xraymode == GP_XRAY_3DSPACE) { + return (point.z / point.w); + } + if (xraymode == GP_XRAY_BACK) { + return 0.999999; + } + + /* in front by default */ + return 0.0; +} + +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); +} + +void main(void) +{ + /* receive 4 points */ + vec4 P0 = gl_in[0].gl_Position; + vec2 sp0 = toScreenSpace(P0); + + float size = finalThickness[0]; + float aspect = 1.0; + /* generate the triangle strip */ + mTexCoord = rotateUV(vec2(0, 1), finaluvdata[0].y); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x - size, sp0.y + size * aspect) / Viewport, getZdepth(P0), 1.0); + EmitVertex(); + + mTexCoord = rotateUV(vec2(0, 0), finaluvdata[0].y); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x - size, sp0.y - size * aspect) / Viewport, getZdepth(P0), 1.0); + EmitVertex(); + + mTexCoord = rotateUV(vec2(1, 1), finaluvdata[0].y); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x + size, sp0.y + size * aspect) / Viewport, getZdepth(P0), 1.0); + EmitVertex(); + + mTexCoord = rotateUV(vec2(1, 0), finaluvdata[0].y); + mColor = finalColor[0]; + gl_Position = vec4(vec2(sp0.x + size, sp0.y - size * aspect) / 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 new file mode 100644 index 00000000000..5e89bf8e5ce --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_point_vert.glsl @@ -0,0 +1,37 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ProjectionMatrix; + +uniform float pixsize; /* rv3d->pixsize */ +uniform float pixelsize; /* U.pixelsize */ +uniform int keep_size; +uniform float objscale; +uniform float pixfactor; + +in vec3 pos; +in vec4 color; +in float thickness; +in vec2 uvdata; + +out vec4 finalColor; +out float finalThickness; +out vec2 finaluvdata; + +#define TRUE 1 + +float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor); + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + 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, 4.0); /* minimum 4 pixels */ + } + + 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 new file mode 100644 index 00000000000..dd54e38c3d0 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_simple_mix_frag.glsl @@ -0,0 +1,15 @@ +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 new file mode 100644 index 00000000000..d57921c1629 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_frag.glsl @@ -0,0 +1,46 @@ +uniform int color_type; +uniform sampler2D myTexture; + +in vec4 mColor; +in vec2 mTexCoord; +in float 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 + +void main() +{ + vec4 tColor = vec4(mColor); + /* if alpha < 0, then encap (only solid mode ) */ + if ((mColor.a < 0) && (color_type == GPENCIL_COLOR_SOLID)) { + vec2 center = vec2(uvfac, 1.0); + tColor.a = tColor.a * -1.0; + float dist = length(mTexCoord - center); + if (dist > 0.50) { + discard; + } + } + /* Solid */ + if (color_type == GPENCIL_COLOR_SOLID) { + fragColor = tColor; + } + /* texture */ + if (color_type == GPENCIL_COLOR_TEXTURE) { + fragColor = texture2D(myTexture, mTexCoord); + /* mult both alpha factor to use strength factor */ + fragColor.a = min(fragColor.a * tColor.a, fragColor.a); + } + /* pattern */ + if (color_type == GPENCIL_COLOR_PATTERN) { + vec4 text_color = texture2D(myTexture, mTexCoord); + fragColor = tColor; + /* mult both alpha factor to use strength factor with color alpha limit */ + fragColor.a = min(text_color.a * tColor.a, tColor.a); + } +} diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl new file mode 100644 index 00000000000..0bcfe8cddb7 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_geom.glsl @@ -0,0 +1,208 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform vec2 Viewport; +uniform int xraymode; +uniform int color_type; + +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 float uvfac; + +#define GP_XRAY_FRONT 0 +#define GP_XRAY_3DSPACE 1 +#define GP_XRAY_BACK 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 + +/* 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 0.0; + } + if (xraymode == GP_XRAY_3DSPACE) { + return (point.z / point.w); + } + if (xraymode == GP_XRAY_BACK) { + return 0.999999; + } + + /* in front by default */ + return 0.0; +} +void main(void) +{ + float MiterLimit = 0.75; + uvfac = 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; + + /* 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 (alpha < 0 used as endcap flag)*/ + if ((P0 == P2) && (color_type == GPENCIL_COLOR_SOLID)){ + mTexCoord = vec2(2, 1); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0; + gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 0); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + gl_Position = vec4((sp1 - (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 2); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + gl_Position = vec4((sp1 + (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + } + + /* generate the triangle strip */ + mTexCoord = (color_type == GPENCIL_COLOR_SOLID) ? vec2(0, 0) : vec2(finaluvdata[1].x, 0); + 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, 1); + 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, 0); + 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, 1); + mColor = finalColor[2]; + gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + /* generate the end endcap (alpha < 0 used as endcap flag)*/ + if ((P1 == P3) && (color_type == GPENCIL_COLOR_SOLID)){ + mTexCoord = vec2(finaluvdata[2].x, 2); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ; + uvfac = finaluvdata[2].x; + gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + mTexCoord = vec2(finaluvdata[2].x, 0); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ; + uvfac = finaluvdata[2].x; + gl_Position = vec4((sp2 - (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + mTexCoord = vec2(finaluvdata[2].x + 2, 1); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ; + uvfac = finaluvdata[2].x; + 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 new file mode 100644 index 00000000000..2f9a105e911 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_stroke_vert.glsl @@ -0,0 +1,37 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ProjectionMatrix; + +uniform float pixsize; /* rv3d->pixsize */ +uniform float pixelsize; /* U.pixelsize */ +uniform int keep_size; +uniform float objscale; +uniform float pixfactor; + +in vec3 pos; +in vec4 color; +in float thickness; +in vec2 uvdata; + +out vec4 finalColor; +out float finalThickness; +out vec2 finaluvdata; + +#define TRUE 1 + +float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor); + +void main(void) +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + 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); + } + + finaluvdata = uvdata; +} 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 new file mode 100644 index 00000000000..0983e6c4d87 --- /dev/null +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_zdepth_mix_frag.glsl @@ -0,0 +1,45 @@ +in vec4 uvcoordsvar; + +out vec4 FragColor; + +uniform sampler2D strokeColor; +uniform sampler2D strokeDepth; +uniform int tonemapping; + +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; +} + +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 = stroke_color; + gl_FragDepth = stroke_depth; +} diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 0db16ab5472..abba8d3ce91 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -136,6 +136,7 @@ typedef char DRWViewportEmptyList; + typedef struct DrawEngineDataSize { int fbl_len; int txl_len; diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index bdfa3211f7c..ac84a847a1b 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -107,6 +107,7 @@ static struct DRWShapeCache { GPUBatch *drw_particle_cross; GPUBatch *drw_particle_circle; GPUBatch *drw_particle_axis; + GPUBatch *drw_gpencil_axes; } SHC = {NULL}; void DRW_shape_cache_free(void) @@ -551,12 +552,67 @@ GPUBatch *DRW_cache_screenspace_circle_get(void) #undef CIRCLE_RESOL } -/** \} */ +/* Grease Pencil object */ +GPUBatch *DRW_cache_gpencil_axes_get(void) +{ + if (!SHC.drw_gpencil_axes) { + int axis; + float v1[3] = { 0.0f, 0.0f, 0.0f }; + float v2[3] = { 0.0f, 0.0f, 0.0f }; + + /* cube data */ + const GLfloat verts[8][3] = { + { -0.25f, -0.25f, -0.25f }, + { -0.25f, -0.25f, 0.25f }, + { -0.25f, 0.25f, -0.25f }, + { -0.25f, 0.25f, 0.25f }, + { 0.25f, -0.25f, -0.25f }, + { 0.25f, -0.25f, 0.25f }, + { 0.25f, 0.25f, -0.25f }, + { 0.25f, 0.25f, 0.25f } + }; + + const GLubyte indices[24] = { 0, 1, 1, 3, 3, 2, 2, 0, 0, 4, 4, 5, 5, 7, 7, 6, 6, 4, 1, 5, 3, 7, 2, 6 }; + + /* Position Only 3D format */ + static GPUVertFormat format = { 0 }; + static uint pos_id; + if (format.attr_len == 0) { + pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + + /* alloc 30 elements for cube and 3 axis */ + GPU_vertbuf_data_alloc(vbo, ARRAY_SIZE(indices) + 6); + + /* draw axis */ + for (axis = 0; axis < 3; axis++) { + v1[axis] = 1.0f; + v2[axis] = -1.0f; + + GPU_vertbuf_attr_set(vbo, pos_id, axis * 2, v1); + GPU_vertbuf_attr_set(vbo, pos_id, axis * 2 + 1, v2); + + /* reset v1 & v2 to zero for next axis */ + v1[axis] = v2[axis] = 0.0f; + } + + /* draw cube */ + for (int i = 0; i < 24; ++i) { + GPU_vertbuf_attr_set(vbo, pos_id, i + 6, verts[indices[i]]); + } + + SHC.drw_gpencil_axes = GPU_batch_create(GPU_PRIM_LINES, vbo, NULL); + } + return SHC.drw_gpencil_axes; +} + /* -------------------------------------------------------------------- */ /** \name Common Object API - * \{ */ +* \{ */ GPUBatch *DRW_cache_object_wire_outline_get(Object *ob) { diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 129c0252f30..7d0996b3059 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -82,6 +82,9 @@ struct GPUBatch *DRW_cache_field_vortex_get(void); struct GPUBatch *DRW_cache_field_tube_limit_get(void); struct GPUBatch *DRW_cache_field_cone_limit_get(void); +/* Grease Pencil */ +struct GPUBatch *DRW_cache_gpencil_axes_get(void); + /* Lamps */ struct GPUBatch *DRW_cache_lamp_get(void); struct GPUBatch *DRW_cache_lamp_shadows_get(void); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index eeb7b1c41ee..d4dbe5db80d 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -41,6 +41,7 @@ struct Curve; struct Lattice; struct Mesh; struct MetaBall; +struct bGPdata; /* Expose via BKE callbacks */ void DRW_mball_batch_cache_dirty(struct MetaBall *mb, int mode); @@ -58,6 +59,9 @@ void DRW_lattice_batch_cache_free(struct Lattice *lt); void DRW_particle_batch_cache_dirty(struct ParticleSystem *psys, int mode); void DRW_particle_batch_cache_free(struct ParticleSystem *psys); +void DRW_gpencil_batch_cache_dirty(struct bGPdata *gpd); +void DRW_gpencil_batch_cache_free(struct bGPdata *gpd); + /* Curve */ struct GPUBatch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache); struct GPUBatch *DRW_curve_batch_cache_get_normal_edge( diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 714edc23719..e6e20934283 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -48,6 +48,7 @@ #include "ED_space_api.h" #include "ED_screen.h" +#include "ED_gpencil.h" #include "ED_particle.h" #include "ED_view3d.h" @@ -1222,10 +1223,17 @@ static void drw_engines_enable_from_mode(int mode) break; case CTX_MODE_OBJECT: break; + case CTX_MODE_GPENCIL_PAINT: + case CTX_MODE_GPENCIL_EDIT: + case CTX_MODE_GPENCIL_SCULPT: + case CTX_MODE_GPENCIL_WEIGHT: + break; default: BLI_assert(!"Draw mode invalid"); break; } + /* grease pencil */ + use_drw_engine(&draw_engine_gpencil_type); } static void drw_engines_enable_from_overlays(int overlay_flag) @@ -1258,6 +1266,10 @@ static void drw_engines_enable(ViewLayer *view_layer, RenderEngineType *engine_t drw_engines_enable_from_object_mode(); drw_engines_enable_from_mode(mode); } + else { + /* if gpencil must draw the strokes, but not the object */ + drw_engines_enable_from_mode(mode); + } } static void drw_engines_disable(void) @@ -1377,6 +1389,7 @@ void DRW_draw_render_loop_ex( Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); RegionView3D *rv3d = ar->regiondata; + bool do_annotations = (((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0) && ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0)); DST.draw_ctx.evil_C = evil_C; DST.viewport = viewport; @@ -1471,6 +1484,17 @@ void DRW_draw_render_loop_ex( drw_engines_draw_scene(); + /* annotations - temporary drawing buffer (3d space) */ + /* XXX: Or should we use a proper draw/overlay engine for this case? */ + if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (do_annotations)) + { + glDisable(GL_DEPTH_TEST); + /* XXX: as scene->gpd is not copied for COW yet */ + ED_gpencil_draw_view3d_annotations(DEG_get_input_scene(depsgraph), depsgraph, v3d, ar, true); + glEnable(GL_DEPTH_TEST); + } + DRW_draw_callbacks_post_scene(); if (DST.draw_ctx.evil_C) { ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.ar, REGION_DRAW_POST_VIEW); @@ -1495,6 +1519,17 @@ void DRW_draw_render_loop_ex( DRW_draw_region_info(); + /* annotations - temporary drawing buffer (screenspace) */ + /* XXX: Or should we use a proper draw/overlay engine for this case? */ + if (((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) && + (do_annotations)) + { + glDisable(GL_DEPTH_TEST); + /* XXX: as scene->gpd is not copied for COW yet */ + ED_gpencil_draw_view3d_annotations(DEG_get_input_scene(depsgraph), depsgraph, v3d, ar, false); + glEnable(GL_DEPTH_TEST); + } + if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) { /* Draw 2D after region info so we can draw on top of the camera passepartout overlay. * 'DRW_draw_region_info' sets the projection in pixel-space. */ @@ -1583,6 +1618,105 @@ void DRW_draw_render_loop_offscreen( GPU_offscreen_bind(ofs, false); } +/* helper to check if exit object type to render */ +static bool DRW_render_check_object_type(struct Depsgraph *depsgraph, short obtype) +{ + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob) + { + if ((ob->type == obtype) && (DRW_check_object_visible_within_active_context(ob))) { + return true; + } + } + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END + + return false; +} + +static void DRW_render_gpencil_to_image(RenderEngine *engine, struct Depsgraph *depsgraph, struct RenderLayer *render_layer, const rcti *rect) +{ + if (draw_engine_gpencil_type.render_to_image) { + if (DRW_render_check_object_type(depsgraph, OB_GPENCIL)) { + ViewportEngineData *gpdata = drw_viewport_engine_data_ensure(&draw_engine_gpencil_type); + draw_engine_gpencil_type.render_to_image(gpdata, engine, render_layer, rect); + } + } +} + +void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph) +{ + /* This function is only valid for Cycles + * Eevee done all work in the Eevee render directly. + * Maybe it can be done equal for both engines? + */ + if (STREQ(engine->type->name, "Eevee")) { + return; + } + + Scene *scene = DEG_get_evaluated_scene(depsgraph); + ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); + RenderEngineType *engine_type = engine->type; + RenderData *r = &scene->r; + Render *render = engine->re; + /* Changing Context */ + /* GPXX Review this context */ + DRW_opengl_context_enable(); + /* Reset before using it. */ + drw_state_prepare_clean_for_draw(&DST); + DST.options.is_image_render = true; + DST.options.is_scene_render = true; + DST.options.draw_background = scene->r.alphamode == R_ADDSKY; + DST.buffer_finish_called = true; + + DST.draw_ctx = (DRWContextState) { + .scene = scene, .view_layer = view_layer, + .engine_type = engine_type, + .depsgraph = depsgraph, .object_mode = OB_MODE_OBJECT, + }; + drw_context_state_init(); + + DST.viewport = GPU_viewport_create(); + const int size[2] = { (r->size * r->xsch) / 100, (r->size * r->ysch) / 100 }; + GPU_viewport_size_set(DST.viewport, size); + + drw_viewport_var_init(); + + /* set default viewport */ + gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT); + glDisable(GL_SCISSOR_TEST); + glViewport(0, 0, size[0], size[1]); + + /* Main rendering. */ + rctf view_rect; + rcti render_rect; + RE_GetViewPlane(render, &view_rect, &render_rect); + if (BLI_rcti_is_empty(&render_rect)) { + BLI_rcti_init(&render_rect, 0, size[0], 0, size[1]); + } + + RenderResult *render_result = RE_engine_get_result(engine); + RenderLayer *render_layer = render_result->layers.first; + + DRW_render_gpencil_to_image(engine, depsgraph, render_layer, &render_rect); + + /* Force cache to reset. */ + drw_viewport_cache_resize(); + GPU_viewport_free(DST.viewport); + DRW_state_reset(); + + glDisable(GL_DEPTH_TEST); + + /* Restore Drawing area. */ + gpuPopAttrib(); + glEnable(GL_SCISSOR_TEST); + GPU_framebuffer_restore(); + + /* Changing Context */ + /* GPXX Review this context */ + DRW_opengl_context_disable(); + + DST.buffer_finish_called = false; +} + void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) { Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -1663,6 +1797,8 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) { RE_SetActiveRenderView(render, render_view->name); engine_type->draw_engine->render_to_image(data, engine, render_layer, &render_rect); + /* grease pencil: render result is merged in the previous render result. */ + DRW_render_gpencil_to_image(engine, depsgraph, render_layer, &render_rect); DST.buffer_finish_called = false; } @@ -1671,8 +1807,6 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) /* Force cache to reset. */ drw_viewport_cache_resize(); - /* TODO grease pencil */ - GPU_viewport_free(DST.viewport); GPU_framebuffer_restore(); @@ -2286,6 +2420,7 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_particle_type); DRW_engine_register(&draw_engine_pose_type); DRW_engine_register(&draw_engine_sculpt_type); + DRW_engine_register(&draw_engine_gpencil_type); /* setup callbacks */ { @@ -2304,6 +2439,9 @@ void DRW_engines_register(void) /* BKE: particle.c */ extern void *BKE_particle_batch_cache_dirty_cb; extern void *BKE_particle_batch_cache_free_cb; + /* BKE: gpencil.c */ + extern void *BKE_gpencil_batch_cache_dirty_cb; + extern void *BKE_gpencil_batch_cache_free_cb; BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty; BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free; @@ -2319,6 +2457,9 @@ void DRW_engines_register(void) BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty; BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free; + + BKE_gpencil_batch_cache_dirty_cb = DRW_gpencil_batch_cache_dirty; + BKE_gpencil_batch_cache_free_cb = DRW_gpencil_batch_cache_free; } } diff --git a/source/blender/draw/modes/draw_mode_engines.h b/source/blender/draw/modes/draw_mode_engines.h index f88d49dfa96..8e8ddfef5a4 100644 --- a/source/blender/draw/modes/draw_mode_engines.h +++ b/source/blender/draw/modes/draw_mode_engines.h @@ -42,5 +42,6 @@ extern DrawEngineType draw_engine_particle_type; extern DrawEngineType draw_engine_pose_type; extern DrawEngineType draw_engine_sculpt_type; extern DrawEngineType draw_engine_overlay_type; +extern DrawEngineType draw_engine_gpencil_type; #endif /* __DRAW_MODE_ENGINES_H__ */ diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index db906714dd5..675a2a02db8 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -165,6 +165,9 @@ typedef struct OBJECT_PrivateData { DRWShadingGroup *field_tube_limit; DRWShadingGroup *field_cone_limit; + /* Grease Pencil */ + DRWShadingGroup *gpencil_axes; + /* Speaker */ DRWShadingGroup *speaker; @@ -1136,6 +1139,10 @@ static void OBJECT_cache_init(void *vedata) geom = DRW_cache_screenspace_circle_get(); stl->g_data->field_curve_sta = shgroup_instance_screen_aligned(psl->non_meshes, geom); + /* Grease Pencil */ + geom = DRW_cache_gpencil_axes_get(); + stl->g_data->gpencil_axes = shgroup_instance(psl->non_meshes, geom); + /* Speaker */ geom = DRW_cache_speaker_get(); stl->g_data->speaker = shgroup_instance(psl->non_meshes, geom); @@ -1820,6 +1827,14 @@ static void volumes_free_smoke_textures(void) BLI_freelistN(&e_data.smoke_domains); } +static void DRW_shgroup_gpencil(OBJECT_StorageList *stl, Object *ob, ViewLayer *view_layer) +{ + float *color; + DRW_object_wire_theme_get(ob, view_layer, &color); + + DRW_shgroup_call_dynamic_add(stl->g_data->gpencil_axes, color, &ob->empty_drawsize, ob->obmat); +} + static void DRW_shgroup_speaker(OBJECT_StorageList *stl, Object *ob, ViewLayer *view_layer) { float *color; @@ -2445,6 +2460,9 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) } DRW_shgroup_empty(stl, psl, ob, view_layer); break; + case OB_GPENCIL: + DRW_shgroup_gpencil(stl, ob, view_layer); + break; case OB_SPEAKER: if (hide_object_extra) { break; diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 3c10cda6456..2431bd50e1b 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -64,6 +64,7 @@ #include "BKE_animsys.h" #include "BKE_curve.h" +#include "BKE_gpencil.h" #include "BKE_key.h" #include "BKE_main.h" #include "BKE_nla.h" @@ -83,6 +84,8 @@ #include "BIF_gl.h" +#include "DEG_depsgraph.h" + #include "WM_api.h" #include "WM_types.h" @@ -661,6 +664,8 @@ static int acf_object_icon(bAnimListElem *ale) return ICON_OUTLINER_OB_SURFACE; case OB_EMPTY: return ICON_OUTLINER_OB_EMPTY; + case OB_GPENCIL: + return ICON_OUTLINER_OB_GREASEPENCIL; default: return ICON_OBJECT_DATA; } @@ -4048,8 +4053,16 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void return; } - if (ale_setting->type == ANIMTYPE_GPLAYER) + if (ale_setting->type == ANIMTYPE_GPLAYER) { + /* draw cache updates for settings that affect the visible strokes */ + if (setting == ACHANNEL_SETTING_VISIBLE) { + bGPdata *gpd = (bGPdata *)ale_setting->id; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + + /* UI updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); + } /* tag copy-on-write flushing (so that the settings will have an effect) */ if (ale_setting->id) { diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index d768be49ad4..3f22ac6fa3a 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1727,8 +1727,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) bGPDlayer *gpl = (bGPDlayer *)ale->data; /* try to delete the layer's data and the layer itself */ - BKE_gpencil_free_frames(gpl); - BLI_freelinkN(&gpd->layers, gpl); + BKE_gpencil_layer_delete(gpd, gpl); break; } case ANIMTYPE_MASKLAYER: diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index adb5a10c19d..898c8b6464a 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -435,6 +435,16 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) ANIM_list_elem_update(ac->bmain, ac->scene, ale); } } + else if (ale->update) { +#if 0 + if (G.debug & G_DEBUG) { + printf("%s: Unhandled animchannel updates (%d) for type=%d (%p)\n", + __func__, ale->update, ale->type, ale->data); + } +#endif + /* Prevent crashes in cases where it can't be handled */ + ale->update = 0; + } BLI_assert(ale->update == 0); } diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 780e984f870..05ea3fd6314 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -552,11 +552,11 @@ static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prev /* populate tree with keyframe nodes */ scene_to_keylist(&ads, scene, &keys, NULL); - gpencil_to_keylist(&ads, scene->gpd, &keys); + gpencil_to_keylist(&ads, scene->gpd, &keys, false); if (ob) { ob_to_keylist(&ads, ob, &keys, NULL); - gpencil_to_keylist(&ads, ob->gpd, &keys); + gpencil_to_keylist(&ads, ob->data, &keys, false); } if (mask) { diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index c59d24bbdf8..1981814eb02 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -72,6 +72,7 @@ #include "DNA_speaker_types.h" #include "DNA_world_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "DNA_layer_types.h" @@ -1660,7 +1661,7 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, bDopeSheet *ads, */ if (filter_mode & ANIMFILTER_ANIMDATA) { /* just add GPD as a channel - this will add everything needed */ - ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL); + ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, gpd); } else { ListBase tmp_data = {NULL, NULL}; @@ -1711,7 +1712,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, voi /* Objects in the scene */ for (base = view_layer->object_bases.first; base; base = base->next) { /* Only consider this object if it has got some GP data (saving on all the other tests) */ - if (base->object && base->object->gpd) { + if (base->object && (base->object->type == OB_GPENCIL)) { Object *ob = base->object; /* firstly, check if object can be included, by the following factors: @@ -1748,7 +1749,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, voi /* finally, include this object's grease pencil datablock */ /* XXX: Should we store these under expanders per item? */ - items += animdata_filter_gpencil_data(anim_data, ads, ob->gpd, filter_mode); + items += animdata_filter_gpencil_data(anim_data, ads, ob->data, filter_mode); } } } @@ -2613,8 +2614,10 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data } /* grease pencil */ - if ((ob->gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) { - tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->gpd, filter_mode); + if ((ob->type == OB_GPENCIL) && + (ob->data) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) + { + tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->data, filter_mode); } } END_ANIMFILTER_SUBCHANNELS; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 30130ce4dac..a8b63e01ac1 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -48,6 +48,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_mask_types.h" #include "BKE_fcurve.h" @@ -783,7 +784,7 @@ void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos BLI_dlrbTree_init(&keys); - gpencil_to_keylist(ads, gpd, &keys); + gpencil_to_keylist(ads, gpd, &keys, false); BLI_dlrbTree_linkedlist_sync(&keys); @@ -1019,7 +1020,7 @@ void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, DLRBT_Tree } -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys) +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) { bGPDlayer *gpl; @@ -1027,7 +1028,9 @@ void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys) /* for now, just aggregate out all the frames, but only for visible layers */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { - gpl_to_keylist(ads, gpl, keys); + if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { + gpl_to_keylist(ads, gpl, keys); + } } } } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 0e09ef6f583..0698282c4e5 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -536,6 +536,16 @@ set(ICON_NAMES # This section is maintained by the updating script, keep BEGIN/END comments. set_property(GLOBAL PROPERTY ICON_GEOM_NAMES # BEGIN ICON_GEOM_NAMES + brush.gpencil.draw.eraser_hard + brush.gpencil.draw.eraser_soft + brush.gpencil.draw.eraser_stroke + brush.gpencil.draw_block + brush.gpencil.draw_fill + brush.gpencil.draw_ink + brush.gpencil.draw_marker + brush.gpencil.draw_noise + brush.gpencil.draw_pen + brush.gpencil.draw_pencil brush.paint_texture.airbrush brush.paint_texture.clone brush.paint_texture.draw @@ -583,6 +593,24 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.generic.select_border ops.generic.select_circle ops.generic.select_lasso + ops.gpencil.draw + ops.gpencil.draw.eraser + ops.gpencil.draw.line + ops.gpencil.draw.poly + ops.gpencil.edit_bend + ops.gpencil.edit_mirror + ops.gpencil.edit_shear + ops.gpencil.edit_to_sphere + ops.gpencil.sculpt_clone + ops.gpencil.sculpt_grab + ops.gpencil.sculpt_pinch + ops.gpencil.sculpt_push + ops.gpencil.sculpt_randomize + ops.gpencil.sculpt_smooth + ops.gpencil.sculpt_strength + ops.gpencil.sculpt_thickness + ops.gpencil.sculpt_twist + ops.gpencil.sculpt_weight ops.mesh.bevel ops.mesh.bisect ops.mesh.dupli_extrude_cursor @@ -634,6 +662,7 @@ if(WITH_BLENDER) # blends data_to_c_simple(../../../../release/datafiles/preview.blend SRC) data_to_c_simple(../../../../release/datafiles/preview_cycles.blend SRC) + data_to_c_simple(../../../../release/datafiles/preview_grease_pencil.blend SRC) # images data_to_c_simple(../../../../release/datafiles/splash.png SRC) @@ -689,6 +718,29 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/twist.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/vertexdraw.png SRC) + # grease pencil sculpt + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_smooth.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_thickness.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_strength.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_grab.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_push.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_twist.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pinch.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_randomize.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_clone.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_weight.png SRC) + + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pencil.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pen.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_ink.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_inknoise.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_block.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_marker.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_fill.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_soft.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_hard.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_stroke.png SRC) + endif() data_to_c_simple(../../../../release/datafiles/startup.blend SRC) diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 587c25031ab..f9f196f6634 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -39,18 +39,24 @@ set(INC_SYS ) set(SRC + annotate_draw.c + annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_monkey.c gpencil_brush.c gpencil_convert.c gpencil_data.c gpencil_edit.c gpencil_interpolate.c + gpencil_primitive.c gpencil_ops.c gpencil_paint.c + gpencil_fill.c gpencil_select.c gpencil_undo.c gpencil_utils.c + gpencil_old.c gpencil_intern.h ) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c new file mode 100644 index 00000000000..dad5af7379c --- /dev/null +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -0,0 +1,1065 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung, Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/annotate_draw.c + * \ingroup edgpencil + */ + + +#include +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_polyfill_2d.h" + +#include "BLF_api.h" +#include "BLT_translation.h" + +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_userdef_types.h" +#include "DNA_object_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" + +#include "WM_api.h" + +#include "BIF_glutil.h" + +#include "GPU_immediate.h" +#include "GPU_draw.h" +#include "GPU_state.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_space_api.h" + +#include "UI_interface_icons.h" +#include "UI_resources.h" + +/* ************************************************** */ +/* GREASE PENCIL DRAWING */ + +/* ----- General Defines ------ */ +/* flags for sflag */ +typedef enum eDrawStrokeFlags { + GP_DRAWDATA_NOSTATUS = (1 << 0), /* don't draw status info */ + GP_DRAWDATA_ONLY3D = (1 << 1), /* only draw 3d-strokes */ + GP_DRAWDATA_ONLYV2D = (1 << 2), /* only draw 'canvas' strokes */ + GP_DRAWDATA_ONLYI2D = (1 << 3), /* only draw 'image' strokes */ + GP_DRAWDATA_IEDITHACK = (1 << 4), /* special hack for drawing strokes in Image Editor (weird coordinates) */ + GP_DRAWDATA_NO_XRAY = (1 << 5), /* don't draw xray in 3D view (which is default) */ + GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ +} eDrawStrokeFlags; + + +/* ----- Tool Buffer Drawing ------ */ + +/* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ +static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short thickness, + short dflag, short sflag, float ink[4]) +{ + int draw_points = 0; + + /* error checking */ + if ((points == NULL) || (totpoints <= 0)) + return; + + /* check if buffer can be drawn */ + if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D)) + return; + + if (sflag & GP_STROKE_ERASER) { + /* don't draw stroke at all! */ + return; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + + const tGPspoint *pt = points; + + if (totpoints == 1) { + /* if drawing a single point, draw it larger */ + GPU_point_size((float)(thickness + 2) * points->pressure); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2iv(pos, &pt->x); + } + else { + float oldpressure = points[0].pressure; + + /* draw stroke curve */ + GPU_line_width(max_ff(oldpressure * thickness, 1.0)); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints); + + for (int i = 0; i < totpoints; i++, pt++) { + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, + * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) + */ + if (fabsf(pt->pressure - oldpressure) > 0.2f) { + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2iv(pos, &(pt - 1)->x); + } + + immEnd(); + draw_points = 0; + + GPU_line_width(max_ff(pt->pressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + immVertex2iv(pos, &(pt - 1)->x); + draw_points++; + } + + oldpressure = pt->pressure; /* reset our threshold */ + } + + /* now the point we want */ + immVertex2iv(pos, &pt->x); + draw_points++; + } + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2iv(pos, &(pt - 1)->x); + } + } + + immEnd(); + immUnbindProgram(); +} + +/* --------- 2D Stroke Drawing Helpers --------- */ +/* change in parameter list */ +static void gp_calc_2d_stroke_fxy(const float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +{ + if (sflag & GP_STROKE_2DSPACE) { + r_co[0] = pt[0]; + r_co[1] = pt[1]; + } + else if (sflag & GP_STROKE_2DIMAGE) { + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + + r_co[0] = x; + r_co[1] = y; + } + else { + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + + r_co[0] = x; + r_co[1] = y; + } +} + +/* ----- Existing Strokes Drawing (3D and Point) ------ */ + +/* draw a given stroke - just a single dot (only one point) */ +static void gp_draw_stroke_point( + const bGPDspoint *points, short thickness, short UNUSED(dflag), short sflag, + int offsx, int offsy, int winx, int winy, const float ink[4]) +{ + const bGPDspoint *pt = points; + + /* get final position using parent matrix */ + float fpt[3]; + copy_v3_v3(fpt, &pt->x); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (sflag & GP_STROKE_3DSPACE) { + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + } + else { + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + + /* get 2D coordinates of point */ + float co[3] = { 0.0f }; + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); + copy_v3_v3(fpt, co); + } + + /* set color */ + immUniformColor3fvAlpha(ink, ink[3]); + + /* set point thickness (since there's only one of these) */ + immUniform1f("size", (float)(thickness + 2) * pt->pressure); + + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, fpt); + immEnd(); + + immUnbindProgram(); +} + +/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ +static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thickness, bool UNUSED(debug), + short UNUSED(sflag), const float ink[4], bool cyclic) +{ + float curpressure = points[0].pressure; + float cyclic_fpt[3]; + int draw_points = 0; + + /* if cyclic needs one vertex more */ + int cyclic_add = 0; + if (cyclic) { + cyclic_add++; + } + + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + + /* draw stroke curve */ + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + for (int i = 0; i < totpoints; i++, pt++) { + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, + * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) + * Note: we want more visible levels of pressures when thickness is bigger. + */ + if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { + /* if the pressure changes before get at least 2 vertices, need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + immEnd(); + draw_points = 0; + + curpressure = pt->pressure; + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + draw_points++; + } + } + + /* now the point we want */ + immVertex3fv(pos, &pt->x); + draw_points++; + + if (cyclic && i == 0) { + /* save first point to use in cyclic */ + copy_v3_v3(cyclic_fpt, &pt->x); + } + } + + if (cyclic) { + /* draw line to first point to complete the cycle */ + immVertex3fv(pos, cyclic_fpt); + draw_points++; + } + + /* if less of two points, need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + + immEnd(); + immUnbindProgram(); +} + +/* ----- Fancy 2D-Stroke Drawing ------ */ + +/* draw a given stroke in 2d */ +static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, + bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float ink[4]) +{ + /* otherwise thickness is twice that of the 3D view */ + float thickness = (float)thickness_s * 0.5f; + + /* strokes in Image Editor need a scale factor, since units there are not pixels! */ + float scalefac = 1.0f; + if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { + scalefac = 0.001f; + } + + /* TODO: fancy++ with the magic of shaders */ + + /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection + * edges rotated to minimize shrinking artifacts, and rounded endcaps + */ + { + const bGPDspoint *pt1, *pt2; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ + int i; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); + + /* get x and y coordinates from first point */ + gp_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0); + + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { + float t0[2], t1[2]; /* tessellated coordinates */ + float m1[2], m2[2]; /* gradient and normal */ + float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ + float pthick; /* thickness at segment point */ + + /* get x and y coordinates from point2 (point1 has already been computed in previous iteration). */ + gp_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1); + + /* calculate gradient and normal - 'angle'=(ny/nx) */ + m1[1] = s1[1] - s0[1]; + m1[0] = s1[0] - s0[0]; + normalize_v2(m1); + m2[1] = -m1[0]; + m2[0] = m1[1]; + + /* always use pressure from first point here */ + pthick = (pt1->pressure * thickness * scalefac); + + /* if the first segment, start of segment is segment's normal */ + if (i == 0) { + /* draw start cap first + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s0[0] - (m1[0] * pthick * 0.75f); + sc[1] = s0[1] - (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* First two points of cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* calculate points for start of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of start cap (and first two points of first segment). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + /* if not the first segment, use bisector of angle between segments */ + else { + float mb[2]; /* bisector normal */ + float athick, dfac; /* actual thickness, difference between thicknesses */ + + /* calculate gradient of bisector (as average of normals) */ + mb[0] = (pm[0] + m2[0]) / 2; + mb[1] = (pm[1] + m2[1]) / 2; + normalize_v2(mb); + + /* calculate gradient to apply + * - as basis, use just pthick * bisector gradient + * - if cross-section not as thick as it should be, add extra padding to fix it + */ + mt[0] = mb[0] * pthick; + mt[1] = mb[1] * pthick; + athick = len_v2(mt); + dfac = pthick - (athick * 2); + + if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { + mt[0] += (mb[0] * dfac); + mt[1] += (mb[1] * dfac); + } + + /* calculate points for start of segment */ + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of previous segment, and first two points of current segment. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* if last segment, also draw end of segment (defined as segment's normal) */ + if (i == totpoints - 2) { + /* for once, we use second point's pressure (otherwise it won't be drawn) */ + pthick = (pt2->pressure * thickness * scalefac); + + /* calculate points for end of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s1[0] - mt[0]; + t0[1] = s1[1] - mt[1]; + t1[0] = s1[0] + mt[0]; + t1[1] = s1[1] + mt[1]; + + /* Last two points of last segment (and first two points of end cap). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* draw end cap as last step + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s1[0] + (m1[0] * pthick * 0.75f); + sc[1] = s1[1] + (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* Last two points of end cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); + /* store stroke's 'natural' normal for next stroke to use */ + copy_v2_v2(pm, m2); + } + + immEnd(); + immUnbindProgram(); + } +} + +/* ----- Strokes Drawing ------ */ + +/* Helper for doing all the checks on whether a stroke can be drawn */ +static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) +{ + /* skip stroke if it isn't in the right display space for this drawing context */ + /* 1) 3D Strokes */ + if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) + return false; + + /* 2) Screen Space 2D Strokes */ + if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) + return false; + + /* 3) Image Space (2D) */ + if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) + return false; + + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1)) + return false; + + /* stroke can be drawn */ + return true; +} + +/* draw a set of strokes */ +static void gp_draw_strokes( + bGPdata *UNUSED(gpd), bGPDlayer *UNUSED(gpl), const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, + int dflag, bool debug, short lthick, const float color[4]) +{ + GPU_enable_program_point_size(); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) { + continue; + } + + /* check which stroke-drawer to use */ + if (dflag & GP_DRAWDATA_ONLY3D) { + const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); + int mask_orig = 0; + + if (no_xray) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + + /* 3D Lines - OpenGL primitives-based */ + if (gps->totpoints == 1) { + gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag, + color, gps->flag & GP_STROKE_CYCLIC); + } + + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); + } + } + else { + /* 2D Strokes... */ + if (gps->totpoints == 1) { + gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, + offsx, offsy, winx, winy, color); + } + } + } + + GPU_disable_program_point_size(); +} + +/* Draw selected verts for strokes being edited */ +static void gp_draw_strokes_edit( + bGPdata *gpd, bGPDlayer *gpl, const bGPDframe *gpf, + int offsx, int offsy, int winx, int winy, + short dflag, short UNUSED(lflag), float alpha) +{ + /* if alpha 0 do not draw */ + if (alpha == 0.0f) + return; + + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; + int mask_orig = 0; + + /* set up depth masks... */ + if (dflag & GP_DRAWDATA_ONLY3D) { + if (no_xray) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + } + + GPU_enable_program_point_size(); + + /* draw stroke verts */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) + continue; + + /* Optimisation: only draw points for selected strokes + * We assume that selected points can only occur in + * strokes that are selected too. + */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + /* 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; + } + + float selectColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); + selectColor[3] = alpha; + + GPUVertFormat *format = immVertexFormat(); + uint pos; /* specified later */ + uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (gps->flag & GP_STROKE_3DSPACE) { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); + } + else { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); + } + + 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 */ + immAttrib3f(color, 0.0f, 1.0f, 0.0f); + immAttrib1f(size, vsize + 4); + } + else if (show_direction_hint && (i == gps->totpoints - 1)) { + /* end point in red smaller */ + immAttrib3f(color, 1.0f, 0.0f, 0.0f); + immAttrib1f(size, vsize + 1); + } + else if (pt->flag & GP_SPOINT_SELECT) { + immAttrib3fv(color, selectColor); + immAttrib1f(size, vsize); + } + else { + immAttrib3fv(color, gpl->color); + immAttrib1f(size, bsize); + } + + /* then position */ + if (gps->flag & GP_STROKE_3DSPACE) { + immVertex3fv(pos, &pt->x); + } + else { + float co[2]; + gp_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + } + } + + immEnd(); + immUnbindProgram(); + } + + GPU_disable_program_point_size(); + + /* clear depth mask */ + if (dflag & GP_DRAWDATA_ONLY3D) { + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); +#if 0 + glDisable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(0, 0); +#endif + } + } +} + +/* ----- General Drawing ------ */ + +/* loop over gpencil data layers, drawing them */ +static void gp_draw_data_layers( + bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, float alpha) +{ + float ink[4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG); + short lthick = gpl->thickness; + + /* apply layer opacity */ + copy_v3_v3(ink, gpl->color); + ink[3] = gpl->opacity; + + /* don't draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0); + if (gpf == NULL) + continue; + + /* set basic stroke thickness */ + GPU_line_width(lthick); + + /* Add layer drawing settings to the set of "draw flags" + * NOTE: If the setting doesn't apply, it *must* be cleared, + * as dflag's carry over from the previous layer + */ +#define GP_DRAWFLAG_APPLY(condition, draw_flag_value) { \ + if (condition) dflag |= (draw_flag_value); \ + else dflag &= ~(draw_flag_value); \ + } (void)0 + + /* xray... */ + GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_NO_XRAY), GP_DRAWDATA_NO_XRAY); + +#undef GP_DRAWFLAG_APPLY + + + /* draw the strokes already in active frame */ + gp_draw_strokes(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, debug, lthick, ink); + + /* Draw verts of selected strokes + * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering + * - locked layers can't be edited, so there's no point showing these verts + * as they will have no bearings on what gets edited + * - only show when in editmode, since operators shouldn't work otherwise + * (NOTE: doing it this way means that the toggling editmode shows visible change immediately) + */ + /* XXX: perhaps we don't want to show these when users are drawing... */ + if ((G.f & G_RENDER_OGL) == 0 && + (gpl->flag & GP_LAYER_LOCKED) == 0 && + (gpd->flag & GP_DATA_STROKE_EDITMODE)) + { + gp_draw_strokes_edit(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, alpha); + } + + /* 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 (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) && + (gpf->flag & GP_FRAME_PAINT)) + { + /* Buffer stroke needs to be drawn with a different linestyle + * to help differentiate them from normal strokes. + * + * It should also be noted that sbuffer contains temporary point types + * i.e. tGPspoints NOT bGPDspoints + */ + gp_draw_stroke_buffer(gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.sbuffer_sflag, ink); + } + } +} + +/* draw a short status message in the top-right corner */ +static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) +{ + rcti rect; + + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_RENDER_OGL) + return; + + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); + + /* 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); + } +} + +/* draw grease-pencil datablock */ +static void gp_draw_data( + bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, float alpha) +{ + /* turn on smooth lines (i.e. anti-aliasing) */ + GPU_line_smooth(true); + + /* turn on alpha-blending */ + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + + /* draw! */ + gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + + /* turn off alpha blending, then smooth lines */ + GPU_blend(false); // alpha blending + GPU_line_smooth(false); // smooth lines +} + +/* if we have strokes for scenes (3d view)/clips (movie clip editor) + * and objects/tracks, multiple data blocks have to be drawn */ +static void gp_draw_data_all( + Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, const char spacetype) +{ + bGPdata *gpd_source = NULL; + float alpha = 1.0f; + + if (scene) { + if (spacetype == SPACE_VIEW3D) { + gpd_source = (scene->gpd ? scene->gpd : NULL); + } + else if (spacetype == SPACE_CLIP && scene->clip) { + /* currently drawing only gpencil data from either clip or track, but not both - XXX fix logic behind */ + gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); + } + + if (gpd_source) { + gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); + } + } + + /* scene/clip data has already been drawn, only object/track data is drawn here + * if gpd_source == gpd, we don't have any object/track data and we can skip */ + if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { + gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + } +} + +/* ----- Grease Pencil Sketches Drawing API ------ */ + +/* ............................ + * XXX + * We need to review the calls below, since they may be/are not that suitable for + * the new ways that we intend to be drawing data... + * ............................ */ + +/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ +void ED_gpencil_draw_2dimage(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + + int offsx, offsy, sizex, sizey; + int dflag = GP_DRAWDATA_NOSTATUS; + + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) return; + + /* calculate rect */ + switch (sa->spacetype) { + case SPACE_IMAGE: /* image */ + case SPACE_CLIP: /* clip */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); + + dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; + break; + } + case SPACE_SEQ: /* sequence */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated + * and everything moved to standard View2d + */ + dflag |= GP_DRAWDATA_ONLYV2D; + break; + } + default: /* for spacetype not yet handled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + dflag |= GP_DRAWDATA_ONLYI2D; + break; + } + + if (ED_screen_animation_playing(wm)) { + /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) + * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) + */ + dflag |= GP_DRAWDATA_NO_ONIONS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); +} + +/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with onlyv2d=true to draw 'canvas' strokes, + * second time with onlyv2d=false for screen-aligned strokes */ +void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + int dflag = 0; + + /* check that we have grease-pencil stuff to draw */ + if (sa == NULL) return; + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) return; + + /* special hack for Image Editor */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) + dflag |= GP_DRAWDATA_IEDITHACK; + + /* draw it! */ + if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); + if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS; + + gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (!onlyv2d) { + gp_draw_status_text(gpd, ar); + } +} + + +/* draw annotations sketches to specified 3d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, + * second time with only3d=false for screen-aligned strokes */ +void ED_gpencil_draw_view3d_annotations( + Scene *scene, struct Depsgraph *depsgraph, + View3D *v3d, ARegion *ar, + bool only3d) +{ + int dflag = 0; + RegionView3D *rv3d = ar->regiondata; + int offsx, offsy, winx, winy; + + /* check that we have grease-pencil stuff to draw */ + /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */ + bGPdata *gpd = scene->gpd; + if (gpd == NULL) return; + + /* when rendering to the offscreen buffer we don't want to + * deal with the camera border, otherwise map the coords to the camera border. */ + if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) { + rctf rectf; + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ + + offsx = round_fl_to_int(rectf.xmin); + offsy = round_fl_to_int(rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); + } + else { + offsx = 0; + offsy = 0; + winx = ar->winx; + winy = ar->winy; + } + + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); +} + +#if 0 // XXX: Reinstate, after renaming the functions + +void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) +{ + int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; + + gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); +} + +#endif + +/* ************************************************** */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c new file mode 100644 index 00000000000..6325052fccd --- /dev/null +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -0,0 +1,2382 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008/2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/annotate_paint.c + * \ingroup edgpencil + */ + + +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_math_geom.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_screen.h" +#include "BKE_tracking.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_clip.h" + +#include "BIF_glutil.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_state.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "DEG_depsgraph.h" + +#include "gpencil_intern.h" + +/* ******************************************* */ +/* 'Globals' and Defines */ + +/* values for tGPsdata->status */ +typedef enum eGPencil_PaintStatus { + GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ + GP_STATUS_PAINTING, /* a stroke is in progress */ + GP_STATUS_ERROR, /* something wasn't correctly set up */ + GP_STATUS_DONE /* painting done */ +} eGPencil_PaintStatus; + +/* Return flags for adding points to stroke buffer */ +typedef enum eGP_StrokeAdd_Result { + GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ + GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ + GP_STROKEADD_NORMAL, /* point was successfully added */ + GP_STROKEADD_FULL /* cannot add any more points to buffer */ +} eGP_StrokeAdd_Result; + +/* Runtime flags */ +typedef enum eGPencil_PaintFlags { + GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ + GP_PAINTFLAG_STROKEADDED = (1 << 1), + GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), + GP_PAINTFLAG_SELECTMASK = (1 << 3), +} eGPencil_PaintFlags; + + +/* Temporary 'Stroke' Operation data + * "p" = op->customdata + */ +typedef struct tGPsdata { + Main *bmain; + Scene *scene; /* current scene from context */ + struct Depsgraph *depsgraph; + + wmWindow *win; /* window where painting originated */ + ScrArea *sa; /* area where painting originated */ + ARegion *ar; /* region where painting originated */ + View2D *v2d; /* needed for GP_STROKE_2DSPACE */ + rctf *subrect; /* for using the camera rect within the 3d view */ + rctf subrect_data; + + GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ + + PointerRNA ownerPtr; /* pointer to owner of gp-datablock */ + bGPdata *gpd; /* gp-datablock layer comes from */ + bGPDlayer *gpl; /* layer we're working on */ + bGPDframe *gpf; /* frame we're working on */ + + char *align_flag; /* projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ + + eGPencil_PaintStatus status; /* current status of painting */ + eGPencil_PaintModes paintmode; /* mode for painting */ + eGPencil_PaintFlags flags; /* flags that can get set during runtime (eGPencil_PaintFlags) */ + + short radius; /* radius of influence for eraser */ + + int mval[2]; /* current mouse-position */ + int mvalo[2]; /* previous recorded mouse-position */ + + float pressure; /* current stylus pressure */ + float opressure; /* previous stylus pressure */ + + /* These need to be doubles, as (at least under unix) they are in seconds since epoch, + * float (and its 7 digits precision) is definitively not enough here! + * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. + */ + double inittime; /* Used when converting to path */ + double curtime; /* Used when converting to path */ + double ocurtime; /* Used when converting to path */ + + float imat[4][4]; /* inverted transformation matrix applying when converting coords from screen-space + * to region space */ + float mat[4][4]; + + float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ + + void *erasercursor; /* radial cursor data for drawing eraser */ + + short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ + int lock_axis; /* lock drawing to one axis */ + + short keymodifier; /* key used for invoking the operator */ +} tGPsdata; + +/* ------ */ + +/* Macros for accessing sensitivity thresholds... */ +/* minimum number of pixels mouse should move before new point created */ +#define MIN_MANHATTEN_PX (U.gp_manhattendist) +/* minimum length of new segment before new point can be added */ +#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) + +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); + p->flags |= GP_PAINTFLAG_STROKEADDED; +} + +/* ------ */ +/* Forward defines for some functions... */ + +static void gp_session_validatebuffer(tGPsdata *p); + +/* ******************************************* */ +/* Context Wrangling... */ + +/* check if context is suitable for drawing */ +static bool gpencil_draw_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) { + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() == 0) + return 1; + else + CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active"); + } + 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; +} + +/* check if projecting strokes into 3d-geometry in the 3D-View */ +static bool gpencil_project_check(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); +} + +/* ******************************************* */ +/* Calculations/Conversions */ + +/* Utilities --------------------------------- */ + +/* get the reference point for stroke-point conversions */ +static void gp_get_3d_reference(tGPsdata *p, float vec[3]) +{ + View3D *v3d = p->sa->spacedata.first; + const float *fp = ED_view3d_cursor3d_get(p->scene, v3d)->location; + + /* use 3D-cursor */ + copy_v3_v3(vec, fp); +} + +/* Stroke Editing ---------------------------- */ + +/* check if the current mouse position is suitable for adding a new point */ +static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) +{ + int dx = abs(mval[0] - pmval[0]); + int dy = abs(mval[1] - pmval[1]); + + /* if buffer is empty, just let this go through (i.e. so that dots will work) */ + if (p->gpd->runtime.sbuffer_size == 0) + return true; + + /* check if mouse moved at least certain distance on both axes (best case) + * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand + */ + else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) + return true; + + /* check if the distance since the last point is significant enough + * - prevents points being added too densely + * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though + */ + else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) + return true; + + /* mouse 'didn't move' */ + else + return false; +} + +/* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ +static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) +{ + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + plane_normal[axis] = 1.0f; + + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } + } +} + +/* reproject stroke to plane locked to axis in 3d cursor location */ +static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) +{ + bGPdata *gpd = p->gpd; + float origin[3]; + float cursor[3]; + RegionView3D *rv3d = p->ar->regiondata; + + /* verify the stroke mode is CURSOR 3d space mode */ + if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { + return; + } + + /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ + gp_get_3d_reference(p, cursor); + zero_v3(origin); + origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; + + gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); +} + +/* convert screen-coordinates to buffer-coordinates */ +/* XXX this method needs a total overhaul! */ +static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3], float *depth) +{ + bGPdata *gpd = p->gpd; + + /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { + if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ + } + else { + float mval_prj[2]; + float rvec[3], dvec[3]; + float mval_f[2] = {UNPACK2(mval)}; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. In general, this + * works OK, but it could of course be improved. + * + * TODO: + * - investigate using nearest point(s) on a previous stroke as + * reference point instead or as offset, for easier stroke matching + */ + + gp_get_3d_reference(p, rvec); + zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(p->ar, 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(p->ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } + } + } + + /* 2d - on 'canvas' (assume that p->v2d is set) */ + else if ((gpd->runtime.sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { + UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); + mul_v3_m4v3(out, p->imat, out); + } + + /* 2d - relative to screen (viewport area) */ + else { + if (p->subrect == NULL) { /* normal 3D view */ + out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; + out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; + } + else { /* camera view, use subrect */ + out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; + out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; + } + } +} + +/* add current stroke-point to buffer (returns whether point was successfully added) */ +static short gp_stroke_addpoint( + tGPsdata *p, const int mval[2], float pressure, double curtime) +{ + bGPdata *gpd = p->gpd; + tGPspoint *pt; + ToolSettings *ts = p->scene->toolsettings; + + /* check painting mode */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only - i.e. only store start and end point in buffer */ + if (gpd->runtime.sbuffer_size == 0) { + /* first point in buffer (start point) */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2_int(&pt->x, mval); + pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* increment buffer size */ + gpd->runtime.sbuffer_size++; + } + else { + /* just reset the endpoint to the latest value + * - assume that pointers for this are always valid... + */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); + + /* store settings */ + copy_v2_v2_int(&pt->x, mval); + pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; + pt->time = (float)(curtime - p->inittime); + + /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ + gpd->runtime.sbuffer_size = 2; + } + + /* can keep carrying on this way :) */ + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ + /* check if still room in buffer */ + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_OVERFLOW; + + /* get pointer to destination point */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); + + /* store settings */ + copy_v2_v2_int(&pt->x, mval); + pt->pressure = pressure; + pt->strength = 1.0f; /* unused for annotations, but initialise for easier conversions to GP Object */ + + /* point time */ + pt->time = (float)(curtime - p->inittime); + + /* increment counters */ + gpd->runtime.sbuffer_size++; + + /* check if another operation can still occur */ + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_FULL; + else + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* get pointer to destination point */ + pt = (tGPspoint *)(gpd->runtime.sbuffer); + + /* store settings */ + copy_v2_v2_int(&pt->x, mval); + pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + 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; + + /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ + if (gpd->runtime.sbuffer_size == 0) { + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + } + + pts = &gps->points[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 *v3d = p->sa->spacedata.first; + + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->annotate_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); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + + /* copy pressure and time */ + pts->pressure = pt->pressure; + pts->strength = pt->strength; + pts->time = pt->time; + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; + } + + /* increment counters */ + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + return GP_STROKEADD_NORMAL; + } + + /* return invalid state for now... */ + return GP_STROKEADD_INVALID; +} + +/* simplify a stroke (in buffer) before storing it + * - applies a reverse Chaikin filter + * - code adapted from etch-a-ton branch + */ +static void gp_stroke_simplify(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; + short num_points = gpd->runtime.sbuffer_size; + short flag = gpd->runtime.sbuffer_sflag; + short i, j; + + /* only simplify if simplification is enabled, and we're not doing a straight line */ + if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)) + return; + + /* don't simplify if less than 4 points in buffer */ + if ((num_points <= 4) || (old_points == NULL)) + return; + + /* clear buffer (but don't free mem yet) so that we can write to it + * - firstly set sbuffer to NULL, so a new one is allocated + * - secondly, reset flag after, as it gets cleared auto + */ + gpd->runtime.sbuffer = NULL; + gp_session_validatebuffer(p); + gpd->runtime.sbuffer_sflag = flag; + +/* macro used in loop to get position of new point + * - used due to the mixture of datatypes in use here + */ +#define GP_SIMPLIFY_AVPOINT(offs, sfac) \ + { \ + co[0] += (float)(old_points[offs].x * sfac); \ + co[1] += (float)(old_points[offs].y * sfac); \ + pressure += old_points[offs].pressure * sfac; \ + time += old_points[offs].time * sfac; \ + } (void)0 + + /* XXX Here too, do not lose start and end points! */ + gp_stroke_addpoint(p, &old_points->x, old_points->pressure, p->inittime + (double)old_points->time); + for (i = 0, j = 0; i < num_points; i++) { + if (i - j == 3) { + float co[2], pressure, time; + int mco[2]; + + /* initialize values */ + co[0] = 0.0f; + co[1] = 0.0f; + pressure = 0.0f; + time = 0.0f; + + /* using macro, calculate new point */ + GP_SIMPLIFY_AVPOINT(j, -0.25f); + GP_SIMPLIFY_AVPOINT(j + 1, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 2, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 3, -0.25f); + + /* set values for adding */ + mco[0] = (int)co[0]; + mco[1] = (int)co[1]; + + /* ignore return values on this... assume to be ok for now */ + gp_stroke_addpoint(p, mco, pressure, p->inittime + (double)time); + + j += 2; + } + } + gp_stroke_addpoint(p, &old_points[num_points - 1].x, old_points[num_points - 1].pressure, + p->inittime + (double)old_points[num_points - 1].time); + + /* free old buffer */ + MEM_freeN(old_points); +} + +/* make a new stroke from the buffer data */ +static void gp_stroke_newfrombuffer(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + bGPDlayer *gpl = p->gpl; + bGPDstroke *gps; + bGPDspoint *pt; + tGPspoint *ptc; + ToolSettings *ts = p->scene->toolsettings; + + int i, totelem; + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; + + /* get total number of points to allocate space for + * - drawing straight-lines only requires the endpoints + */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; + else + totelem = gpd->runtime.sbuffer_size; + + /* exit with error if no valid points from this stroke */ + if (totelem == 0) { + if (G.debug & G_DEBUG) + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); + 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) { + 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 = gpl->thickness; + 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_CACHES; + + /* allocate enough memory for a continuous array for storage points */ + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->tot_triangles = 0; + + /* set pointer to first non-initialized point */ + pt = gps->points + (gps->totpoints - totelem); + + /* copy points from the buffer to the stroke */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only -> only endpoints */ + { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, 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; + + pt++; + } + + if (totelem == 2) { + /* last point if applicable */ + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, 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; + } + } + 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); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + pt->time = ptc->time; + } + else { + float *depth_arr = NULL; + + /* get an array of depths, far depths are blended */ + if (gpencil_project_check(p)) { + int mval[2], mval_prev[2] = { 0 }; + int interp_depth = 0; + int found_depth = 0; + + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); + + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { + copy_v2_v2_int(mval, &ptc->x); + + if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr + i) == 0))) + { + interp_depth = true; + } + else { + found_depth = true; + } + + copy_v2_v2_int(mval_prev, mval); + } + + if (found_depth == false) { + /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) + depth_arr[i] = 0.9999f; + } + else { + if (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) { + /* remove all info between the valid endpoints */ + int first_valid = 0; + int last_valid = 0; + + for (i = 0; i < gpd->runtime.sbuffer_size; i++) { + if (depth_arr[i] != FLT_MAX) + break; + } + first_valid = i; + + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { + if (depth_arr[i] != FLT_MAX) + break; + } + last_valid = i; + + /* invalidate non-endpoints, so only blend between first and last */ + for (i = first_valid + 1; i < last_valid; i++) + depth_arr[i] = FLT_MAX; + + interp_depth = true; + } + + if (interp_depth) { + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); + } + } + } + + + pt = gps->points; + + /* convert all points (normal behavior) */ + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); + + /* 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 axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + + if (depth_arr) + MEM_freeN(depth_arr); + } + + /* add stroke to frame */ + BLI_addtail(&p->gpf->strokes, gps); + gp_stroke_added_enable(p); +} + +/* --- 'Eraser' for 'Paint' Tool ------ */ + +/* helper to free a stroke + * NOTE: gps->dvert and gps->triangles should be NULL, but check anyway for good measure + */ +static void gp_free_stroke(bGPdata *UNUSED(gpd), bGPDframe *gpf, bGPDstroke *gps) +{ + if (gps->points) { + MEM_freeN(gps->points); + } + + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + if (gps->triangles) { + MEM_freeN(gps->triangles); + } + + BLI_freelinkN(&gpf->strokes, gps); +} + + +/* which which point is infront (result should only be used for comparison) */ +static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) +{ + if (rv3d->is_persp) { + return ED_view3d_calc_zfac(rv3d, co, NULL); + } + else { + return -dot_v3v3(rv3d->viewinv[2], co); + } +} + +/* only erase stroke points that are visible (3d view) */ +static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) +{ + if ((p->sa->spacetype == SPACE_VIEW3D) && + (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) + { + RegionView3D *rv3d = p->ar->regiondata; + const int mval[2] = {x, y}; + float mval_3d[3]; + + if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { + const float depth_mval = view3d_point_depth(rv3d, mval_3d); + const float depth_pt = view3d_point_depth(rv3d, &pt->x); + + if (depth_pt > depth_mval) { + return true; + } + } + } + return false; +} + +/* eraser tool - evaluation per stroke */ +/* TODO: this could really do with some optimization (KD-Tree/BVH?) */ +static void gp_stroke_eraser_dostroke(tGPsdata *p, + bGPDframe *gpf, bGPDstroke *gps, + const int mval[2], const int mvalo[2], + const int radius, const rcti *rect) +{ + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + + if (gps->totpoints == 0) { + /* just free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + else if (gps->totpoints == 1) { + /* only process if it hasn't been masked out... */ + if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { + gp_point_to_xy(&p->gsc, gps, gps->points, &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])) { + /* only check if point is inside */ + if (len_v2v2_int(mval, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + } + } + } + else { + /* Perform culling? */ + bool do_cull = false; + + /* Clear Tags + * + * Note: It's better this way, as we are sure that + * we don't miss anything, though things will be + * slightly slower as a result + */ + for (i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->flag &= ~GP_SPOINT_TAG; + } + + /* First Pass: Loop over the points in the stroke + * 1) Thin out parts of the stroke under the brush + * 2) Tag "too thin" parts for removal (in second pass) + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* get points to work with */ + pt1 = gps->points + i; + pt2 = gps->points + i + 1; + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); + + /* Check that point segment of the boundbox of the eraser 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 + * eraser region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || + (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) + { + /* Edge is affected - Check individual points now */ + if (len_v2v2_int(mval, pc1) <= radius) { + pt1->flag |= GP_SPOINT_TAG; + } + if (len_v2v2_int(mval, pc2) <= radius) { + pt2->flag |= GP_SPOINT_TAG; + } + do_cull = true; + } + } + } + } + + /* Second Pass: Remove any points that are tagged */ + if (do_cull) { + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); + } + } +} + +/* erase strokes which fall under the eraser strokes */ +static void gp_stroke_doeraser(tGPsdata *p) +{ + bGPDframe *gpf = p->gpf; + bGPDstroke *gps, *gpn; + rcti rect; + + /* rect is rectangle of eraser */ + rect.xmin = p->mval[0] - p->radius; + rect.ymin = p->mval[1] - p->radius; + rect.xmax = p->mval[0] + p->radius; + rect.ymax = p->mval[1] + p->radius; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { + View3D *v3d = p->sa->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); + } + } + + /* loop over strokes of active layer only (session init already took care of ensuring validity), + * checking segments for intersections to remove + */ + for (gps = gpf->strokes.first; gps; gps = gpn) { + gpn = gps->next; + /* 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, gpf, gps, p->mval, p->mvalo, p->radius, &rect); + } + } +} + +/* ******************************************* */ +/* Sketching Operator */ + +/* clear the session buffers (call this before AND after a paint operation) */ +static void gp_session_validatebuffer(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - reset sbuffer\n"); */ + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + } + else { + /* printf("\t\tGP - allocate sbuffer\n"); */ + gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + } + + /* reset indices */ + gpd->runtime.sbuffer_size = 0; + + /* reset flags */ + gpd->runtime.sbuffer_sflag = 0; + + /* reset inittime */ + p->inittime = 0.0; +} + +/* (re)init new painting data */ +static bool gp_session_initdata(bContext *C, tGPsdata *p) +{ + Main *bmain = CTX_data_main(C); + bGPdata **gpd_ptr = NULL; + ScrArea *curarea = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + /* make sure the active view (at the starting time) is a 3d-view */ + if (curarea == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No active view for painting\n"); + return 0; + } + + /* pass on current scene and window */ + p->bmain = CTX_data_main(C); + p->scene = CTX_data_scene(C); + p->depsgraph = CTX_data_depsgraph(C); + p->win = CTX_wm_window(C); + + unit_m4(p->imat); + unit_m4(p->mat); + + switch (curarea->spacetype) { + /* supported views first */ + case SPACE_VIEW3D: + { + /* View3D *v3d = curarea->spacedata.first; */ + /* RegionView3D *rv3d = ar->regiondata; */ + + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->sa = curarea; + p->ar = ar; + p->align_flag = &ts->annotate_v3d_align; + + if (ar->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); + return 0; + } + break; + } + case SPACE_NODE: + { + /* SpaceNode *snode = curarea->spacedata.first; */ + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq = curarea->spacedata.first; + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_seq_align; + + /* check that gpencil data is allowed to be drawn */ + if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); + return 0; + } + break; + } + case SPACE_IMAGE: + { + /* SpaceImage *sima = curarea->spacedata.first; */ + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_ima_align; + break; + } + case SPACE_CLIP: + { + SpaceClip *sc = curarea->spacedata.first; + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (clip == NULL) { + p->status = GP_STATUS_ERROR; + return false; + } + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + + invert_m4_m4(p->imat, sc->unistabmat); + + /* custom color for new layer */ + p->custom_color[0] = 1.0f; + p->custom_color[1] = 0.0f; + p->custom_color[2] = 0.5f; + p->custom_color[3] = 0.9f; + + if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + int framenr = ED_space_clip_get_clip_frame_number(sc); + MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); + MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; + + if (marker) { + p->imat[3][0] -= marker->pos[0]; + p->imat[3][1] -= marker->pos[1]; + } + else { + p->status = GP_STATUS_ERROR; + return false; + } + } + + invert_m4_m4(p->mat, p->imat); + copy_m4_m4(p->gsc.mat, p->mat); + break; + } + /* unsupported views */ + default: + { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Annotations are not supported in this editor\n"); + return 0; + } + } + + /* get gp-data */ + gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); + if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Current context doesn't allow for any Annotation data\n"); + return 0; + } + else { + /* if no existing GPencil block exists, add one */ + if (*gpd_ptr == NULL) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); + *gpd_ptr = gpd; + + /* mark datablock as being used for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; + + /* annotations always in front of all objects */ + gpd->xray_mode = GP_XRAY_FRONT; + } + p->gpd = *gpd_ptr; + } + + if (ED_gpencil_session_active() == 0) { + /* initialize undo stack, + * also, existing undo stack would make buffer drawn + */ + gpencil_undo_init(p->gpd); + } + + /* clear out buffer (stored in gp-data), in case something contaminated it */ + gp_session_validatebuffer(p); + + /* lock axis */ + p->lock_axis = ts->gp_sculpt.lock_axis; + + return 1; +} + +/* init new painting session */ +static tGPsdata *gp_session_initpaint(bContext *C) +{ + tGPsdata *p = NULL; + + /* create new context data */ + p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); + + gp_session_initdata(C, p); + + /* radius for eraser circle is defined in userprefs now */ + /* NOTE: we do this here, so that if we exit immediately, + * erase size won't get lost + */ + p->radius = U.gp_eraser; + + /* return context data for running paint operator */ + return p; +} + +/* cleanup after a painting session */ +static void gp_session_cleanup(tGPsdata *p) +{ + bGPdata *gpd = (p) ? p->gpd : NULL; + + /* error checking */ + if (gpd == NULL) + return; + + /* free stroke buffer */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - free sbuffer\n"); */ + MEM_freeN(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; + } + + /* clear flags */ + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; + p->inittime = 0.0; +} + +static void gp_session_free(tGPsdata *p) +{ + MEM_freeN(p); +} + + +/* init new stroke */ +static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) +{ + Scene *scene = p->scene; + ToolSettings *ts = scene->toolsettings; + + /* get active layer (or add a new one if non-existent) */ + p->gpl = BKE_gpencil_layer_getactive(p->gpd); + if (p->gpl == NULL) { + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + + if (p->custom_color[3]) + copy_v3_v3(p->gpl->color, p->custom_color); + } + if (p->gpl->flag & GP_LAYER_LOCKED) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Cannot paint on locked layer\n"); + return; + } + + /* get active frame (add a new one if not matching frame) */ + if (paintmode == GP_PAINTMODE_ERASER) { + /* Eraser mode: + * 1) Only allow erasing on the active layer (unlike for 3d-art Grease Pencil), + * since we won't be exposing layer locking in the UI + * 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; + + if (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; + } + } + + /* Ensure active frame is set correctly... */ + p->gpf = p->gpl->actframe; + + /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on + * (though this is only available in editmode) + */ + if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { + if (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_SELECT_MASK) { + p->flags |= GP_PAINTFLAG_SELECTMASK; + } + } + + if (has_layer_to_erase == false) { + p->status = GP_STATUS_ERROR; + //if (G.debug & G_DEBUG) + printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); + return; + } + } + else { + /* Drawing Modes - Add a new frame if needed on the active layer */ + short add_frame_mode = GP_GETFRAME_ADD_NEW; + + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) + add_frame_mode = GP_GETFRAME_ADD_COPY; + else + add_frame_mode = GP_GETFRAME_ADD_NEW; + + p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + + if (p->gpf == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No frame created (gpencil_paint_init)\n"); + return; + } + else { + p->gpf->flag |= GP_FRAME_PAINT; + } + } + + /* set 'eraser' for this stroke if using eraser */ + p->paintmode = paintmode; + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; + + /* check if we should respect depth while erasing */ + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags |= GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + else { + /* disable eraser flags - so that we can switch modes during a session */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ + p->flags |= GP_PAINTFLAG_FIRSTRUN; + + + /* when drawing in the camera view, in 2D space, set the subrect */ + p->subrect = NULL; + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + if (p->sa->spacetype == SPACE_VIEW3D) { + View3D *v3d = p->sa->spacedata.first; + RegionView3D *rv3d = p->ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + ED_view3d_calc_camera_border(p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); /* no shift */ + p->subrect = &p->subrect_data; + } + } + } + + /* init stroke point space-conversion settings... */ + p->gsc.gpd = p->gpd; + p->gsc.gpl = p->gpl; + + p->gsc.sa = p->sa; + p->gsc.ar = p->ar; + p->gsc.v2d = p->v2d; + + p->gsc.subrect_data = p->subrect_data; + p->gsc.subrect = p->subrect; + + copy_m4_m4(p->gsc.mat, p->mat); + + + /* check if points will need to be made in view-aligned space */ + if (*p->align_flag & GP_PROJECT_VIEWSPACE) { + switch (p->sa->spacetype) { + case SPACE_VIEW3D: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + break; + } + case SPACE_NODE: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + case SPACE_SEQ: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = (SpaceImage *)p->sa->spacedata.first; + + /* only set these flags if the image editor doesn't have an image active, + * otherwise user will be confused by strokes not appearing after they're drawn + * + * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint! + */ + if (ELEM(NULL, sima, sima->image)) { + /* make strokes be drawn in screen space */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_2DSPACE; + *(p->align_flag) &= ~GP_PROJECT_VIEWSPACE; + } + else { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + } + break; + } + case SPACE_CLIP: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + } + } +} + +/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ +static void gp_paint_strokeend(tGPsdata *p) +{ + ToolSettings *ts = p->scene->toolsettings; + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* check if doing eraser or not */ + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { + /* simplify stroke before transferring? */ + gp_stroke_simplify(p); + + /* transfer stroke to frame */ + gp_stroke_newfrombuffer(p); + } + + /* clean up buffer now */ + gp_session_validatebuffer(p); +} + +/* finish off stroke painting operation */ +static void gp_paint_cleanup(tGPsdata *p) +{ + /* p->gpd==NULL happens when stroke failed to initialize, + * for example when GP is hidden in current space (sergey) + */ + if (p->gpd) { + /* finish off a stroke */ + gp_paint_strokeend(p); + } + + /* "unlock" frame */ + if (p->gpf) + p->gpf->flag &= ~GP_FRAME_PAINT; +} + +/* ------------------------------- */ + +/* Helper callback for drawing the cursor itself */ +static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) +{ + tGPsdata *p = (tGPsdata *)p_ptr; + + if (p->paintmode == GP_PAINTMODE_ERASER) { + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_line_smooth(true); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); + + imm_draw_circle_wire_2d(shdr_pos, x, y, p->radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, p->radius / 2)); /* was fixed 40 */ + + immUnbindProgram(); + + GPU_blend(false); + GPU_line_smooth(false); + } +} + +/* Turn brush cursor in 3D view on/off */ +static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) +{ + if (p->erasercursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); + p->erasercursor = NULL; + } + else if (enable && !p->erasercursor) { + /* enable cursor */ + p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), + NULL, /* XXX */ + gpencil_draw_eraser, p); + } +} + +/* Check if tablet eraser is being used (when processing events) */ +static bool gpencil_is_tablet_eraser_active(const wmEvent *event) +{ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + return (wmtab->Active == EVT_TABLET_ERASER); + } + + return false; +} + +/* ------------------------------- */ + +static void gpencil_draw_exit(bContext *C, wmOperator *op) +{ + tGPsdata *p = op->customdata; + + /* clear undo stack */ + gpencil_undo_finish(); + + /* restore cursor to indicate end of drawing */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + /* don't assume that operator data exists at all */ + if (p) { + /* check size of buffer before cleanup, to determine if anything happened here */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* turn off radial brush cursor */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + + /* always store the new eraser size to be used again next time + * NOTE: Do this even when not in eraser mode, as eraser may + * have been toggled at some point. + */ + U.gp_eraser = p->radius; + + /* cleanup */ + gp_paint_cleanup(p); + gp_session_cleanup(p); + gp_session_free(p); + } + + op->customdata = NULL; +} + +static void gpencil_draw_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_draw_exit(C, op); +} + +/* ------------------------------- */ + + +static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPsdata *p; + eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + + /* check context */ + p = op->customdata = gp_session_initpaint(C); + if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { + /* something wasn't set correctly in context */ + gpencil_draw_exit(C, op); + return 0; + } + + /* init painting data */ + gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); + if (p->status == GP_STATUS_ERROR) { + gpencil_draw_exit(C, op); + return 0; + } + + if (event != NULL) { + p->keymodifier = event->keymodifier; + } + else { + p->keymodifier = -1; + } + + /* everything is now setup ok */ + return 1; +} + + +/* ------------------------------- */ + +/* ensure that the correct cursor icon is set */ +static void gpencil_draw_cursor_set(tGPsdata *p) +{ + if (p->paintmode == GP_PAINTMODE_ERASER) + WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + else + WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); +} + +/* update UI indicators of status, including cursor and header prints */ +static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) +{ + /* header prints */ + switch (p->status) { + case GP_STATUS_PAINTING: + switch (p->paintmode) { + case GP_PAINTMODE_DRAW_POLY: + /* Provide usage tips, since this is modal, and unintuitive without hints */ + ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + default: + /* Do nothing - the others are self explanatory, exit quickly once the mouse is released + * Showing any text would just be annoying as it would flicker. + */ + break; + } + break; + + case GP_STATUS_IDLING: + /* print status info */ + switch (p->paintmode) { + case GP_PAINTMODE_ERASER: + ED_workspace_status_text(C, IFACE_("Annotation Eraser: Hold and drag LMB or RMB to erase | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_STRAIGHT: + ED_workspace_status_text(C, IFACE_("Annotation Line Draw: Hold and drag LMB to draw | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW: + ED_workspace_status_text(C, IFACE_("Annotation Freehand Draw: Hold and drag LMB to draw | " + "E/ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_POLY: + ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + + default: /* unhandled future cases */ + ED_workspace_status_text(C, IFACE_("Annotation Session: ESC/Enter to end (or click outside this area)")); + break; + } + break; + + case GP_STATUS_ERROR: + case GP_STATUS_DONE: + /* clear status string */ + ED_workspace_status_text(C, NULL); + break; + } +} + +/* ------------------------------- */ + +/* create a new stroke point at the point indicated by the painting context */ +static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +{ + /* handle drawing/erasing -> test for erasing first */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* do 'live' erasing now */ + gp_stroke_doeraser(p); + + /* store used values */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + 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)) { + /* 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 */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } +} + +/* handle draw event */ +static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph) +{ + tGPsdata *p = op->customdata; + PointerRNA itemptr; + float mousef[2]; + int tablet = 0; + + /* convert from window-space to area-space mouse coordinates + * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... + */ + p->mval[0] = event->mval[0] + 1; + p->mval[1] = event->mval[1] + 1; + + /* verify key status for straight lines */ + if ((event->ctrl > 0) || (event->alt > 0)) { + if (p->straight[0] == 0) { + int dx = abs(p->mval[0] - p->mvalo[0]); + int dy = abs(p->mval[1] - p->mvalo[1]); + if ((dx > 0) || (dy > 0)) { + /* check mouse direction to replace the other coordinate with previous values */ + if (dx >= dy) { + /* horizontal */ + p->straight[0] = 1; + p->straight[1] = p->mval[1]; /* save y */ + } + else { + /* vertical */ + p->straight[0] = 2; + p->straight[1] = p->mval[0]; /* save x */ + } + } + } + } + else { + p->straight[0] = 0; + } + + p->curtime = PIL_check_seconds_timer(); + + /* handle pressure sensitivity (which is supplied by tablets) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + p->pressure = wmtab->Pressure; + + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; + } + } + } + else { + /* No tablet data -> No pressure info is available */ + p->pressure = 1.0f; + } + + /* special exception for start of strokes (i.e. maybe for just a dot) */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->inittime = p->ocurtime = p->curtime; + p->straight[0] = 0; + p->straight[1] = 0; + + /* special exception here for too high pressure values on first touch in + * windows for some tablets, then we just skip first touch... + */ + if (tablet && (p->pressure >= 0.99f)) + return; + } + + /* check if alt key is pressed and limit to straight lines */ + if (p->straight[0] != 0) { + if (p->straight[0] == 1) { + /* horizontal */ + p->mval[1] = p->straight[1]; /* replace y */ + } + else { + /* vertical */ + p->mval[0] = p->straight[1]; /* replace x */ + } + } + + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + mousef[0] = p->mval[0]; + mousef[1] = p->mval[1]; + RNA_float_set_array(&itemptr, "mouse", mousef); + RNA_float_set(&itemptr, "pressure", p->pressure); + RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); + + RNA_float_set(&itemptr, "time", p->curtime - p->inittime); + + /* apply the current latest drawing point */ + gpencil_draw_apply(op, p, depsgraph); + + /* force refresh */ + ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ +} + +/* ------------------------------- */ + +/* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ +static int gpencil_draw_exec(bContext *C, wmOperator *op) +{ + tGPsdata *p = NULL; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* printf("GPencil - Starting Re-Drawing\n"); */ + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, NULL)) { + if (op->customdata) MEM_freeN(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] = (int)mousef[0]; + p->mval[1] = (int)mousef[1]; + p->pressure = RNA_float_get(&itemptr, "pressure"); + p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; + + if (RNA_boolean_get(&itemptr, "is_start")) { + /* if first-run flag isn't set already (i.e. not true first stroke), + * then we must terminate the previous one first before continuing + */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + /* TODO: both of these ops can set error-status, but we probably don't need to worry */ + gp_paint_strokeend(p); + gp_paint_initstroke(p, p->paintmode, depsgraph); + } + } + + /* if first run, set previous data too */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } + + /* apply this data as necessary now (as per usual) */ + gpencil_draw_apply(op, p, depsgraph); + } + RNA_END; + + /* printf("\tGP - done\n"); */ + + /* cleanup */ + gpencil_draw_exit(C, op); + + /* refreshes */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +/* ------------------------------- */ + +/* start of interactive drawing part of operator */ +static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + tGPsdata *p = NULL; + + /* GPXX Need a better solution */ + if ((ob != NULL) && (ob->type == OB_GPENCIL)) { + BKE_report(op->reports, RPT_ERROR, "Cannot draw annotation with a Grease Pencil object active"); + return OPERATOR_CANCELLED; + } + + if (G.debug & G_DEBUG) + printf("GPencil - Starting Drawing\n"); + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, event)) { + if (op->customdata) + MEM_freeN(op->customdata); + if (G.debug & G_DEBUG) + printf("\tGP - no valid data\n"); + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* TODO: set any additional settings that we can take from the events? + * TODO? if tablet is erasing, force eraser to be on? */ + + /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ + + /* if eraser is on, draw radial aid */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + gpencil_draw_toggle_eraser_cursor(C, p, true); + } + /* 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(op, event, CTX_data_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; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* add a modal handler for this operator, so that we can then draw continuous strokes */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ +static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) +{ + bScreen *sc = CTX_wm_screen(C); + return (BLI_findindex(&sc->areabase, sa_test) != -1); +} + +static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) +{ + tGPsdata *p = op->customdata; + + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + printf("\t\t\tGP - wrong area execution abort!\n"); + 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, p)) + gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); + + if (p->status != GP_STATUS_ERROR) { + p->status = GP_STATUS_PAINTING; + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } + + return op->customdata; +} + +static void gpencil_stroke_end(wmOperator *op) +{ + tGPsdata *p = op->customdata; + + gp_paint_cleanup(p); + + gpencil_undo_push(p->gpd); + + gp_session_cleanup(p); + + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + + p->gpd = NULL; + p->gpl = NULL; + p->gpf = NULL; +} + +/* events handling during interactive drawing part of operator */ +static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPsdata *p = op->customdata; + int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ + + /* if (event->type == NDOF_MOTION) + * return OPERATOR_PASS_THROUGH; + * ------------------------------- + * [mce] Not quite what I was looking + * for, but a good start! GP continues to + * draw on the screen while the 3D mouse + * moves the viewpoint. Problem is that + * the stroke is converted to 3D only after + * it is finished. This approach should work + * better in tools that immediately apply + * in 3D space. + */ + + if (p->status == GP_STATUS_IDLING) { + ARegion *ar = CTX_wm_region(C); + p->ar = ar; + } + + /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ + if (ISKEYBOARD(event->type)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { + /* allow some keys: + * - for frame changing [#33412] + * - for undo (during sketching sessions) + */ + } + else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { + /* allow numpad keys so that camera/view manipulations can still take place + * - PAD0 in particular is really important for Grease Pencil drawing, + * as animators may be working "to camera", so having this working + * is essential for ensuring that they can quickly return to that view + */ + } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } + else { + estate = OPERATOR_RUNNING_MODAL; + } + } + + //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 [#32647] + */ + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { + /* exit() ends the current stroke before cleaning up */ + /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + 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) + * (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"); */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_PRESS) { + bool in_bounds = false; + + /* Check if we're outside the bounds of the active region + * NOTE: An exception here is that if launched from the toolbar, + * whatever region we're now in should become the new region + */ + if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { + /* Change to whatever region is now under the mouse */ + ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); + + if (G.debug & G_DEBUG) { + printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", + current_region, p->ar, event->x, event->y, + p->sa->totrct.xmin, p->sa->totrct.ymin, p->sa->totrct.xmax, p->sa->totrct.ymax); + } + + if (current_region) { + /* Assume that since we found the cursor in here, it is in bounds + * and that this should be the region that we begin drawing in + */ + p->ar = current_region; + in_bounds = true; + } + else { + /* Out of bounds, or invalid in some other way */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); + } + } + else if (p->ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(p->ar, ®ion_rect); + in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + } + else { + /* No region */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: No active region found in GP Paint session data\n", __func__); + } + + if (in_bounds) { + /* Switch paintmode (temporarily if need be) based on which button was used + * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + */ + if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { + /* turn on eraser */ + p->paintmode = GP_PAINTMODE_ERASER; + } + else if (event->type == LEFTMOUSE) { + /* restore drawmode to default */ + p->paintmode = RNA_enum_get(op->ptr, "mode"); + } + + gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); + + /* not painting, so start stroke (this should be mouse-button down) */ + p = gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { + estate = OPERATOR_CANCELLED; + } + } + else if (p->status != GP_STATUS_ERROR) { + /* User clicked outside bounds of window while idling, so exit paintmode + * NOTE: Don't enter this case if an error occurred while finding the + * region (as above) + */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_RELEASE) { + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + } + + /* handle mode-specific events */ + if (p->status == GP_STATUS_PAINTING) { + /* 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"); */ + gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + + /* finish painting operation if anything went wrong just now */ + if (p->status == GP_STATUS_ERROR) { + printf("\t\t\t\tGP - add error done!\n"); + estate = OPERATOR_CANCELLED; + } + 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"); */ + switch (event->type) { + case WHEELDOWNMOUSE: /* larger */ + case PADPLUSKEY: + p->radius += 5; + break; + + case WHEELUPMOUSE: /* smaller */ + case PADMINUS: + p->radius -= 5; + + if (p->radius <= 0) + p->radius = 1; + break; + } + + /* force refresh */ + ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ + + /* event handled, so just tag as running modal */ + estate = OPERATOR_RUNNING_MODAL; + } + /* there shouldn't be any other events, but just in case there are, let's swallow them + * (i.e. to prevent problems with undo) + */ + else { + /* swallow event to save ourselves trouble */ + estate = OPERATOR_RUNNING_MODAL; + } + } + + /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + if (0 == gpencil_area_exists(C, p->sa)) + estate = OPERATOR_CANCELLED; + 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 */ + switch (estate) { + case OPERATOR_FINISHED: + /* one last flush before we're done */ + gpencil_draw_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_draw_exit(C, op); + break; + + 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; + } + + /* return status code */ + return estate; +} + +/* ------------------------------- */ + +static const EnumPropertyItem prop_gpencil_drawmodes[] = { + {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, + {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 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 Annotation strokes"}, + {0, NULL, 0, NULL, NULL} +}; + +void GPENCIL_OT_annotate(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Annotation Draw"; + ot->idname = "GPENCIL_OT_annotate"; + ot->description = "Make annotations on the active data"; + + /* api callbacks */ + ot->exec = gpencil_draw_exec; + ot->invoke = gpencil_draw_invoke; + ot->modal = gpencil_draw_modal; + ot->cancel = gpencil_draw_cancel; + ot->poll = gpencil_draw_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* settings for drawing */ + ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 29b24886017..5c56877cbe6 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -46,6 +46,7 @@ #include "BLF_api.h" #include "BLT_translation.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -55,8 +56,13 @@ #include "DNA_object_types.h" #include "BKE_context.h" +#include "BKE_brush.h" #include "BKE_global.h" +#include "BKE_paint.h" #include "BKE_gpencil.h" +#include "BKE_image.h" + +#include "DEG_depsgraph.h" #include "WM_api.h" @@ -74,6 +80,10 @@ #include "UI_interface_icons.h" #include "UI_resources.h" +#include "IMB_imbuf_types.h" + +#include "gpencil_intern.h" + /* ************************************************** */ /* GREASE PENCIL DRAWING */ @@ -88,8 +98,7 @@ typedef enum eDrawStrokeFlags { GP_DRAWDATA_NO_XRAY = (1 << 5), /* don't draw xray in 3D view (which is default) */ GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ GP_DRAWDATA_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */ - GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */ - GP_DRAWDATA_HQ_FILL = (1 << 9) /* Use high quality fill */ + GP_DRAWDATA_FILL = (1 << 8) /* fill insides/bounded-regions of strokes */ } eDrawStrokeFlags; @@ -100,12 +109,12 @@ typedef enum eDrawStrokeFlags { #endif /* conversion utility (float --> normalized unsigned byte) */ -#define F2UB(x) (unsigned char)(255.0f * x) +#define F2UB(x) (uchar)(255.0f * x) /* ----- Tool Buffer Drawing ------ */ /* helper functions to set color of buffer point */ -static void gp_set_tpoint_varying_color(const tGPspoint *pt, const float ink[4], unsigned attrib_id) +static void gp_set_tpoint_varying_color(const tGPspoint *pt, const float ink[4], uint attrib_id) { float alpha = ink[3] * pt->strength; CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); @@ -119,7 +128,7 @@ static void gp_set_point_uniform_color(const bGPDspoint *pt, const float ink[4]) immUniformColor3fvAlpha(ink, alpha); } -static void gp_set_point_varying_color(const bGPDspoint *pt, const float ink[4], unsigned attrib_id) +static void gp_set_point_varying_color(const bGPDspoint *pt, const float ink[4], uint attrib_id) { float alpha = ink[3] * pt->strength; CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); @@ -134,7 +143,7 @@ static void gp_draw_stroke_buffer_fill(const tGPspoint *points, int totpoints, f } int tot_triangles = totpoints - 2; /* allocate memory for temporary areas */ - unsigned int(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, "GP Stroke buffer temp triangulation"); + uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, "GP Stroke buffer temp triangulation"); float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, "GP Stroke buffer temp 2d points"); /* Convert points to array and triangulate @@ -146,7 +155,7 @@ static void gp_draw_stroke_buffer_fill(const tGPspoint *points, int totpoints, f points2d[i][0] = pt->x; points2d[i][1] = pt->y; } - BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)totpoints, 0, (unsigned int(*)[3])tmp_triangles); + BLI_polyfill_calc((const float(*)[2])points2d, (uint)totpoints, 0, (uint(*)[3])tmp_triangles); /* draw triangulation data */ if (tot_triangles > 0) { @@ -253,7 +262,7 @@ static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short if (i != 0) { gp_set_tpoint_varying_color(pt - 1, ink, color); immVertex2iv(pos, &(pt - 1)->x); - ++draw_points; + draw_points++; } oldpressure = pt->pressure; /* reset our threshold */ @@ -262,7 +271,7 @@ static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short /* now the point we want */ gp_set_tpoint_varying_color(pt, ink, color); immVertex2iv(pos, &pt->x); - ++draw_points; + draw_points++; } /* need to have 2 points to avoid immEnd assert error */ if (draw_points < 2) { @@ -402,6 +411,50 @@ static void gp_draw_stroke_volumetric_3d( /* --------------- 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, 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]; + } +} /* Get points of stroke always flat to view not affected by camera view or view position */ static void gp_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction) @@ -447,7 +500,6 @@ static void gp_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*po *r_direction = (int)locy[2]; } - /* 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) { @@ -455,14 +507,23 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) /* allocate memory for temporary areas */ gps->tot_triangles = gps->totpoints - 2; - unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); + 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 */ gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); - BLI_polyfill_calc(points2d, (unsigned int)gps->totpoints, direction, tmp_triangles); + 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; @@ -476,7 +537,12 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) } for (int i = 0; i < gps->tot_triangles; i++) { - memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3])); + 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 { @@ -493,73 +559,128 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) } /* clear memory */ - if (tmp_triangles) MEM_freeN(tmp_triangles); - if (points2d) MEM_freeN(points2d); + 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, + int offsx, int offsy, int winx, int winy, const float diff_mat[4][4]) +{ + float fpt[3]; + float co[2]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* if 2d, need conversion */ + if (!flag & GP_STROKE_3DSPACE) { + gp_calc_2d_stroke_fxy(fpt, flag, offsx, offsy, winx, winy, co); + copy_v2_v2(fpt, co); + fpt[2] = 0.0f; /* 2d always is z=0.0f */ + } + + immAttrib2f(texcoord, uv[0], uv[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, int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float color[4]) { - float fpt[3]; - BLI_assert(gps->totpoints >= 3); + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); - - /* Triangulation fill if high quality flag is enabled */ - if (palcolor->flag & PC_COLOR_HQ_FILL) { - /* Calculate triangles cache for filling area (must be done only after changes) */ - if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { - gp_triangulate_stroke_fill(gps); - } - BLI_assert(gps->tot_triangles >= 1); - - uint pos; - if (gps->flag & GP_STROKE_3DSPACE) { - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - } - else { - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - } - - immUniformColor4fv(color); - - /* 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 */ - - bGPDtriangle *stroke_triangle = gps->triangles; - bGPDspoint *pt; - - if (gps->flag & GP_STROKE_3DSPACE) { - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - pt = &gps->points[stroke_triangle->verts[j]]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - immVertex3fv(pos, fpt); - } - } - } - else { - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - float co[2]; - pt = &gps->points[stroke_triangle->verts[j]]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); - immVertex3fv(pos, fpt); - } - } - } - - immEnd(); - immUnbindProgram(); + /* Calculate triangles cache for filling area (must be done only after changes) */ + if ((gps->flag & GP_STROKE_RECALC_CACHES) || (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); + immBindBuiltinProgram(GPU_SHADER_GPENCIL_FILL); + + immUniformColor4fv(color); + immUniform4fv("color2", gp_style->mix_rgba); + 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_COLOR_TEX_MIX ? 1 : 0); + immUniform1i("t_flip", gp_style->flag & GP_STYLE_COLOR_FLIP_FILL ? 1 : 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 + /* 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 */ + + const bGPDtriangle *stroke_triangle = gps->triangles; + 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, + offsx, offsy, winx, winy, diff_mat); + } + } + + immEnd(); + immUnbindProgram(); } /* ----- Existing Strokes Drawing (3D and Point) ------ */ @@ -601,87 +722,81 @@ static void gp_draw_stroke_point( immUnbindProgram(); } -/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thickness, bool UNUSED(debug), - short UNUSED(sflag), const float diff_mat[4][4], const float ink[4], bool cyclic) +/* draw a given stroke in 3d (i.e. in 3d-space) */ +static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic) { + bGPDspoint *points = tgpw->gps->points; + int totpoints = tgpw->gps->totpoints; + + const float viewport[2] = { (float)tgpw->winx, (float)tgpw->winy }; float curpressure = points[0].pressure; float fpt[3]; - float cyclic_fpt[3]; - int draw_points = 0; - - /* if cyclic needs one vertex more */ - int cyclic_add = 0; - if (cyclic) { - ++cyclic_add; - } + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint thickattrib = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); + immBindBuiltinProgram(GPU_SHADER_GPENCIL_STROKE); + immUniform2fv("Viewport", viewport); + immUniform1f("pixsize", tgpw->rv3d->pixsize); + immUniform1f("pixelsize", U.pixelsize); + float obj_scale = (tgpw->ob->size[0] + tgpw->ob->size[1] + tgpw->ob->size[2]) / 3.0f; - /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + immUniform1f("objscale", obj_scale); + int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); + immUniform1i("keep_size", keep_size); + immUniform1i("pixfactor", tgpw->gpd->pixfactor); + immUniform1i("xraymode", tgpw->gpd->xray_mode); /* draw stroke curve */ GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2); const bGPDspoint *pt = points; + for (int i = 0; i < totpoints; i++, pt++) { + /* first point for adjacency (not drawn) */ + if (i == 0) { + gp_set_point_varying_color(points, ink, color); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + if ((cyclic) && (totpoints > 2)) { + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x); + } + else { + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); + } + mul_v3_fl(fpt, -1.0f); + immVertex3fv(pos, fpt); + } + /* set point */ gp_set_point_varying_color(pt, ink, color); - - /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, - * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) - * Note: we want more visible levels of pressures when thickness is bigger. - */ - if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { - /* if the pressure changes before get at least 2 vertices, need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - immVertex3fv(pos, fpt); - } - immEnd(); - draw_points = 0; - - curpressure = pt->pressure; - GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); - - /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - gp_set_point_varying_color(pt2, ink, color); - immVertex3fv(pos, fpt); - ++draw_points; - } - } - - /* now the point we want */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x); immVertex3fv(pos, fpt); - ++draw_points; - if (cyclic && i == 0) { - /* save first point to use in cyclic */ - copy_v3_v3(cyclic_fpt, fpt); - } + curpressure = pt->pressure; } - if (cyclic) { + if (cyclic && totpoints > 2) { /* draw line to first point to complete the cycle */ - immVertex3fv(pos, cyclic_fpt); - ++draw_points; - } + immAttrib1f(thickattrib, max_ff(points->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x); + immVertex3fv(pos, fpt); - /* if less of two points, need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - gp_set_point_varying_color(pt2, ink, color); + /* now add adjacency point (not drawn) */ + immAttrib1f(thickattrib, max_ff((points + 1)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); + immVertex3fv(pos, fpt); + } + /* last adjacency point (not drawn) */ + else { + gp_set_point_varying_color(points + totpoints - 1, ink, color); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x); + mul_v3_fl(fpt, -1.0f); immVertex3fv(pos, fpt); } @@ -692,8 +807,9 @@ static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thi /* ----- Fancy 2D-Stroke Drawing ------ */ /* draw a given stroke in 2d */ -static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) +static void gp_draw_stroke_2d( + const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, + bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; @@ -900,10 +1016,7 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) } /* draw a set of strokes */ -static void gp_draw_strokes( - bGPdata *gpd, const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, - bool debug, short lthick, const float opacity, const float tintcolor[4], - const bool onion, const bool custonion, const float diff_mat[4][4]) +static void gp_draw_strokes(tGPDdraw *tgpw) { float tcolor[4]; float tfill[4]; @@ -912,31 +1025,41 @@ static void gp_draw_strokes( GPU_enable_program_point_size(); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (bGPDstroke *gps = tgpw->t_gpf->strokes.first; gps; gps = gps->next) { /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) { + if (gp_can_draw_stroke(gps, tgpw->dflag) == false) { continue; } /* check if the color is visible */ - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); - if ((palcolor == NULL) || - (palcolor->flag & PC_COLOR_HIDE) || + Material *ma = tgpw->gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; + + if ((gp_style == NULL) || + (gp_style->flag & GP_STYLE_COLOR_HIDE) || /* if onion and ghost flag do not draw*/ - (onion && (palcolor->flag & PC_COLOR_ONIONSKIN))) + (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_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)) + { + continue; + } + /* calculate thickness */ - sthickness = gps->thickness + lthick; + sthickness = gps->thickness + tgpw->lthick; if (sthickness <= 0) { continue; } /* check which stroke-drawer to use */ - if (dflag & GP_DRAWDATA_ONLY3D) { - const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); + if (tgpw->dflag & GP_DRAWDATA_ONLY3D) { + const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); int mask_orig = 0; if (no_xray) { @@ -951,57 +1074,64 @@ static void gp_draw_strokes( /* 3D Fill */ //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - if (gps->totpoints >= 3) { - /* set color using palette, tint color and opacity */ - interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); - tfill[3] = palcolor->fill[3] * opacity; - if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if ((gps->totpoints >= 3) && (tgpw->disable_fill != 1)) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { const float *color; - if (!onion) { + if (!tgpw->onion) { color = tfill; } else { - if (custonion) { - color = tintcolor; + if (tgpw->custonion) { + color = tgpw->tintcolor; } else { - ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]); + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); color = tfill; } } - gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat, color); + gp_draw_stroke_fill( + tgpw->gpd, gps, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); } } /* 3D Stroke */ - /* set color using palette, tint color and opacity */ - if (!onion) { - interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); - tcolor[3] = palcolor->color[3] * opacity; + /* set color using material tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; copy_v4_v4(ink, tcolor); } else { - if (custonion) { - copy_v4_v4(ink, tintcolor); + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); } else { - ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); copy_v4_v4(ink, tcolor); } } - if (palcolor->flag & PC_COLOR_VOLUMETRIC) { + if (gp_style->mode == GP_STYLE_MODE_DOTS) { /* volumetric stroke drawing */ - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); + if (tgpw->disable_fill != 1) { + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); + } } else { /* 3D Lines - OpenGL primitives-based */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, - diff_mat, ink); + if (tgpw->disable_fill != 1) { + gp_draw_stroke_point(gps->points, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, + tgpw->diff_mat, ink); + } } else { - gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag, - diff_mat, ink, gps->flag & GP_STROKE_CYCLIC); + tgpw->gps = gps; + gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC); } } if (no_xray) { @@ -1014,58 +1144,63 @@ static void gp_draw_strokes( else { /* 2D - Fill */ if (gps->totpoints >= 3) { - /* set color using palette, tint color and opacity */ - interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); - tfill[3] = palcolor->fill[3] * opacity; - if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { const float *color; - if (!onion) { + if (!tgpw->onion) { color = tfill; } else { - if (custonion) { - color = tintcolor; + if (tgpw->custonion) { + color = tgpw->tintcolor; } else { - ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2], - tintcolor[3]); + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); color = tfill; } } - gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat, color); + gp_draw_stroke_fill( + tgpw->gpd, gps, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); } } /* 2D Strokes... */ - /* set color using palette, tint color and opacity */ - if (!onion) { - interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); - tcolor[3] = palcolor->color[3] * opacity; + /* set color using material, tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; copy_v4_v4(ink, tcolor); } else { - if (custonion) { - copy_v4_v4(ink, tintcolor); + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); } else { - ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); copy_v4_v4(ink, tcolor); } } - if (palcolor->flag & PC_COLOR_VOLUMETRIC) { + if (gp_style->mode == GP_STYLE_MODE_DOTS) { /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, - offsx, offsy, winx, winy, diff_mat, ink); + gp_draw_stroke_volumetric_2d( + gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); } else { /* normal 2D strokes */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, - diff_mat, ink); + gp_draw_stroke_point( + gps->points, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, + tgpw->diff_mat, ink); } else { - gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug, - offsx, offsy, winx, winy, diff_mat, ink); + gp_draw_stroke_2d( + gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, false, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); } } } @@ -1114,14 +1249,16 @@ static void gp_draw_strokes_edit( if ((gps->flag & GP_STROKE_SELECT) == 0) continue; - /* verify palette color lock */ + /* verify color lock */ { - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); - if (palcolor != NULL) { - if (palcolor->flag & PC_COLOR_HIDE) { + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; + + if (gp_style != NULL) { + if (gp_style->flag & GP_STYLE_COLOR_HIDE) { continue; } - if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) { + if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { continue; } } @@ -1143,8 +1280,9 @@ static void gp_draw_strokes_edit( } /* for now, we assume that the base color of the points is not too close to the real color */ - /* set color using palette */ - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + /* set color using material */ + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; float selectColor[4]; UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); @@ -1153,7 +1291,7 @@ static void gp_draw_strokes_edit( GPUVertFormat *format = immVertexFormat(); uint pos; /* specified later */ uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); if (gps->flag & GP_STROKE_3DSPACE) { pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -1189,7 +1327,7 @@ static void gp_draw_strokes_edit( immAttrib1f(size, vsize); } else { - immAttrib3fv(color, palcolor->color); + immAttrib3fv(color, gp_style->stroke_rgba); immAttrib1f(size, bsize); } @@ -1229,97 +1367,18 @@ static void gp_draw_strokes_edit( /* ----- General Drawing ------ */ -/* draw onion-skinning for a layer */ -static void gp_draw_onionskins( - bGPdata *gpd, const bGPDlayer *gpl, const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, bool debug, const float diff_mat[4][4]) -{ - const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; - const float alpha = 1.0f; - float color[4]; - - /* 1) Draw Previous Frames First */ - if (gpl->flag & GP_LAYER_GHOST_PREVCOL) { - copy_v3_v3(color, gpl->gcolor_prev); - } - else { - copy_v3_v3(color, default_color); - } - - if (gpl->gstep > 0) { - /* draw previous frames first */ - for (bGPDframe *gf = gpf->prev; gf; gf = gf->prev) { - /* check if frame is drawable */ - if ((gpf->framenum - gf->framenum) <= gpl->gstep) { - /* alpha decreases with distance from curframe index */ - float fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); - color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); - } - else - break; - } - } - else if (gpl->gstep == 0) { - /* draw the strokes for the ghost frames (at half of the alpha set by user) */ - if (gpf->prev) { - color[3] = (alpha / 7); - gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); - } - } - else { - /* don't draw - disabled */ - } - - /* 2) Now draw next frames */ - if (gpl->flag & GP_LAYER_GHOST_NEXTCOL) { - copy_v3_v3(color, gpl->gcolor_next); - } - else { - copy_v3_v3(color, default_color); - } - - if (gpl->gstep_next > 0) { - /* now draw next frames */ - for (bGPDframe *gf = gpf->next; gf; gf = gf->next) { - /* check if frame is drawable */ - if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) { - /* alpha decreases with distance from curframe index */ - float fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); - color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); - } - else - break; - } - } - else if (gpl->gstep_next == 0) { - /* draw the strokes for the ghost frames (at half of the alpha set by user) */ - if (gpf->next) { - color[3] = (alpha / 4); - gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); - } - } - else { - /* don't draw - disabled */ - } -} /* draw interpolate strokes (used only while operator is running) */ -void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type) +void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type) { + tGPDdraw tgpw; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; tGPDinterpolate_layer *tgpil; - float diff_mat[4][4]; - float color[4]; + Object *obact = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); - int offsx = 0; - int offsy = 0; - int winx = tgpi->ar->winx; - int winy = tgpi->ar->winy; + float color[4]; UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); color[3] = 0.6f; @@ -1329,32 +1388,127 @@ void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type) 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->ar->winx; + tgpw.winy = tgpi->ar->winy; + tgpw.dflag = dflag; + /* turn on alpha-blending */ - GPU_blend(true); + glEnable(GL_BLEND); for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { /* calculate parent position */ - ED_gpencil_parent_location(tgpil->gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat); if (tgpil->interFrame) { - gp_draw_strokes(tgpi->gpd, tgpil->interFrame, offsx, offsy, winx, winy, dflag, false, - tgpil->gpl->thickness, 1.0f, color, true, true, diff_mat); + tgpw.gpl = tgpil->gpl; + tgpw.gpf = tgpil->interFrame; + tgpw.t_gpf = tgpil->interFrame; + + tgpw.lthick = tgpil->gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, color); + tgpw.onion = true; + tgpw.custonion = true; + + gp_draw_strokes(&tgpw); + } + } + glDisable(GL_BLEND); +} + +/* draw interpolate strokes (used only while operator is running) */ +void ED_gp_draw_primitives(const bContext *C, tGPDprimitive *tgpi, const int type) +{ + tGPDdraw tgpw; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* if idle, do not draw */ + if (tgpi->flag == 0) { + return; + } + + Object *obact = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_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->ar->winx; + tgpw.winy = tgpi->ar->winy; + tgpw.dflag = dflag; + + /* turn on alpha-blending */ + GPU_blend(true); + /* calculate parent position */ + ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpi->gpl, tgpw.diff_mat); + if (tgpi->gpf) { + tgpw.gps = tgpi->gpf->strokes.first; + if (tgpw.gps->totpoints > 0) { + tgpw.gpl = tgpi->gpl; + tgpw.gpf = tgpi->gpf; + tgpw.t_gpf = tgpi->gpf; + + tgpw.lthick = tgpi->gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, color); + tgpw.onion = true; + tgpw.custonion = true; + + 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); +} + /* loop over gpencil data layers, drawing them */ -static void gp_draw_data_layers( - const bGPDbrush *brush, float alpha, bGPdata *gpd, +static void gp_draw_data_layers(RegionView3D *rv3d, + const Brush *brush, float alpha, Object *ob, bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { float diff_mat[4][4]; + tGPDdraw tgpw; + + tgpw.rv3d = rv3d; + tgpw.depsgraph = NULL; /* XXX: This is not used here */ + tgpw.ob = ob; + tgpw.gpd = gpd; + tgpw.gpl = NULL; + tgpw.gpf = NULL; + tgpw.t_gpf = NULL; + tgpw.offsx = offsx; + tgpw.offsy = offsy; + tgpw.winx = winx; + tgpw.winy = winy; + tgpw.dflag = dflag; for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* calculate parent position */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, diff_mat); - bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG); - short lthick = brush->thickness + gpl->thickness; + short lthick = brush->size + gpl->line_change; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) @@ -1383,30 +1537,20 @@ static void gp_draw_data_layers( /* volumetric strokes... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); - /* HQ fills... */ - GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); - #undef GP_DRAWFLAG_APPLY - /* Draw 'onionskins' (frame left + right) - * - It is only possible to show these if the option is enabled - * - The "no onions" flag prevents ghosts from appearing during animation playback/scrubbing - * and in renders - * - The per-layer "always show" flag however overrides the playback/render restriction, - * allowing artists to selectively turn onionskins on/off during playback - */ - if ((gpl->flag & GP_LAYER_ONIONSKIN) && - ((dflag & GP_DRAWDATA_NO_ONIONS) == 0 || (gpl->flag & GP_LAYER_GHOST_ALWAYS))) - { - /* Drawing method - only immediately surrounding (gstep = 0), - * or within a frame range on either side (gstep > 0) - */ - gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat); - } + tgpw.gpl = gpl; + tgpw.gpf = gpf; + tgpw.t_gpf = gpf; // XXX? + tgpw.lthick = gpl->line_change; + tgpw.opacity = gpl->opacity; + copy_v4_v4(tgpw.tintcolor, gpl->tintcolor); + tgpw.onion = false; + tgpw.custonion = false; + copy_m4_m4(tgpw.diff_mat, diff_mat); /* draw the strokes already in active frame */ - gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, - gpl->opacity, gpl->tintcolor, false, false, diff_mat); + gp_draw_strokes(&tgpw); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering @@ -1435,19 +1579,25 @@ static void gp_draw_data_layers( * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - if (gpd->sflag & PC_COLOR_VOLUMETRIC) { - gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, - dflag, gpd->scolor); + if (gpd->runtime.mode == GP_STYLE_MODE_DOTS) { + gp_draw_stroke_volumetric_buffer( + gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.scolor); } else { - gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor, gpd->sfill); + gp_draw_stroke_buffer( + gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.sbuffer_sflag, + gpd->runtime.scolor, gpd->runtime.sfill); } } } } /* draw a short status message in the top-right corner */ -static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) +static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar) { rcti rect; @@ -1493,8 +1643,8 @@ static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) } /* draw grease-pencil datablock */ -static void gp_draw_data( - const bGPDbrush *brush, float alpha, bGPdata *gpd, +static void gp_draw_data(RegionView3D *rv3d, + const Brush *brush, float alpha, Object *ob, bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* turn on smooth lines (i.e. anti-aliasing) */ @@ -1510,7 +1660,7 @@ static void gp_draw_data( GPU_blend(true); /* draw! */ - gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data_layers(rv3d, brush, alpha, ob, gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ GPU_blend(false); // alpha blending @@ -1519,33 +1669,22 @@ static void gp_draw_data( /* if we have strokes for scenes (3d view)/clips (movie clip editor) * and objects/tracks, multiple data blocks have to be drawn */ -static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, - int cfra, int dflag, const char spacetype) +static void gp_draw_data_all( + RegionView3D *rv3d, Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, const char UNUSED(spacetype)) { bGPdata *gpd_source = NULL; - ToolSettings *ts; - bGPDbrush *brush = NULL; + ToolSettings *ts = NULL; + Brush *brush = NULL; if (scene) { ts = scene->toolsettings; - brush = BKE_gpencil_brush_getactive(ts); - /* if no brushes, create default set */ - if (brush == NULL) { - BKE_gpencil_brush_init_presets(ts); - brush = BKE_gpencil_brush_getactive(ts); - } - - if (spacetype == SPACE_VIEW3D) { - gpd_source = (scene->gpd ? scene->gpd : NULL); - } - else if (spacetype == SPACE_CLIP && scene->clip) { - /* currently drawing only gpencil data from either clip or track, but not both - XXX fix logic behind */ - gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); - } + brush = BKE_brush_getactive_gpencil(ts); if (gpd_source) { if (brush != NULL) { - gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, - offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data( + rv3d, brush, 1.0f, NULL, gpd_source, + offsx, offsy, winx, winy, cfra, dflag); } } } @@ -1554,136 +1693,35 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { if (brush != NULL) { - gp_draw_data(brush, ts->gp_sculpt.alpha, gpd, - offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data( + rv3d, brush, 1.0f, NULL, gpd, + offsx, offsy, winx, winy, cfra, dflag); } } } /* ----- Grease Pencil Sketches Drawing API ------ */ -/* ............................ - * XXX - * We need to review the calls below, since they may be/are not that suitable for - * the new ways that we intend to be drawing data... - * ............................ */ - -/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ -void ED_gpencil_draw_2dimage(const bContext *C) -{ - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - - int offsx, offsy, sizex, sizey; - int dflag = GP_DRAWDATA_NOSTATUS; - - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX - if (gpd == NULL) return; - - /* calculate rect */ - switch (sa->spacetype) { - case SPACE_IMAGE: /* image */ - case SPACE_CLIP: /* clip */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); - - dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; - break; - } - case SPACE_SEQ: /* sequence */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated - * and everything moved to standard View2d - */ - dflag |= GP_DRAWDATA_ONLYV2D; - break; - } - default: /* for spacetype not yet handled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - dflag |= GP_DRAWDATA_ONLYI2D; - break; - } - - if (ED_screen_animation_playing(wm)) { - /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) - * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) - */ - dflag |= GP_DRAWDATA_NO_ONIONS; - } - - /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); -} - -/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly - * Note: this gets called twice - first time with onlyv2d=true to draw 'canvas' strokes, - * second time with onlyv2d=false for screen-aligned strokes */ -void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) -{ - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - int dflag = 0; - - /* check that we have grease-pencil stuff to draw */ - if (sa == NULL) return; - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX - if (gpd == NULL) return; - - /* special hack for Image Editor */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) - dflag |= GP_DRAWDATA_IEDITHACK; - - /* draw it! */ - if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); - if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS; - - gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); - - /* draw status text (if in screen/pixel-space) */ - if (!onlyv2d) { - gp_draw_status_text(gpd, ar); - } -} /* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, * second time with only3d=false for screen-aligned strokes */ -void ED_gpencil_draw_view3d(wmWindowManager *wm, - Scene *scene, - ViewLayer *view_layer, - struct Depsgraph *depsgraph, - View3D *v3d, - ARegion *ar, - bool only3d) +void ED_gpencil_draw_view3d( + wmWindowManager *wm, + Scene *scene, + ViewLayer *view_layer, + struct Depsgraph *depsgraph, + View3D *v3d, + ARegion *ar, + bool only3d) { int dflag = 0; RegionView3D *rv3d = ar->regiondata; int offsx, offsy, winx, winy; /* check that we have grease-pencil stuff to draw */ - bGPdata *gpd = ED_gpencil_data_get_active_v3d(scene, view_layer); + // XXX: This is the only place that still uses this function + bGPdata *gpd = ED_gpencil_data_get_active_v3d(view_layer); if (gpd == NULL) return; /* when rendering to the offscreen buffer we don't want to @@ -1726,14 +1764,73 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, } /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + gp_draw_data_all(rv3d, scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); } -void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) +/* draw grease-pencil sketches to specified 3d-view for gp object + * assuming that matrices are already set correctly + */ +void ED_gpencil_draw_view3d_object(wmWindowManager *wm, Scene *scene, Depsgraph *depsgraph, Object *ob, View3D *v3d, ARegion *ar, bool only3d) +{ + int dflag = 0; + RegionView3D *rv3d = ar->regiondata; + int offsx, offsy, winx, winy; + + /* check that we have grease-pencil stuff to draw */ + bGPdata *gpd = ob->data; + if (gpd == NULL) return; + + /* when rendering to the offscreen buffer we don't want to + * deal with the camera border, otherwise map the coords to the camera border. */ + if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) { + rctf rectf; + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ + + offsx = round_fl_to_int(rectf.xmin); + offsy = round_fl_to_int(rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); + } + else { + offsx = 0; + offsy = 0; + winx = ar->winx; + winy = ar->winy; + } + + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + + if ((wm == NULL) || ED_screen_animation_playing(wm)) { + /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) + * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) + */ + dflag |= GP_DRAWDATA_NO_ONIONS; + } + + /* draw it! */ + ToolSettings *ts = scene->toolsettings; + Brush *brush = BKE_brush_getactive_gpencil(ts); + if (brush != NULL) { + gp_draw_data(rv3d, brush, 1.0f, ob, gpd, + offsx, offsy, winx, winy, CFRA, dflag); + } +} + +void ED_gpencil_draw_ex(RegionView3D *rv3d, Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) { int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; - gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); + gp_draw_data_all(rv3d, scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); } - -/* ************************************************** */ diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 5e62a87caf3..88f935eb8bf 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -492,6 +492,8 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* make a copy of stroke, then of its points array */ gpsn = MEM_dupallocN(gps); gpsn->points = MEM_dupallocN(gps->points); + gpsn->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsn); /* duplicate triangle information */ gpsn->triangles = MEM_dupallocN(gps->triangles); /* append stroke to frame */ diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c new file mode 100644 index 00000000000..8a7128adde1 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -0,0 +1,1567 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Matias Mendiola + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/gpencil_add_monkey.c + * \ingroup edgpencil + */ + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_monkey_color(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + if (STREQ(ma->id.name, pct->name)) { + return i; + } + } + + /* create a new one */ + BKE_object_material_slot_add(bmain, ob); + ma = BKE_material_add_gpencil(bmain, pct->name); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + + return BKE_object_material_slot_find_index(ob, ma) - 1; +} + +/* ***************************************************************** */ +/* Monkey Geometry */ + +static const float data0[538 * GP_PRIM_DATABUF_SIZE] = { + -0.509f, 0.0f, -0.156f, 0.267f, 0.362f, -0.522f, 0.0f, -0.159f, 0.31f, 0.407f, -0.531f, 0.0f, -0.16f, 0.347f, 0.426f, -0.543f, -0.0f, -0.162f, 0.38f, 0.439f, + -0.554f, -0.0f, -0.163f, 0.409f, 0.448f, -0.566f, -0.0f, -0.165f, 0.433f, 0.458f, -0.578f, -0.0f, -0.167f, 0.454f, 0.478f, -0.591f, -0.0f, -0.168f, 0.471f, 0.5f, + -0.604f, -0.0f, -0.169f, 0.485f, 0.51f, -0.619f, -0.0f, -0.171f, 0.496f, 0.516f, -0.634f, -0.0f, -0.171f, 0.504f, 0.519f, -0.649f, -0.0f, -0.171f, 0.511f, 0.519f, + -0.665f, -0.0f, -0.17f, 0.516f, 0.521f, -0.681f, -0.0f, -0.17f, 0.521f, 0.53f, -0.697f, -0.0f, -0.169f, 0.524f, 0.533f, -0.713f, -0.0f, -0.167f, 0.527f, 0.533f, + -0.729f, 0.0f, -0.165f, 0.53f, 0.534f, -0.745f, 0.0f, -0.161f, 0.531f, 0.534f, -0.761f, 0.0f, -0.157f, 0.533f, 0.535f, -0.777f, 0.0f, -0.153f, 0.534f, 0.535f, + -0.792f, 0.0f, -0.148f, 0.535f, 0.536f, -0.808f, 0.0f, -0.144f, 0.535f, 0.535f, -0.822f, 0.0f, -0.139f, 0.536f, 0.537f, -0.837f, 0.0f, -0.133f, 0.536f, 0.537f, + -0.852f, 0.0f, -0.128f, 0.536f, 0.537f, -0.866f, 0.0f, -0.122f, 0.536f, 0.537f, -0.88f, 0.0f, -0.115f, 0.536f, 0.537f, -0.894f, 0.0f, -0.109f, 0.536f, 0.537f, + -0.908f, 0.0f, -0.101f, 0.535f, 0.535f, -0.922f, 0.0f, -0.092f, 0.535f, 0.535f, -0.936f, 0.0f, -0.082f, 0.534f, 0.534f, -0.949f, 0.0f, -0.072f, 0.534f, 0.534f, + -0.963f, 0.0f, -0.061f, 0.534f, 0.534f, -0.976f, 0.0f, -0.05f, 0.534f, 0.534f, -0.988f, 0.0f, -0.039f, 0.534f, 0.534f, -1.0f, 0.0f, -0.028f, 0.533f, 0.534f, + -1.011f, 0.0f, -0.017f, 0.533f, 0.533f, -1.022f, 0.0f, -0.007f, 0.533f, 0.534f, -1.033f, 0.0f, 0.004f, 0.533f, 0.533f, -1.043f, 0.0f, 0.014f, 0.532f, 0.532f, + -1.053f, 0.0f, 0.025f, 0.532f, 0.532f, -1.062f, 0.0f, 0.036f, 0.531f, 0.531f, -1.071f, 0.0f, 0.046f, 0.531f, 0.531f, -1.078f, 0.0f, 0.057f, 0.531f, 0.531f, + -1.085f, 0.0f, 0.068f, 0.531f, 0.531f, -1.092f, 0.0f, 0.08f, 0.532f, 0.532f, -1.098f, 0.0f, 0.091f, 0.533f, 0.533f, -1.104f, 0.0f, 0.105f, 0.535f, 0.535f, + -1.11f, 0.0f, 0.119f, 0.539f, 0.539f, -1.115f, 0.0f, 0.133f, 0.54f, 0.54f, -1.118f, 0.0f, 0.148f, 0.541f, 0.541f, -1.121f, 0.0f, 0.162f, 0.542f, 0.542f, + -1.123f, 0.0f, 0.177f, 0.542f, 0.542f, -1.125f, 0.0f, 0.193f, 0.543f, 0.543f, -1.125f, 0.0f, 0.208f, 0.543f, 0.543f, -1.125f, 0.0f, 0.225f, 0.543f, 0.543f, + -1.124f, 0.0f, 0.241f, 0.545f, 0.545f, -1.122f, 0.0f, 0.258f, 0.546f, 0.546f, -1.119f, 0.0f, 0.274f, 0.548f, 0.548f, -1.116f, 0.0f, 0.29f, 0.549f, 0.549f, + -1.111f, 0.0f, 0.305f, 0.549f, 0.549f, -1.106f, 0.0f, 0.318f, 0.549f, 0.549f, -1.1f, 0.0f, 0.33f, 0.549f, 0.549f, -1.094f, 0.0f, 0.34f, 0.549f, 0.549f, + -1.087f, 0.0f, 0.349f, 0.55f, 0.55f, -1.08f, 0.0f, 0.357f, 0.549f, 0.549f, -1.072f, 0.0f, 0.365f, 0.55f, 0.55f, -1.063f, 0.0f, 0.372f, 0.551f, 0.551f, + -1.054f, 0.0f, 0.379f, 0.552f, 0.552f, -1.044f, 0.0f, 0.385f, 0.553f, 0.553f, -1.034f, 0.0f, 0.391f, 0.553f, 0.553f, -1.024f, 0.0f, 0.396f, 0.554f, 0.554f, + -1.013f, 0.0f, 0.401f, 0.554f, 0.554f, -1.003f, 0.0f, 0.405f, 0.554f, 0.554f, -0.991f, 0.0f, 0.409f, 0.554f, 0.554f, -0.978f, 0.0f, 0.412f, 0.555f, 0.555f, + -0.964f, -0.0f, 0.414f, 0.555f, 0.555f, -0.949f, -0.0f, 0.414f, 0.556f, 0.556f, -0.934f, -0.0f, 0.413f, 0.556f, 0.556f, -0.919f, -0.0f, 0.412f, 0.557f, 0.557f, + -0.905f, -0.0f, 0.41f, 0.557f, 0.557f, -0.892f, -0.0f, 0.406f, 0.557f, 0.557f, -0.879f, -0.0f, 0.402f, 0.557f, 0.558f, -0.867f, -0.0f, 0.398f, 0.557f, 0.557f, + -0.855f, -0.0f, 0.394f, 0.557f, 0.557f, -0.843f, -0.0f, 0.388f, 0.557f, 0.557f, -0.831f, -0.0f, 0.381f, 0.558f, 0.557f, -0.82f, -0.0f, 0.375f, 0.558f, 0.557f, + -0.81f, -0.0f, 0.368f, 0.558f, 0.558f, -0.801f, -0.0f, 0.362f, 0.558f, 0.558f, -0.793f, -0.0f, 0.357f, 0.557f, 0.559f, -0.784f, 0.0f, 0.353f, 0.557f, 0.559f, + -0.776f, 0.0f, 0.35f, 0.556f, 0.559f, -0.768f, 0.0f, 0.348f, 0.556f, 0.559f, -0.76f, 0.0f, 0.346f, 0.555f, 0.559f, -0.752f, 0.0f, 0.346f, 0.554f, 0.559f, + -0.744f, 0.0f, 0.347f, 0.553f, 0.554f, -0.737f, 0.0f, 0.348f, 0.552f, 0.548f, -0.729f, 0.0f, 0.351f, 0.551f, 0.544f, -0.723f, 0.0f, 0.355f, 0.551f, 0.546f, + -0.716f, 0.0f, 0.36f, 0.55f, 0.546f, -0.709f, 0.0f, 0.366f, 0.55f, 0.547f, -0.702f, 0.0f, 0.372f, 0.549f, 0.547f, -0.696f, 0.0f, 0.379f, 0.549f, 0.547f, + -0.689f, 0.0f, 0.386f, 0.549f, 0.548f, -0.683f, 0.0f, 0.394f, 0.549f, 0.548f, -0.676f, 0.0f, 0.403f, 0.549f, 0.549f, -0.67f, 0.0f, 0.413f, 0.549f, 0.548f, + -0.664f, 0.0f, 0.422f, 0.549f, 0.549f, -0.658f, 0.0f, 0.432f, 0.55f, 0.549f, -0.652f, 0.0f, 0.441f, 0.551f, 0.548f, -0.646f, 0.0f, 0.451f, 0.552f, 0.548f, + -0.639f, 0.0f, 0.46f, 0.554f, 0.548f, -0.632f, 0.0f, 0.469f, 0.556f, 0.549f, -0.624f, 0.0f, 0.478f, 0.559f, 0.549f, -0.616f, 0.0f, 0.487f, 0.563f, 0.549f, + -0.609f, 0.0f, 0.497f, 0.567f, 0.549f, -0.6f, 0.0f, 0.507f, 0.572f, 0.558f, -0.592f, 0.0f, 0.518f, 0.577f, 0.574f, -0.584f, 0.0f, 0.528f, 0.582f, 0.587f, + -0.575f, 0.0f, 0.538f, 0.586f, 0.592f, -0.566f, 0.0f, 0.548f, 0.591f, 0.595f, -0.556f, 0.0f, 0.557f, 0.594f, 0.597f, -0.546f, 0.0f, 0.567f, 0.597f, 0.598f, + -0.536f, 0.0f, 0.577f, 0.6f, 0.6f, -0.525f, 0.0f, 0.586f, 0.602f, 0.603f, -0.514f, 0.0f, 0.596f, 0.604f, 0.605f, -0.503f, 0.0f, 0.606f, 0.605f, 0.606f, + -0.492f, 0.0f, 0.615f, 0.606f, 0.607f, -0.482f, 0.0f, 0.624f, 0.607f, 0.607f, -0.471f, 0.0f, 0.632f, 0.608f, 0.607f, -0.462f, 0.0f, 0.64f, 0.609f, 0.607f, + -0.453f, 0.0f, 0.647f, 0.61f, 0.61f, -0.444f, 0.0f, 0.654f, 0.612f, 0.611f, -0.435f, 0.0f, 0.66f, 0.614f, 0.613f, -0.427f, 0.0f, 0.666f, 0.616f, 0.615f, + -0.418f, 0.0f, 0.672f, 0.617f, 0.618f, -0.409f, 0.0f, 0.677f, 0.619f, 0.621f, -0.399f, 0.0f, 0.683f, 0.621f, 0.622f, -0.389f, 0.0f, 0.69f, 0.623f, 0.623f, + -0.379f, 0.0f, 0.696f, 0.624f, 0.624f, -0.368f, 0.0f, 0.702f, 0.626f, 0.626f, -0.356f, 0.0f, 0.708f, 0.628f, 0.628f, -0.345f, 0.0f, 0.713f, 0.63f, 0.63f, + -0.333f, 0.0f, 0.719f, 0.633f, 0.631f, -0.32f, 0.0f, 0.724f, 0.637f, 0.632f, -0.307f, 0.0f, 0.729f, 0.641f, 0.64f, -0.294f, 0.0f, 0.732f, 0.646f, 0.644f, + -0.281f, 0.0f, 0.736f, 0.65f, 0.655f, -0.268f, 0.0f, 0.739f, 0.654f, 0.657f, -0.255f, 0.0f, 0.742f, 0.657f, 0.658f, -0.243f, 0.0f, 0.745f, 0.659f, 0.661f, + -0.23f, 0.0f, 0.747f, 0.662f, 0.663f, -0.217f, 0.0f, 0.75f, 0.664f, 0.664f, -0.203f, 0.0f, 0.753f, 0.666f, 0.666f, -0.19f, 0.0f, 0.755f, 0.667f, 0.668f, + -0.177f, 0.0f, 0.757f, 0.669f, 0.67f, -0.163f, 0.0f, 0.76f, 0.671f, 0.671f, -0.15f, 0.0f, 0.762f, 0.673f, 0.672f, -0.136f, 0.0f, 0.764f, 0.674f, 0.674f, + -0.122f, 0.0f, 0.767f, 0.676f, 0.676f, -0.108f, 0.0f, 0.769f, 0.677f, 0.678f, -0.093f, 0.0f, 0.771f, 0.678f, 0.68f, -0.079f, 0.0f, 0.773f, 0.678f, 0.68f, + -0.064f, 0.0f, 0.774f, 0.679f, 0.679f, -0.049f, 0.0f, 0.775f, 0.68f, 0.68f, -0.033f, 0.0f, 0.775f, 0.68f, 0.68f, -0.018f, 0.0f, 0.776f, 0.68f, 0.68f, + -0.002f, 0.0f, 0.776f, 0.681f, 0.68f, 0.013f, 0.0f, 0.777f, 0.681f, 0.681f, 0.029f, 0.0f, 0.777f, 0.682f, 0.681f, 0.045f, 0.0f, 0.777f, 0.682f, 0.681f, + 0.061f, 0.0f, 0.777f, 0.683f, 0.683f, 0.077f, 0.0f, 0.776f, 0.683f, 0.683f, 0.094f, 0.0f, 0.775f, 0.684f, 0.684f, 0.11f, 0.0f, 0.774f, 0.685f, 0.683f, + 0.126f, 0.0f, 0.773f, 0.685f, 0.685f, 0.142f, 0.0f, 0.771f, 0.687f, 0.685f, 0.158f, 0.0f, 0.769f, 0.688f, 0.685f, 0.174f, 0.0f, 0.767f, 0.69f, 0.686f, + 0.19f, 0.0f, 0.765f, 0.691f, 0.692f, 0.206f, 0.0f, 0.762f, 0.693f, 0.694f, 0.222f, 0.0f, 0.757f, 0.695f, 0.696f, 0.238f, 0.0f, 0.752f, 0.697f, 0.697f, + 0.254f, 0.0f, 0.747f, 0.699f, 0.698f, 0.27f, 0.0f, 0.742f, 0.7f, 0.7f, 0.286f, 0.0f, 0.736f, 0.702f, 0.702f, 0.302f, 0.0f, 0.73f, 0.704f, 0.704f, + 0.318f, 0.0f, 0.724f, 0.705f, 0.71f, 0.335f, 0.0f, 0.717f, 0.707f, 0.71f, 0.351f, 0.0f, 0.709f, 0.708f, 0.71f, 0.367f, 0.0f, 0.701f, 0.709f, 0.711f, + 0.382f, 0.0f, 0.692f, 0.71f, 0.713f, 0.397f, 0.0f, 0.683f, 0.711f, 0.713f, 0.41f, 0.0f, 0.675f, 0.712f, 0.713f, 0.422f, 0.0f, 0.666f, 0.712f, 0.714f, + 0.434f, 0.0f, 0.658f, 0.713f, 0.714f, 0.446f, 0.0f, 0.649f, 0.714f, 0.714f, 0.458f, 0.0f, 0.641f, 0.714f, 0.714f, 0.47f, 0.0f, 0.632f, 0.715f, 0.715f, + 0.483f, 0.0f, 0.622f, 0.715f, 0.716f, 0.496f, 0.0f, 0.611f, 0.715f, 0.716f, 0.51f, 0.0f, 0.6f, 0.716f, 0.717f, 0.523f, 0.0f, 0.588f, 0.716f, 0.716f, + 0.536f, 0.0f, 0.576f, 0.717f, 0.717f, 0.55f, 0.0f, 0.563f, 0.717f, 0.717f, 0.564f, 0.0f, 0.549f, 0.717f, 0.717f, 0.577f, 0.0f, 0.536f, 0.718f, 0.717f, + 0.59f, 0.0f, 0.522f, 0.718f, 0.717f, 0.603f, 0.0f, 0.508f, 0.718f, 0.718f, 0.615f, 0.0f, 0.496f, 0.718f, 0.718f, 0.625f, 0.0f, 0.484f, 0.718f, 0.718f, + 0.635f, 0.0f, 0.473f, 0.719f, 0.718f, 0.645f, 0.0f, 0.461f, 0.719f, 0.718f, 0.654f, 0.0f, 0.45f, 0.719f, 0.718f, 0.662f, 0.0f, 0.44f, 0.719f, 0.719f, + 0.67f, 0.0f, 0.431f, 0.719f, 0.719f, 0.676f, 0.0f, 0.422f, 0.719f, 0.719f, 0.682f, 0.0f, 0.414f, 0.719f, 0.719f, 0.687f, 0.0f, 0.407f, 0.719f, 0.719f, + 0.692f, 0.0f, 0.4f, 0.719f, 0.719f, 0.697f, 0.0f, 0.394f, 0.719f, 0.719f, 0.701f, 0.0f, 0.388f, 0.718f, 0.718f, 0.705f, 0.0f, 0.383f, 0.718f, 0.717f, + 0.708f, 0.0f, 0.378f, 0.718f, 0.717f, 0.711f, 0.0f, 0.374f, 0.717f, 0.717f, 0.714f, 0.0f, 0.37f, 0.717f, 0.717f, 0.717f, 0.0f, 0.366f, 0.717f, 0.717f, + 0.719f, 0.0f, 0.362f, 0.718f, 0.717f, 0.722f, 0.0f, 0.359f, 0.718f, 0.718f, 0.724f, 0.0f, 0.356f, 0.718f, 0.717f, 0.727f, 0.0f, 0.352f, 0.717f, 0.719f, + 0.73f, 0.0f, 0.349f, 0.717f, 0.719f, 0.734f, 0.0f, 0.347f, 0.715f, 0.719f, 0.737f, 0.0f, 0.344f, 0.714f, 0.714f, 0.742f, 0.0f, 0.341f, 0.713f, 0.709f, + 0.746f, 0.0f, 0.339f, 0.714f, 0.707f, 0.751f, 0.0f, 0.336f, 0.718f, 0.704f, 0.757f, 0.0f, 0.334f, 0.724f, 0.705f, 0.763f, 0.0f, 0.332f, 0.732f, 0.705f, + 0.769f, -0.0f, 0.329f, 0.742f, 0.704f, 0.775f, -0.0f, 0.328f, 0.753f, 0.713f, 0.782f, -0.0f, 0.327f, 0.764f, 0.804f, 0.789f, -0.0f, 0.327f, 0.774f, 0.813f, + 0.797f, -0.0f, 0.327f, 0.783f, 0.815f, 0.805f, -0.0f, 0.328f, 0.791f, 0.815f, 0.814f, -0.0f, 0.329f, 0.797f, 0.816f, 0.823f, -0.0f, 0.331f, 0.802f, 0.815f, + 0.832f, 0.0f, 0.335f, 0.806f, 0.816f, 0.841f, 0.0f, 0.341f, 0.809f, 0.816f, 0.851f, 0.0f, 0.346f, 0.811f, 0.816f, 0.861f, 0.0f, 0.351f, 0.812f, 0.816f, + 0.871f, 0.0f, 0.356f, 0.813f, 0.815f, 0.881f, 0.0f, 0.361f, 0.814f, 0.816f, 0.893f, 0.0f, 0.365f, 0.814f, 0.816f, 0.906f, 0.0f, 0.368f, 0.814f, 0.817f, + 0.922f, 0.0f, 0.372f, 0.813f, 0.816f, 0.939f, 0.0f, 0.375f, 0.812f, 0.817f, 0.957f, 0.0f, 0.377f, 0.811f, 0.817f, 0.977f, 0.0f, 0.379f, 0.81f, 0.815f, + 0.995f, 0.0f, 0.38f, 0.808f, 0.813f, 1.012f, 0.0f, 0.379f, 0.806f, 0.807f, 1.028f, 0.0f, 0.377f, 0.803f, 0.803f, 1.042f, 0.0f, 0.374f, 0.8f, 0.801f, + 1.054f, 0.0f, 0.371f, 0.797f, 0.8f, 1.065f, 0.0f, 0.366f, 0.794f, 0.8f, 1.076f, 0.0f, 0.361f, 0.791f, 0.792f, 1.085f, 0.0f, 0.355f, 0.788f, 0.781f, + 1.093f, 0.0f, 0.348f, 0.785f, 0.781f, 1.1f, 0.0f, 0.34f, 0.783f, 0.78f, 1.106f, 0.0f, 0.33f, 0.782f, 0.78f, 1.113f, 0.0f, 0.321f, 0.781f, 0.778f, + 1.117f, 0.0f, 0.31f, 0.78f, 0.777f, 1.122f, -0.0f, 0.299f, 0.779f, 0.777f, 1.125f, -0.0f, 0.286f, 0.778f, 0.776f, 1.129f, -0.0f, 0.274f, 0.778f, 0.777f, + 1.131f, -0.0f, 0.262f, 0.778f, 0.777f, 1.132f, -0.0f, 0.249f, 0.777f, 0.777f, 1.134f, -0.0f, 0.237f, 0.777f, 0.778f, 1.134f, -0.0f, 0.225f, 0.777f, 0.778f, + 1.135f, -0.0f, 0.213f, 0.776f, 0.777f, 1.134f, -0.0f, 0.201f, 0.776f, 0.776f, 1.134f, -0.0f, 0.189f, 0.776f, 0.775f, 1.132f, -0.0f, 0.177f, 0.775f, 0.776f, + 1.13f, -0.0f, 0.164f, 0.775f, 0.775f, 1.129f, -0.0f, 0.152f, 0.774f, 0.774f, 1.126f, -0.0f, 0.141f, 0.774f, 0.773f, 1.122f, -0.0f, 0.13f, 0.774f, 0.772f, + 1.118f, -0.0f, 0.118f, 0.773f, 0.772f, 1.113f, -0.0f, 0.108f, 0.773f, 0.773f, 1.107f, -0.0f, 0.097f, 0.773f, 0.774f, 1.102f, -0.0f, 0.087f, 0.772f, 0.773f, + 1.095f, -0.0f, 0.077f, 0.772f, 0.773f, 1.088f, -0.0f, 0.067f, 0.771f, 0.772f, 1.081f, -0.0f, 0.057f, 0.771f, 0.773f, 1.073f, -0.0f, 0.048f, 0.77f, 0.772f, + 1.066f, -0.0f, 0.038f, 0.769f, 0.767f, 1.058f, -0.0f, 0.029f, 0.768f, 0.766f, 1.05f, -0.0f, 0.019f, 0.768f, 0.765f, 1.041f, -0.0f, 0.011f, 0.767f, 0.765f, + 1.032f, -0.0f, 0.003f, 0.767f, 0.766f, 1.023f, -0.0f, -0.004f, 0.766f, 0.765f, 1.013f, -0.0f, -0.011f, 0.766f, 0.765f, 1.003f, -0.0f, -0.019f, 0.765f, 0.766f, + 0.993f, -0.0f, -0.026f, 0.765f, 0.765f, 0.983f, -0.0f, -0.034f, 0.764f, 0.765f, 0.972f, -0.0f, -0.041f, 0.762f, 0.765f, 0.962f, -0.0f, -0.048f, 0.761f, 0.765f, + 0.951f, -0.0f, -0.055f, 0.759f, 0.762f, 0.94f, -0.0f, -0.063f, 0.756f, 0.761f, 0.929f, -0.0f, -0.07f, 0.754f, 0.755f, 0.918f, -0.0f, -0.078f, 0.751f, 0.751f, + 0.907f, -0.0f, -0.085f, 0.748f, 0.747f, 0.896f, -0.0f, -0.092f, 0.745f, 0.744f, 0.884f, -0.0f, -0.099f, 0.742f, 0.742f, 0.873f, -0.0f, -0.105f, 0.739f, 0.738f, + 0.861f, -0.0f, -0.11f, 0.736f, 0.737f, 0.849f, 0.0f, -0.115f, 0.733f, 0.731f, 0.836f, 0.0f, -0.119f, 0.73f, 0.73f, 0.823f, 0.0f, -0.124f, 0.728f, 0.727f, + 0.81f, 0.0f, -0.128f, 0.725f, 0.725f, 0.796f, 0.0f, -0.132f, 0.723f, 0.723f, 0.783f, 0.0f, -0.136f, 0.72f, 0.719f, 0.77f, 0.0f, -0.141f, 0.718f, 0.717f, + 0.756f, 0.0f, -0.145f, 0.715f, 0.712f, 0.742f, 0.0f, -0.15f, 0.713f, 0.708f, 0.728f, 0.0f, -0.152f, 0.711f, 0.707f, 0.713f, 0.0f, -0.155f, 0.709f, 0.706f, + 0.699f, 0.0f, -0.156f, 0.706f, 0.706f, 0.684f, 0.0f, -0.158f, 0.704f, 0.705f, 0.67f, 0.0f, -0.159f, 0.702f, 0.705f, 0.656f, 0.0f, -0.16f, 0.7f, 0.704f, + 0.642f, 0.0f, -0.161f, 0.698f, 0.702f, 0.628f, 0.0f, -0.161f, 0.695f, 0.698f, 0.614f, 0.0f, -0.162f, 0.693f, 0.695f, 0.6f, 0.0f, -0.162f, 0.691f, 0.691f, + 0.587f, 0.0f, -0.162f, 0.688f, 0.686f, 0.574f, 0.0f, -0.162f, 0.686f, 0.685f, 0.561f, 0.0f, -0.161f, 0.683f, 0.683f, 0.548f, 0.0f, -0.161f, 0.681f, 0.683f, + 0.535f, 0.0f, -0.161f, 0.678f, 0.678f, 0.523f, 0.0f, -0.16f, 0.676f, 0.676f, 0.512f, 0.0f, -0.16f, 0.673f, 0.674f, 0.501f, 0.0f, -0.16f, 0.671f, 0.67f, + 0.49f, 0.0f, -0.16f, 0.668f, 0.668f, 0.48f, 0.0f, -0.161f, 0.666f, 0.663f, 0.469f, 0.0f, -0.162f, 0.665f, 0.66f, 0.458f, 0.0f, -0.165f, 0.663f, 0.66f, + 0.447f, 0.0f, -0.167f, 0.662f, 0.659f, 0.437f, 0.0f, -0.171f, 0.661f, 0.659f, 0.426f, 0.0f, -0.175f, 0.66f, 0.659f, 0.415f, 0.0f, -0.18f, 0.66f, 0.659f, + 0.404f, 0.0f, -0.185f, 0.659f, 0.659f, 0.393f, 0.0f, -0.191f, 0.659f, 0.657f, 0.383f, 0.0f, -0.196f, 0.659f, 0.657f, 0.373f, 0.0f, -0.202f, 0.658f, 0.659f, + 0.363f, -0.0f, -0.208f, 0.658f, 0.658f, 0.353f, -0.0f, -0.215f, 0.658f, 0.659f, 0.344f, -0.0f, -0.223f, 0.658f, 0.659f, 0.336f, -0.0f, -0.23f, 0.658f, 0.659f, + 0.327f, -0.0f, -0.238f, 0.658f, 0.658f, 0.319f, -0.0f, -0.245f, 0.657f, 0.657f, 0.312f, -0.0f, -0.253f, 0.657f, 0.656f, 0.305f, -0.0f, -0.261f, 0.656f, 0.658f, + 0.299f, -0.0f, -0.269f, 0.655f, 0.658f, 0.293f, 0.0f, -0.278f, 0.653f, 0.657f, 0.288f, 0.0f, -0.287f, 0.65f, 0.657f, 0.283f, 0.0f, -0.295f, 0.646f, 0.656f, + 0.279f, 0.0f, -0.304f, 0.642f, 0.655f, 0.275f, 0.0f, -0.313f, 0.637f, 0.642f, 0.271f, 0.0f, -0.322f, 0.633f, 0.637f, 0.268f, 0.0f, -0.331f, 0.628f, 0.609f, + 0.265f, 0.0f, -0.341f, 0.624f, 0.607f, 0.263f, 0.0f, -0.35f, 0.62f, 0.608f, 0.261f, 0.0f, -0.359f, 0.617f, 0.608f, 0.259f, 0.0f, -0.369f, 0.614f, 0.607f, + 0.258f, 0.0f, -0.379f, 0.612f, 0.606f, 0.257f, 0.0f, -0.389f, 0.61f, 0.606f, 0.258f, 0.0f, -0.399f, 0.609f, 0.605f, 0.258f, 0.0f, -0.41f, 0.608f, 0.604f, + 0.26f, 0.0f, -0.421f, 0.608f, 0.606f, 0.263f, 0.0f, -0.431f, 0.607f, 0.606f, 0.266f, 0.0f, -0.441f, 0.607f, 0.606f, 0.27f, 0.0f, -0.452f, 0.606f, 0.607f, + 0.274f, 0.0f, -0.463f, 0.606f, 0.607f, 0.279f, 0.0f, -0.475f, 0.605f, 0.607f, 0.283f, 0.0f, -0.487f, 0.604f, 0.607f, 0.288f, 0.0f, -0.498f, 0.603f, 0.607f, + 0.293f, 0.0f, -0.511f, 0.601f, 0.607f, 0.297f, 0.0f, -0.523f, 0.598f, 0.606f, 0.301f, 0.0f, -0.536f, 0.595f, 0.605f, 0.305f, 0.0f, -0.549f, 0.591f, 0.602f, + 0.309f, 0.0f, -0.562f, 0.588f, 0.597f, 0.312f, 0.0f, -0.576f, 0.583f, 0.585f, 0.315f, 0.0f, -0.59f, 0.579f, 0.577f, 0.318f, 0.0f, -0.604f, 0.574f, 0.576f, + 0.321f, 0.0f, -0.618f, 0.569f, 0.57f, 0.323f, 0.0f, -0.633f, 0.564f, 0.564f, 0.326f, 0.0f, -0.647f, 0.559f, 0.554f, 0.328f, 0.0f, -0.663f, 0.555f, 0.549f, + 0.33f, 0.0f, -0.678f, 0.551f, 0.546f, 0.332f, 0.0f, -0.693f, 0.547f, 0.543f, 0.334f, 0.0f, -0.709f, 0.544f, 0.543f, 0.336f, 0.0f, -0.726f, 0.541f, 0.541f, + 0.338f, 0.0f, -0.742f, 0.538f, 0.54f, 0.338f, 0.0f, -0.758f, 0.536f, 0.538f, 0.338f, 0.0f, -0.773f, 0.534f, 0.53f, 0.337f, 0.0f, -0.787f, 0.532f, 0.528f, + 0.337f, 0.0f, -0.801f, 0.53f, 0.528f, 0.336f, 0.0f, -0.814f, 0.529f, 0.528f, 0.334f, 0.0f, -0.827f, 0.527f, 0.528f, 0.333f, 0.0f, -0.84f, 0.525f, 0.529f, + 0.331f, 0.0f, -0.853f, 0.523f, 0.529f, 0.328f, 0.0f, -0.866f, 0.521f, 0.528f, 0.324f, 0.0f, -0.877f, 0.519f, 0.516f, 0.32f, 0.0f, -0.889f, 0.516f, 0.515f, + 0.315f, 0.0f, -0.9f, 0.513f, 0.515f, 0.31f, 0.0f, -0.91f, 0.51f, 0.514f, 0.304f, 0.0f, -0.921f, 0.507f, 0.513f, 0.297f, 0.0f, -0.931f, 0.505f, 0.507f, + 0.289f, 0.0f, -0.94f, 0.502f, 0.498f, 0.281f, 0.0f, -0.948f, 0.499f, 0.494f, 0.272f, 0.0f, -0.956f, 0.497f, 0.491f, 0.262f, 0.0f, -0.963f, 0.495f, 0.49f, + 0.253f, 0.0f, -0.969f, 0.494f, 0.491f, 0.242f, 0.0f, -0.975f, 0.493f, 0.491f, 0.231f, 0.0f, -0.98f, 0.492f, 0.49f, 0.22f, 0.0f, -0.986f, 0.491f, 0.489f, + 0.208f, 0.0f, -0.99f, 0.491f, 0.49f, 0.195f, 0.0f, -0.994f, 0.491f, 0.491f, 0.181f, 0.0f, -0.998f, 0.491f, 0.491f, 0.168f, 0.0f, -1.001f, 0.491f, 0.492f, + 0.154f, 0.0f, -1.005f, 0.491f, 0.492f, 0.141f, 0.0f, -1.008f, 0.492f, 0.492f, 0.126f, 0.0f, -1.01f, 0.492f, 0.492f, 0.112f, 0.0f, -1.011f, 0.492f, 0.492f, + 0.097f, 0.0f, -1.013f, 0.492f, 0.492f, 0.081f, 0.0f, -1.013f, 0.492f, 0.492f, 0.066f, 0.0f, -1.014f, 0.493f, 0.493f, 0.05f, 0.0f, -1.014f, 0.493f, 0.494f, + 0.035f, 0.0f, -1.014f, 0.493f, 0.494f, 0.019f, 0.0f, -1.013f, 0.493f, 0.494f, 0.004f, 0.0f, -1.012f, 0.493f, 0.494f, -0.011f, 0.0f, -1.011f, 0.493f, 0.493f, + -0.026f, 0.0f, -1.01f, 0.492f, 0.493f, -0.041f, 0.0f, -1.008f, 0.492f, 0.492f, -0.056f, 0.0f, -1.006f, 0.492f, 0.492f, -0.07f, 0.0f, -1.004f, 0.491f, 0.492f, + -0.084f, 0.0f, -1.001f, 0.491f, 0.492f, -0.098f, 0.0f, -0.999f, 0.491f, 0.491f, -0.112f, 0.0f, -0.995f, 0.491f, 0.49f, -0.125f, 0.0f, -0.992f, 0.49f, 0.49f, + -0.138f, 0.0f, -0.987f, 0.49f, 0.491f, -0.15f, 0.0f, -0.983f, 0.49f, 0.49f, -0.162f, 0.0f, -0.978f, 0.49f, 0.49f, -0.174f, 0.0f, -0.973f, 0.489f, 0.489f, + -0.185f, 0.0f, -0.967f, 0.489f, 0.488f, -0.196f, 0.0f, -0.961f, 0.489f, 0.489f, -0.207f, 0.0f, -0.955f, 0.489f, 0.489f, -0.218f, 0.0f, -0.949f, 0.489f, 0.49f, + -0.229f, 0.0f, -0.943f, 0.489f, 0.489f, -0.24f, 0.0f, -0.936f, 0.489f, 0.489f, -0.25f, 0.0f, -0.929f, 0.489f, 0.489f, -0.261f, 0.0f, -0.922f, 0.489f, 0.489f, + -0.271f, 0.0f, -0.914f, 0.489f, 0.49f, -0.28f, 0.0f, -0.907f, 0.49f, 0.49f, -0.289f, 0.0f, -0.898f, 0.49f, 0.489f, -0.298f, 0.0f, -0.89f, 0.49f, 0.489f, + -0.306f, 0.0f, -0.882f, 0.49f, 0.49f, -0.314f, 0.0f, -0.875f, 0.491f, 0.489f, -0.322f, 0.0f, -0.866f, 0.492f, 0.489f, -0.328f, 0.0f, -0.857f, 0.492f, 0.489f, + -0.333f, 0.0f, -0.847f, 0.493f, 0.49f, -0.336f, 0.0f, -0.836f, 0.494f, 0.488f, -0.338f, 0.0f, -0.824f, 0.496f, 0.49f, -0.338f, 0.0f, -0.811f, 0.497f, 0.49f, + -0.338f, 0.0f, -0.798f, 0.499f, 0.491f, -0.337f, 0.0f, -0.785f, 0.501f, 0.497f, -0.337f, 0.0f, -0.772f, 0.503f, 0.5f, -0.337f, 0.0f, -0.759f, 0.505f, 0.504f, + -0.336f, -0.0f, -0.746f, 0.507f, 0.505f, -0.336f, -0.0f, -0.733f, 0.51f, 0.51f, -0.335f, -0.0f, -0.719f, 0.512f, 0.513f, -0.334f, -0.0f, -0.706f, 0.515f, 0.515f, + -0.333f, -0.0f, -0.692f, 0.518f, 0.516f, -0.332f, -0.0f, -0.678f, 0.52f, 0.522f, -0.331f, -0.0f, -0.665f, 0.523f, 0.523f, -0.329f, -0.0f, -0.651f, 0.525f, 0.528f, + -0.327f, -0.0f, -0.637f, 0.528f, 0.53f, -0.325f, -0.0f, -0.624f, 0.53f, 0.532f, -0.322f, -0.0f, -0.61f, 0.532f, 0.534f, -0.319f, -0.0f, -0.597f, 0.535f, 0.535f, + -0.316f, -0.0f, -0.584f, 0.537f, 0.538f, -0.313f, -0.0f, -0.57f, 0.539f, 0.54f, -0.31f, -0.0f, -0.557f, 0.541f, 0.542f, -0.307f, -0.0f, -0.544f, 0.542f, 0.545f, + -0.303f, -0.0f, -0.531f, 0.544f, 0.546f, -0.3f, -0.0f, -0.519f, 0.546f, 0.549f, -0.298f, -0.0f, -0.506f, 0.547f, 0.549f, -0.295f, -0.0f, -0.494f, 0.548f, 0.549f, + -0.292f, -0.0f, -0.482f, 0.549f, 0.55f, -0.29f, -0.0f, -0.47f, 0.55f, 0.552f, -0.287f, -0.0f, -0.459f, 0.551f, 0.552f, -0.285f, -0.0f, -0.447f, 0.551f, 0.552f, + -0.284f, -0.0f, -0.436f, 0.552f, 0.552f, -0.282f, -0.0f, -0.425f, 0.552f, 0.553f, -0.281f, -0.0f, -0.413f, 0.553f, 0.553f, -0.28f, -0.0f, -0.402f, 0.553f, 0.553f, + -0.28f, -0.0f, -0.392f, 0.553f, 0.553f, -0.281f, -0.0f, -0.381f, 0.554f, 0.553f, -0.283f, -0.0f, -0.369f, 0.554f, 0.554f, -0.286f, -0.0f, -0.359f, 0.554f, 0.554f, + -0.289f, -0.0f, -0.348f, 0.555f, 0.554f, -0.294f, -0.0f, -0.337f, 0.555f, 0.555f, -0.299f, -0.0f, -0.327f, 0.555f, 0.554f, -0.305f, -0.0f, -0.317f, 0.556f, 0.555f, + -0.312f, -0.0f, -0.307f, 0.556f, 0.555f, -0.319f, -0.0f, -0.297f, 0.556f, 0.557f, -0.326f, 0.0f, -0.287f, 0.557f, 0.558f, -0.334f, 0.0f, -0.278f, 0.557f, 0.557f, + -0.341f, 0.0f, -0.268f, 0.557f, 0.558f, -0.349f, 0.0f, -0.259f, 0.558f, 0.558f, -0.359f, 0.0f, -0.251f, 0.558f, 0.558f, -0.368f, 0.0f, -0.243f, 0.558f, 0.558f, + -0.378f, 0.0f, -0.235f, 0.558f, 0.559f, -0.388f, 0.0f, -0.228f, 0.558f, 0.558f, -0.398f, 0.0f, -0.221f, 0.559f, 0.559f, -0.408f, 0.0f, -0.214f, 0.559f, 0.559f, + -0.418f, 0.0f, -0.208f, 0.559f, 0.559f, -0.427f, 0.0f, -0.202f, 0.559f, 0.558f, -0.436f, 0.0f, -0.196f, 0.559f, 0.559f, -0.445f, 0.0f, -0.191f, 0.559f, 0.559f, + -0.453f, 0.0f, -0.187f, 0.558f, 0.559f, -0.462f, 0.0f, -0.183f, 0.558f, 0.558f, -0.469f, 0.0f, -0.18f, 0.558f, 0.558f, -0.477f, 0.0f, -0.176f, 0.558f, 0.558f, + -0.484f, 0.0f, -0.174f, 0.557f, 0.558f, -0.493f, 0.0f, -0.17f, 0.555f, 0.559f, +}; + + +static const float data1[136 * GP_PRIM_DATABUF_SIZE] = { + -0.369f, 0.0f, -0.048f, 0.065f, 0.065f, -0.378f, 0.0f, -0.046f, 0.239f, 0.293f, -0.383f, 0.0f, -0.044f, 0.316f, 0.339f, -0.39f, 0.0f, -0.041f, 0.348f, 0.355f, + -0.398f, 0.0f, -0.038f, 0.364f, 0.368f, -0.405f, 0.0f, -0.035f, 0.373f, 0.374f, -0.413f, 0.0f, -0.031f, 0.381f, 0.381f, -0.421f, 0.0f, -0.026f, 0.388f, 0.391f, + -0.429f, 0.0f, -0.02f, 0.392f, 0.394f, -0.437f, 0.0f, -0.014f, 0.395f, 0.396f, -0.445f, 0.0f, -0.008f, 0.397f, 0.397f, -0.453f, 0.0f, -0.001f, 0.399f, 0.4f, + -0.461f, 0.0f, 0.007f, 0.401f, 0.401f, -0.468f, -0.0f, 0.016f, 0.404f, 0.404f, -0.474f, 0.0f, 0.023f, 0.406f, 0.407f, -0.479f, 0.0f, 0.03f, 0.409f, 0.409f, + -0.485f, 0.0f, 0.039f, 0.412f, 0.412f, -0.49f, 0.0f, 0.048f, 0.415f, 0.415f, -0.495f, 0.0f, 0.057f, 0.417f, 0.417f, -0.499f, 0.0f, 0.068f, 0.42f, 0.421f, + -0.503f, 0.0f, 0.079f, 0.421f, 0.421f, -0.507f, -0.0f, 0.091f, 0.423f, 0.423f, -0.51f, -0.0f, 0.102f, 0.424f, 0.424f, -0.513f, -0.0f, 0.112f, 0.424f, 0.425f, + -0.515f, -0.0f, 0.123f, 0.425f, 0.425f, -0.517f, -0.0f, 0.135f, 0.425f, 0.425f, -0.518f, -0.0f, 0.146f, 0.426f, 0.425f, -0.519f, -0.0f, 0.158f, 0.426f, 0.425f, + -0.52f, -0.0f, 0.169f, 0.426f, 0.426f, -0.52f, -0.0f, 0.181f, 0.427f, 0.427f, -0.519f, -0.0f, 0.192f, 0.427f, 0.427f, -0.518f, -0.0f, 0.203f, 0.427f, 0.427f, + -0.517f, -0.0f, 0.213f, 0.427f, 0.428f, -0.515f, -0.0f, 0.222f, 0.428f, 0.427f, -0.513f, -0.0f, 0.232f, 0.428f, 0.427f, -0.51f, -0.0f, 0.241f, 0.429f, 0.427f, + -0.508f, -0.0f, 0.25f, 0.43f, 0.428f, -0.505f, -0.0f, 0.259f, 0.431f, 0.431f, -0.501f, -0.0f, 0.267f, 0.431f, 0.432f, -0.497f, -0.0f, 0.276f, 0.432f, 0.433f, + -0.493f, -0.0f, 0.284f, 0.433f, 0.433f, -0.488f, -0.0f, 0.293f, 0.434f, 0.434f, -0.484f, -0.0f, 0.301f, 0.434f, 0.435f, -0.479f, -0.0f, 0.308f, 0.435f, 0.436f, + -0.474f, -0.0f, 0.316f, 0.435f, 0.435f, -0.468f, -0.0f, 0.322f, 0.436f, 0.436f, -0.463f, -0.0f, 0.329f, 0.436f, 0.436f, -0.457f, -0.0f, 0.335f, 0.436f, 0.436f, + -0.451f, -0.0f, 0.341f, 0.437f, 0.436f, -0.445f, -0.0f, 0.347f, 0.438f, 0.437f, -0.438f, -0.0f, 0.352f, 0.44f, 0.437f, -0.432f, -0.0f, 0.357f, 0.442f, 0.441f, + -0.426f, 0.0f, 0.362f, 0.444f, 0.446f, -0.419f, 0.0f, 0.366f, 0.445f, 0.447f, -0.413f, 0.0f, 0.369f, 0.446f, 0.447f, -0.407f, 0.0f, 0.373f, 0.446f, 0.447f, + -0.401f, 0.0f, 0.376f, 0.447f, 0.447f, -0.395f, 0.0f, 0.378f, 0.447f, 0.448f, -0.388f, 0.0f, 0.381f, 0.447f, 0.448f, -0.382f, 0.0f, 0.383f, 0.448f, 0.448f, + -0.375f, 0.0f, 0.384f, 0.448f, 0.448f, -0.369f, 0.0f, 0.386f, 0.448f, 0.448f, -0.362f, 0.0f, 0.387f, 0.448f, 0.448f, -0.355f, 0.0f, 0.388f, 0.448f, 0.448f, + -0.348f, 0.0f, 0.388f, 0.448f, 0.448f, -0.341f, 0.0f, 0.387f, 0.448f, 0.449f, -0.334f, 0.0f, 0.387f, 0.448f, 0.448f, -0.327f, 0.0f, 0.386f, 0.448f, 0.449f, + -0.32f, 0.0f, 0.384f, 0.449f, 0.449f, -0.313f, 0.0f, 0.382f, 0.449f, 0.449f, -0.307f, 0.0f, 0.38f, 0.449f, 0.449f, -0.3f, 0.0f, 0.377f, 0.449f, 0.45f, + -0.294f, 0.0f, 0.375f, 0.45f, 0.45f, -0.288f, -0.0f, 0.372f, 0.45f, 0.45f, -0.282f, -0.0f, 0.368f, 0.45f, 0.451f, -0.276f, -0.0f, 0.365f, 0.45f, 0.451f, + -0.27f, -0.0f, 0.361f, 0.45f, 0.451f, -0.264f, -0.0f, 0.357f, 0.45f, 0.451f, -0.258f, -0.0f, 0.352f, 0.45f, 0.45f, -0.251f, -0.0f, 0.347f, 0.45f, 0.451f, + -0.245f, -0.0f, 0.341f, 0.451f, 0.451f, -0.24f, -0.0f, 0.335f, 0.451f, 0.451f, -0.234f, -0.0f, 0.329f, 0.451f, 0.451f, -0.228f, -0.0f, 0.323f, 0.452f, 0.452f, + -0.223f, -0.0f, 0.316f, 0.452f, 0.453f, -0.218f, -0.0f, 0.309f, 0.452f, 0.453f, -0.213f, -0.0f, 0.301f, 0.453f, 0.453f, -0.208f, -0.0f, 0.294f, 0.453f, 0.453f, + -0.204f, -0.0f, 0.286f, 0.453f, 0.453f, -0.2f, -0.0f, 0.277f, 0.453f, 0.454f, -0.196f, -0.0f, 0.269f, 0.453f, 0.454f, -0.192f, -0.0f, 0.26f, 0.454f, 0.454f, + -0.189f, -0.0f, 0.25f, 0.454f, 0.454f, -0.186f, -0.0f, 0.241f, 0.454f, 0.455f, -0.183f, -0.0f, 0.231f, 0.454f, 0.455f, -0.181f, -0.0f, 0.221f, 0.454f, 0.455f, + -0.179f, -0.0f, 0.209f, 0.455f, 0.455f, -0.177f, -0.0f, 0.197f, 0.455f, 0.455f, -0.176f, -0.0f, 0.184f, 0.455f, 0.455f, -0.176f, -0.0f, 0.171f, 0.455f, 0.456f, + -0.176f, -0.0f, 0.158f, 0.455f, 0.456f, -0.177f, -0.0f, 0.145f, 0.455f, 0.456f, -0.178f, -0.0f, 0.132f, 0.455f, 0.456f, -0.18f, -0.0f, 0.12f, 0.456f, 0.456f, + -0.182f, -0.0f, 0.108f, 0.456f, 0.456f, -0.185f, -0.0f, 0.097f, 0.456f, 0.456f, -0.188f, -0.0f, 0.086f, 0.456f, 0.457f, -0.191f, -0.0f, 0.076f, 0.456f, 0.457f, + -0.194f, -0.0f, 0.067f, 0.457f, 0.457f, -0.198f, -0.0f, 0.058f, 0.457f, 0.457f, -0.202f, -0.0f, 0.05f, 0.457f, 0.457f, -0.206f, -0.0f, 0.042f, 0.457f, 0.457f, + -0.21f, -0.0f, 0.034f, 0.458f, 0.457f, -0.215f, -0.0f, 0.027f, 0.458f, 0.457f, -0.22f, -0.0f, 0.02f, 0.458f, 0.458f, -0.225f, -0.0f, 0.014f, 0.458f, 0.458f, + -0.23f, -0.0f, 0.007f, 0.458f, 0.458f, -0.235f, -0.0f, 0.002f, 0.459f, 0.458f, -0.24f, -0.0f, -0.004f, 0.459f, 0.458f, -0.246f, -0.0f, -0.009f, 0.46f, 0.459f, + -0.251f, 0.0f, -0.013f, 0.464f, 0.463f, -0.257f, 0.0f, -0.018f, 0.467f, 0.468f, -0.262f, 0.0f, -0.022f, 0.469f, 0.469f, -0.268f, 0.0f, -0.026f, 0.471f, 0.47f, + -0.274f, 0.0f, -0.029f, 0.477f, 0.478f, -0.28f, 0.0f, -0.033f, 0.478f, 0.478f, -0.286f, 0.0f, -0.036f, 0.478f, 0.478f, -0.292f, 0.0f, -0.038f, 0.479f, 0.479f, + -0.298f, 0.0f, -0.041f, 0.48f, 0.48f, -0.305f, 0.0f, -0.043f, 0.48f, 0.48f, -0.311f, 0.0f, -0.045f, 0.482f, 0.482f, -0.318f, 0.0f, -0.047f, 0.482f, 0.482f, + -0.324f, 0.0f, -0.048f, 0.482f, 0.482f, -0.331f, 0.0f, -0.049f, 0.48f, 0.482f, -0.336f, 0.0f, -0.05f, 0.457f, 0.485f, -0.344f, 0.0f, -0.05f, 0.32f, 0.32f, +}; + +static const float data2[2 * GP_PRIM_DATABUF_SIZE] = { + -0.512f, 0.0f, -0.168f, 0.545f, 0.557f, -0.521f, 0.0f, -0.167f, 0.535f, 0.558f, +}; + +static const float data3[1 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.186f, 0.0f, 0.003f, +}; + +static const float data4[1 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.186f, 0.02f, 0.02f, +}; + +static const float data5[48 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.187f, 0.066f, 0.066f, -1.013f, 0.0f, 0.2f, 0.222f, 0.356f, -1.01f, 0.0f, 0.208f, 0.295f, 0.404f, -1.006f, 0.0f, 0.218f, 0.354f, 0.431f, + -1.001f, 0.0f, 0.226f, 0.392f, 0.445f, -0.994f, 0.0f, 0.233f, 0.418f, 0.453f, -0.987f, 0.0f, 0.238f, 0.437f, 0.457f, -0.979f, 0.0f, 0.242f, 0.45f, 0.47f, + -0.97f, 0.0f, 0.245f, 0.459f, 0.473f, -0.96f, -0.0f, 0.246f, 0.465f, 0.474f, -0.951f, -0.0f, 0.245f, 0.469f, 0.475f, -0.942f, 0.0f, 0.242f, 0.471f, 0.473f, + -0.932f, 0.0f, 0.239f, 0.472f, 0.474f, -0.924f, 0.0f, 0.234f, 0.471f, 0.474f, -0.915f, 0.0f, 0.228f, 0.469f, 0.474f, -0.906f, 0.0f, 0.22f, 0.464f, 0.47f, + -0.898f, 0.0f, 0.212f, 0.458f, 0.46f, -0.89f, 0.0f, 0.203f, 0.451f, 0.453f, -0.882f, 0.0f, 0.193f, 0.443f, 0.443f, -0.875f, 0.0f, 0.182f, 0.435f, 0.437f, + -0.869f, 0.0f, 0.172f, 0.426f, 0.428f, -0.863f, 0.0f, 0.161f, 0.417f, 0.415f, -0.858f, 0.0f, 0.148f, 0.409f, 0.41f, -0.854f, 0.0f, 0.137f, 0.399f, 0.399f, + -0.85f, 0.0f, 0.126f, 0.39f, 0.392f, -0.847f, 0.0f, 0.116f, 0.379f, 0.386f, -0.846f, 0.0f, 0.109f, 0.369f, 0.371f, -0.846f, 0.0f, 0.104f, 0.361f, 0.357f, + -0.847f, 0.0f, 0.101f, 0.355f, 0.339f, -0.849f, 0.0f, 0.101f, 0.353f, 0.334f, -0.853f, 0.0f, 0.103f, 0.354f, 0.345f, -0.859f, 0.0f, 0.108f, 0.357f, 0.35f, + -0.865f, 0.0f, 0.116f, 0.363f, 0.365f, -0.873f, 0.0f, 0.126f, 0.369f, 0.375f, -0.881f, 0.0f, 0.137f, 0.375f, 0.379f, -0.89f, 0.0f, 0.149f, 0.381f, 0.38f, + -0.899f, 0.0f, 0.159f, 0.387f, 0.385f, -0.908f, 0.0f, 0.168f, 0.394f, 0.394f, -0.919f, 0.0f, 0.177f, 0.401f, 0.398f, -0.932f, 0.0f, 0.184f, 0.409f, 0.404f, + -0.945f, 0.0f, 0.191f, 0.418f, 0.415f, -0.958f, 0.0f, 0.195f, 0.427f, 0.431f, -0.969f, 0.0f, 0.197f, 0.434f, 0.443f, -0.979f, 0.0f, 0.197f, 0.436f, 0.445f, + -0.987f, 0.0f, 0.195f, 0.428f, 0.463f, -0.995f, 0.0f, 0.192f, 0.398f, 0.46f, -1.001f, 0.0f, 0.189f, 0.345f, 0.465f, -1.01f, 0.0f, 0.183f, 0.236f, 0.236f, +}; + +static const float data6[47 * GP_PRIM_DATABUF_SIZE] = { + 0.022f, 0.0f, -0.353f, 0.125f, 0.125f, 0.012f, 0.0f, -0.352f, 0.175f, 0.288f, 0.004f, 0.0f, -0.352f, 0.206f, 0.313f, -0.006f, 0.0f, -0.352f, 0.241f, 0.323f, + -0.017f, 0.0f, -0.352f, 0.27f, 0.33f, -0.029f, 0.0f, -0.351f, 0.295f, 0.334f, -0.041f, 0.0f, -0.349f, 0.314f, 0.337f, -0.052f, 0.0f, -0.344f, 0.327f, 0.341f, + -0.063f, 0.0f, -0.337f, 0.336f, 0.344f, -0.072f, 0.0f, -0.329f, 0.341f, 0.345f, -0.081f, 0.0f, -0.32f, 0.345f, 0.345f, -0.088f, 0.0f, -0.311f, 0.348f, 0.345f, + -0.093f, 0.0f, -0.303f, 0.352f, 0.347f, -0.098f, 0.0f, -0.295f, 0.356f, 0.352f, -0.101f, 0.0f, -0.287f, 0.361f, 0.357f, -0.102f, 0.0f, -0.279f, 0.367f, 0.364f, + -0.103f, 0.0f, -0.271f, 0.373f, 0.378f, -0.102f, 0.0f, -0.263f, 0.379f, 0.382f, -0.1f, 0.0f, -0.255f, 0.383f, 0.389f, -0.098f, 0.0f, -0.247f, 0.387f, 0.391f, + -0.094f, 0.0f, -0.24f, 0.389f, 0.393f, -0.09f, 0.0f, -0.233f, 0.391f, 0.393f, -0.086f, 0.0f, -0.227f, 0.392f, 0.393f, -0.082f, 0.0f, -0.222f, 0.393f, 0.393f, + -0.078f, 0.0f, -0.219f, 0.394f, 0.393f, -0.075f, 0.0f, -0.217f, 0.397f, 0.393f, -0.072f, 0.0f, -0.217f, 0.4f, 0.393f, -0.07f, 0.0f, -0.219f, 0.402f, 0.408f, + -0.069f, 0.0f, -0.222f, 0.404f, 0.408f, -0.069f, 0.0f, -0.228f, 0.406f, 0.409f, -0.069f, 0.0f, -0.234f, 0.407f, 0.409f, -0.07f, 0.0f, -0.241f, 0.408f, 0.409f, + -0.07f, 0.0f, -0.248f, 0.408f, 0.409f, -0.07f, 0.0f, -0.256f, 0.409f, 0.409f, -0.07f, 0.0f, -0.263f, 0.409f, 0.41f, -0.069f, 0.0f, -0.271f, 0.41f, 0.411f, + -0.068f, 0.0f, -0.279f, 0.41f, 0.411f, -0.065f, 0.0f, -0.287f, 0.41f, 0.411f, -0.062f, 0.0f, -0.295f, 0.409f, 0.411f, -0.057f, 0.0f, -0.303f, 0.409f, 0.409f, + -0.052f, 0.0f, -0.31f, 0.408f, 0.409f, -0.047f, 0.0f, -0.318f, 0.407f, 0.408f, -0.041f, 0.0f, -0.324f, 0.406f, 0.407f, -0.035f, 0.0f, -0.329f, 0.403f, 0.407f, + -0.027f, 0.0f, -0.333f, 0.4f, 0.408f, -0.021f, 0.0f, -0.336f, 0.398f, 0.403f, -0.012f, 0.0f, -0.339f, 0.393f, 0.393f, +}; + +static const float data7[162 * GP_PRIM_DATABUF_SIZE] = { + -0.291f, 0.0f, -0.34f, 0.093f, 0.093f, -0.289f, -0.0f, -0.35f, 0.149f, 0.176f, -0.287f, -0.0f, -0.357f, 0.182f, 0.242f, -0.284f, -0.0f, -0.365f, 0.215f, 0.257f, + -0.281f, -0.0f, -0.374f, 0.242f, 0.266f, -0.278f, -0.0f, -0.384f, 0.266f, 0.287f, -0.275f, -0.0f, -0.394f, 0.285f, 0.304f, -0.271f, 0.0f, -0.405f, 0.302f, 0.316f, + -0.267f, 0.0f, -0.417f, 0.317f, 0.326f, -0.263f, 0.0f, -0.429f, 0.33f, 0.337f, -0.259f, 0.0f, -0.442f, 0.342f, 0.346f, -0.256f, 0.0f, -0.454f, 0.354f, 0.351f, + -0.253f, 0.0f, -0.467f, 0.365f, 0.362f, -0.251f, 0.0f, -0.48f, 0.376f, 0.38f, -0.249f, -0.0f, -0.493f, 0.386f, 0.391f, -0.247f, -0.0f, -0.505f, 0.394f, 0.396f, + -0.246f, -0.0f, -0.518f, 0.401f, 0.405f, -0.245f, 0.0f, -0.53f, 0.408f, 0.409f, -0.245f, 0.0f, -0.542f, 0.415f, 0.413f, -0.245f, 0.0f, -0.554f, 0.421f, 0.42f, + -0.245f, 0.0f, -0.565f, 0.426f, 0.43f, -0.246f, 0.0f, -0.575f, 0.43f, 0.433f, -0.246f, -0.0f, -0.585f, 0.432f, 0.435f, -0.247f, -0.0f, -0.594f, 0.434f, 0.436f, + -0.247f, -0.0f, -0.603f, 0.435f, 0.436f, -0.248f, -0.0f, -0.612f, 0.436f, 0.436f, -0.25f, -0.0f, -0.621f, 0.437f, 0.438f, -0.252f, -0.0f, -0.631f, 0.437f, 0.438f, + -0.254f, -0.0f, -0.642f, 0.438f, 0.438f, -0.255f, 0.0f, -0.653f, 0.438f, 0.438f, -0.258f, 0.0f, -0.664f, 0.438f, 0.439f, -0.26f, 0.0f, -0.674f, 0.439f, 0.439f, + -0.261f, 0.0f, -0.685f, 0.439f, 0.439f, -0.262f, 0.0f, -0.696f, 0.439f, 0.439f, -0.264f, 0.0f, -0.706f, 0.439f, 0.439f, -0.265f, 0.0f, -0.717f, 0.439f, 0.439f, + -0.265f, 0.0f, -0.727f, 0.438f, 0.439f, -0.266f, 0.0f, -0.738f, 0.437f, 0.439f, -0.266f, 0.0f, -0.749f, 0.435f, 0.438f, -0.266f, 0.0f, -0.76f, 0.433f, 0.433f, + -0.265f, 0.0f, -0.771f, 0.431f, 0.428f, -0.265f, 0.0f, -0.781f, 0.43f, 0.428f, -0.263f, 0.0f, -0.792f, 0.429f, 0.428f, -0.26f, 0.0f, -0.802f, 0.428f, 0.429f, + -0.257f, 0.0f, -0.812f, 0.426f, 0.427f, -0.254f, 0.0f, -0.821f, 0.423f, 0.426f, -0.25f, 0.0f, -0.829f, 0.421f, 0.42f, -0.247f, 0.0f, -0.837f, 0.418f, 0.416f, + -0.242f, 0.0f, -0.844f, 0.417f, 0.415f, -0.238f, 0.0f, -0.85f, 0.415f, 0.413f, -0.234f, 0.0f, -0.857f, 0.415f, 0.413f, -0.229f, 0.0f, -0.864f, 0.414f, 0.413f, + -0.224f, 0.0f, -0.87f, 0.414f, 0.413f, -0.219f, 0.0f, -0.877f, 0.414f, 0.414f, -0.214f, 0.0f, -0.883f, 0.414f, 0.413f, -0.208f, 0.0f, -0.89f, 0.413f, 0.413f, + -0.203f, 0.0f, -0.897f, 0.413f, 0.413f, -0.197f, 0.0f, -0.903f, 0.413f, 0.413f, -0.191f, 0.0f, -0.909f, 0.413f, 0.413f, -0.186f, 0.0f, -0.914f, 0.413f, 0.413f, + -0.181f, 0.0f, -0.92f, 0.413f, 0.413f, -0.175f, -0.0f, -0.925f, 0.413f, 0.413f, -0.17f, -0.0f, -0.931f, 0.413f, 0.413f, -0.164f, -0.0f, -0.936f, 0.413f, 0.413f, + -0.159f, -0.0f, -0.942f, 0.413f, 0.413f, -0.152f, -0.0f, -0.948f, 0.413f, 0.413f, -0.145f, -0.0f, -0.955f, 0.413f, 0.413f, -0.137f, -0.0f, -0.961f, 0.414f, 0.413f, + -0.13f, -0.0f, -0.967f, 0.414f, 0.413f, -0.122f, -0.0f, -0.974f, 0.414f, 0.414f, -0.114f, -0.0f, -0.979f, 0.414f, 0.413f, -0.106f, -0.0f, -0.985f, 0.414f, 0.413f, + -0.098f, -0.0f, -0.989f, 0.414f, 0.414f, -0.091f, -0.0f, -0.993f, 0.414f, 0.413f, -0.083f, -0.0f, -0.997f, 0.414f, 0.414f, -0.075f, -0.0f, -0.999f, 0.414f, 0.414f, + -0.066f, -0.0f, -1.001f, 0.414f, 0.414f, -0.057f, -0.0f, -1.003f, 0.414f, 0.413f, -0.046f, -0.0f, -1.006f, 0.414f, 0.413f, -0.038f, -0.0f, -1.008f, 0.414f, 0.413f, + -0.031f, -0.0f, -1.009f, 0.421f, 0.413f, -0.036f, -0.0f, -1.008f, 0.423f, 0.424f, -0.045f, -0.0f, -1.006f, 0.425f, 0.425f, -0.054f, -0.0f, -1.005f, 0.425f, 0.425f, + -0.064f, -0.0f, -1.005f, 0.425f, 0.425f, -0.073f, -0.0f, -1.004f, 0.425f, 0.425f, -0.084f, -0.0f, -1.003f, 0.425f, 0.425f, -0.095f, -0.0f, -1.001f, 0.424f, 0.424f, + -0.105f, -0.0f, -0.997f, 0.423f, 0.424f, -0.116f, -0.0f, -0.994f, 0.422f, 0.422f, -0.127f, -0.0f, -0.991f, 0.421f, 0.419f, -0.137f, -0.0f, -0.987f, 0.42f, 0.419f, + -0.148f, -0.0f, -0.983f, 0.42f, 0.419f, -0.158f, -0.0f, -0.98f, 0.42f, 0.419f, -0.167f, -0.0f, -0.976f, 0.419f, 0.419f, -0.176f, -0.0f, -0.973f, 0.419f, 0.419f, + -0.184f, -0.0f, -0.969f, 0.419f, 0.419f, -0.192f, -0.0f, -0.966f, 0.419f, 0.418f, -0.2f, 0.0f, -0.962f, 0.419f, 0.418f, -0.207f, 0.0f, -0.957f, 0.419f, 0.419f, + -0.215f, 0.0f, -0.953f, 0.419f, 0.418f, -0.223f, 0.0f, -0.948f, 0.419f, 0.419f, -0.231f, 0.0f, -0.944f, 0.419f, 0.419f, -0.239f, 0.0f, -0.939f, 0.419f, 0.419f, + -0.247f, 0.0f, -0.934f, 0.419f, 0.419f, -0.255f, 0.0f, -0.929f, 0.419f, 0.419f, -0.262f, 0.0f, -0.924f, 0.419f, 0.419f, -0.269f, 0.0f, -0.919f, 0.419f, 0.418f, + -0.275f, 0.0f, -0.914f, 0.419f, 0.419f, -0.281f, 0.0f, -0.909f, 0.419f, 0.418f, -0.287f, 0.0f, -0.904f, 0.419f, 0.418f, -0.293f, 0.0f, -0.899f, 0.419f, 0.418f, + -0.299f, 0.0f, -0.894f, 0.42f, 0.419f, -0.304f, 0.0f, -0.888f, 0.421f, 0.42f, -0.311f, 0.0f, -0.882f, 0.423f, 0.422f, -0.317f, 0.0f, -0.876f, 0.424f, 0.424f, + -0.322f, 0.0f, -0.869f, 0.426f, 0.426f, -0.328f, 0.0f, -0.861f, 0.427f, 0.427f, -0.332f, 0.0f, -0.853f, 0.429f, 0.429f, -0.336f, 0.0f, -0.843f, 0.43f, 0.429f, + -0.339f, 0.0f, -0.834f, 0.432f, 0.431f, -0.341f, 0.0f, -0.821f, 0.435f, 0.434f, -0.342f, 0.0f, -0.809f, 0.438f, 0.439f, -0.343f, 0.0f, -0.796f, 0.44f, 0.44f, + -0.343f, 0.0f, -0.783f, 0.442f, 0.442f, -0.343f, 0.0f, -0.772f, 0.446f, 0.445f, -0.342f, 0.0f, -0.76f, 0.45f, 0.45f, -0.342f, 0.0f, -0.748f, 0.454f, 0.455f, + -0.34f, 0.0f, -0.735f, 0.457f, 0.457f, -0.339f, 0.0f, -0.723f, 0.46f, 0.46f, -0.338f, 0.0f, -0.711f, 0.463f, 0.464f, -0.336f, 0.0f, -0.7f, 0.465f, 0.465f, + -0.335f, 0.0f, -0.688f, 0.466f, 0.466f, -0.332f, 0.0f, -0.676f, 0.467f, 0.467f, -0.331f, 0.0f, -0.664f, 0.467f, 0.467f, -0.33f, 0.0f, -0.651f, 0.467f, 0.467f, + -0.328f, 0.0f, -0.638f, 0.467f, 0.467f, -0.325f, 0.0f, -0.625f, 0.467f, 0.467f, -0.323f, 0.0f, -0.614f, 0.467f, 0.467f, -0.321f, 0.0f, -0.603f, 0.467f, 0.466f, + -0.318f, 0.0f, -0.592f, 0.467f, 0.466f, -0.315f, 0.0f, -0.581f, 0.467f, 0.466f, -0.313f, 0.0f, -0.569f, 0.467f, 0.467f, -0.311f, -0.0f, -0.557f, 0.467f, 0.467f, + -0.309f, -0.0f, -0.543f, 0.467f, 0.467f, -0.306f, -0.0f, -0.531f, 0.467f, 0.467f, -0.303f, -0.0f, -0.519f, 0.467f, 0.467f, -0.301f, -0.0f, -0.507f, 0.467f, 0.468f, + -0.299f, -0.0f, -0.497f, 0.467f, 0.467f, -0.297f, -0.0f, -0.487f, 0.467f, 0.467f, -0.295f, 0.0f, -0.476f, 0.465f, 0.467f, -0.293f, 0.0f, -0.466f, 0.463f, 0.467f, + -0.292f, 0.0f, -0.456f, 0.46f, 0.466f, -0.291f, 0.0f, -0.445f, 0.455f, 0.459f, -0.29f, 0.0f, -0.435f, 0.449f, 0.457f, -0.29f, 0.0f, -0.424f, 0.44f, 0.448f, + -0.29f, 0.0f, -0.413f, 0.43f, 0.44f, -0.29f, 0.0f, -0.403f, 0.418f, 0.437f, -0.29f, -0.0f, -0.393f, 0.404f, 0.415f, -0.291f, -0.0f, -0.384f, 0.388f, 0.393f, + -0.29f, -0.0f, -0.376f, 0.374f, 0.379f, -0.29f, -0.0f, -0.365f, 0.352f, 0.352f, +}; + +static const float data8[55 * GP_PRIM_DATABUF_SIZE] = { + 0.781f, 0.0f, 0.098f, 0.109f, 0.109f, 0.784f, 0.0f, 0.105f, 0.202f, 0.338f, 0.785f, 0.0f, 0.108f, 0.254f, 0.369f, 0.787f, 0.0f, 0.113f, 0.306f, 0.382f, + 0.787f, 0.0f, 0.118f, 0.344f, 0.392f, 0.789f, 0.0f, 0.123f, 0.372f, 0.401f, 0.79f, 0.0f, 0.128f, 0.392f, 0.41f, 0.792f, 0.0f, 0.135f, 0.406f, 0.42f, + 0.794f, 0.0f, 0.142f, 0.416f, 0.424f, 0.797f, 0.0f, 0.152f, 0.424f, 0.428f, 0.801f, 0.0f, 0.161f, 0.429f, 0.431f, 0.807f, 0.0f, 0.172f, 0.432f, 0.435f, + 0.814f, 0.0f, 0.182f, 0.435f, 0.438f, 0.821f, 0.0f, 0.19f, 0.437f, 0.439f, 0.828f, 0.0f, 0.197f, 0.439f, 0.44f, 0.836f, 0.0f, 0.204f, 0.44f, 0.441f, + 0.845f, -0.0f, 0.211f, 0.44f, 0.441f, 0.853f, -0.0f, 0.215f, 0.441f, 0.441f, 0.861f, -0.0f, 0.219f, 0.441f, 0.441f, 0.87f, -0.0f, 0.222f, 0.441f, 0.442f, + 0.878f, -0.0f, 0.224f, 0.441f, 0.442f, 0.886f, -0.0f, 0.226f, 0.441f, 0.442f, 0.895f, -0.0f, 0.227f, 0.44f, 0.442f, 0.903f, 0.0f, 0.226f, 0.439f, 0.441f, + 0.911f, 0.0f, 0.225f, 0.436f, 0.441f, 0.919f, 0.0f, 0.224f, 0.432f, 0.441f, 0.927f, 0.0f, 0.221f, 0.425f, 0.436f, 0.934f, 0.0f, 0.218f, 0.415f, 0.429f, + 0.94f, 0.0f, 0.215f, 0.404f, 0.406f, 0.944f, 0.0f, 0.211f, 0.393f, 0.389f, 0.947f, 0.0f, 0.208f, 0.384f, 0.378f, 0.948f, 0.0f, 0.204f, 0.376f, 0.371f, + 0.946f, 0.0f, 0.2f, 0.369f, 0.364f, 0.943f, 0.0f, 0.196f, 0.365f, 0.358f, 0.937f, 0.0f, 0.193f, 0.364f, 0.354f, 0.931f, 0.0f, 0.189f, 0.366f, 0.359f, + 0.925f, 0.0f, 0.186f, 0.37f, 0.367f, 0.917f, 0.0f, 0.182f, 0.374f, 0.375f, 0.908f, 0.0f, 0.177f, 0.378f, 0.382f, 0.899f, 0.0f, 0.172f, 0.381f, 0.384f, + 0.889f, 0.0f, 0.167f, 0.384f, 0.385f, 0.876f, 0.0f, 0.163f, 0.387f, 0.387f, 0.864f, 0.0f, 0.156f, 0.39f, 0.388f, 0.852f, 0.0f, 0.15f, 0.393f, 0.39f, + 0.841f, 0.0f, 0.144f, 0.396f, 0.396f, 0.832f, 0.0f, 0.138f, 0.399f, 0.401f, 0.826f, 0.0f, 0.133f, 0.401f, 0.404f, 0.82f, 0.0f, 0.127f, 0.403f, 0.405f, + 0.816f, 0.0f, 0.122f, 0.403f, 0.407f, 0.812f, 0.0f, 0.119f, 0.399f, 0.406f, 0.808f, 0.0f, 0.115f, 0.39f, 0.405f, 0.805f, 0.0f, 0.113f, 0.371f, 0.407f, + 0.801f, 0.0f, 0.111f, 0.341f, 0.407f, 0.799f, 0.0f, 0.109f, 0.309f, 0.405f, 0.795f, 0.0f, 0.106f, 0.255f, 0.255f, +}; + +static const float data9[70 * GP_PRIM_DATABUF_SIZE] = { + 0.819f, -0.0f, 0.325f, 0.109f, 0.109f, 0.829f, -0.0f, 0.328f, 0.258f, 0.403f, 0.835f, -0.0f, 0.329f, 0.327f, 0.428f, 0.843f, -0.0f, 0.331f, 0.383f, 0.452f, + 0.851f, -0.0f, 0.332f, 0.419f, 0.465f, 0.861f, -0.0f, 0.334f, 0.444f, 0.473f, 0.87f, -0.0f, 0.336f, 0.461f, 0.48f, 0.881f, -0.0f, 0.337f, 0.473f, 0.486f, + 0.892f, -0.0f, 0.339f, 0.482f, 0.496f, 0.904f, -0.0f, 0.341f, 0.489f, 0.501f, 0.917f, -0.0f, 0.342f, 0.494f, 0.503f, 0.931f, -0.0f, 0.342f, 0.498f, 0.505f, + 0.945f, -0.0f, 0.342f, 0.501f, 0.505f, 0.958f, -0.0f, 0.342f, 0.503f, 0.506f, 0.971f, -0.0f, 0.341f, 0.505f, 0.506f, 0.984f, -0.0f, 0.341f, 0.506f, 0.506f, + 0.997f, -0.0f, 0.339f, 0.507f, 0.508f, 1.009f, -0.0f, 0.337f, 0.507f, 0.507f, 1.021f, -0.0f, 0.333f, 0.508f, 0.508f, 1.033f, -0.0f, 0.33f, 0.508f, 0.508f, + 1.044f, -0.0f, 0.326f, 0.508f, 0.508f, 1.056f, -0.0f, 0.322f, 0.508f, 0.508f, 1.068f, -0.0f, 0.317f, 0.508f, 0.508f, 1.078f, -0.0f, 0.311f, 0.507f, 0.508f, + 1.089f, -0.0f, 0.304f, 0.506f, 0.508f, 1.099f, 0.0f, 0.294f, 0.503f, 0.506f, 1.107f, 0.0f, 0.287f, 0.498f, 0.506f, 1.113f, 0.0f, 0.28f, 0.49f, 0.505f, + 1.117f, 0.0f, 0.276f, 0.48f, 0.501f, 1.121f, 0.0f, 0.272f, 0.468f, 0.492f, 1.124f, 0.0f, 0.27f, 0.455f, 0.467f, 1.127f, 0.0f, 0.27f, 0.443f, 0.431f, + 1.129f, 0.0f, 0.271f, 0.431f, 0.4f, 1.13f, 0.0f, 0.274f, 0.422f, 0.399f, 1.13f, 0.0f, 0.278f, 0.414f, 0.399f, 1.13f, 0.0f, 0.286f, 0.408f, 0.399f, + 1.128f, 0.0f, 0.295f, 0.404f, 0.399f, 1.124f, 0.0f, 0.305f, 0.402f, 0.399f, 1.119f, 0.0f, 0.316f, 0.403f, 0.4f, 1.113f, -0.0f, 0.327f, 0.405f, 0.401f, + 1.107f, -0.0f, 0.337f, 0.408f, 0.411f, 1.1f, -0.0f, 0.345f, 0.412f, 0.412f, 1.094f, -0.0f, 0.352f, 0.416f, 0.413f, 1.087f, -0.0f, 0.357f, 0.421f, 0.422f, + 1.08f, -0.0f, 0.363f, 0.426f, 0.428f, 1.071f, -0.0f, 0.368f, 0.429f, 0.43f, 1.062f, -0.0f, 0.373f, 0.431f, 0.431f, 1.051f, -0.0f, 0.377f, 0.433f, 0.431f, + 1.039f, -0.0f, 0.381f, 0.436f, 0.437f, 1.026f, -0.0f, 0.383f, 0.438f, 0.44f, 1.013f, -0.0f, 0.384f, 0.44f, 0.44f, 1.0f, -0.0f, 0.385f, 0.441f, 0.443f, + 0.987f, -0.0f, 0.385f, 0.442f, 0.443f, 0.975f, -0.0f, 0.384f, 0.443f, 0.443f, 0.962f, -0.0f, 0.383f, 0.443f, 0.444f, 0.949f, -0.0f, 0.381f, 0.443f, 0.443f, + 0.936f, -0.0f, 0.38f, 0.443f, 0.444f, 0.923f, -0.0f, 0.378f, 0.443f, 0.444f, 0.909f, -0.0f, 0.375f, 0.443f, 0.444f, 0.897f, -0.0f, 0.371f, 0.443f, 0.444f, + 0.886f, -0.0f, 0.367f, 0.443f, 0.443f, 0.876f, -0.0f, 0.363f, 0.443f, 0.444f, 0.868f, -0.0f, 0.359f, 0.443f, 0.442f, 0.86f, -0.0f, 0.355f, 0.442f, 0.443f, + 0.852f, -0.0f, 0.35f, 0.441f, 0.443f, 0.844f, -0.0f, 0.347f, 0.433f, 0.443f, 0.837f, -0.0f, 0.343f, 0.409f, 0.443f, 0.83f, -0.0f, 0.338f, 0.344f, 0.443f, + 0.824f, -0.0f, 0.335f, 0.239f, 0.437f, 0.815f, -0.0f, 0.326f, 0.0f, 0.003f, +}; + +static const float data10[227 * GP_PRIM_DATABUF_SIZE] = { + -0.675f, 0.0f, 0.411f, 0.099f, 0.099f, -0.669f, 0.0f, 0.418f, 0.358f, 0.358f, -0.666f, 0.0f, 0.424f, 0.381f, 0.381f, -0.662f, 0.0f, 0.431f, 0.389f, 0.389f, + -0.658f, 0.0f, 0.438f, 0.393f, 0.393f, -0.649f, 0.0f, 0.448f, 0.404f, 0.404f, -0.641f, 0.0f, 0.458f, 0.419f, 0.419f, -0.632f, 0.0f, 0.468f, 0.431f, 0.434f, + -0.626f, 0.0f, 0.476f, 0.435f, 0.436f, -0.62f, 0.0f, 0.484f, 0.437f, 0.438f, -0.615f, 0.0f, 0.492f, 0.439f, 0.439f, -0.61f, 0.0f, 0.499f, 0.439f, 0.44f, + -0.605f, 0.0f, 0.506f, 0.44f, 0.44f, -0.6f, 0.0f, 0.512f, 0.44f, 0.44f, -0.595f, 0.0f, 0.519f, 0.44f, 0.44f, -0.59f, 0.0f, 0.526f, 0.441f, 0.441f, + -0.584f, 0.0f, 0.532f, 0.441f, 0.441f, -0.579f, 0.0f, 0.539f, 0.441f, 0.441f, -0.573f, 0.0f, 0.545f, 0.442f, 0.442f, -0.566f, 0.0f, 0.551f, 0.443f, 0.443f, + -0.559f, 0.0f, 0.557f, 0.443f, 0.443f, -0.552f, 0.0f, 0.563f, 0.444f, 0.444f, -0.545f, 0.0f, 0.569f, 0.445f, 0.445f, -0.538f, 0.0f, 0.576f, 0.447f, 0.447f, + -0.532f, 0.0f, 0.582f, 0.448f, 0.448f, -0.525f, 0.0f, 0.589f, 0.45f, 0.45f, -0.519f, 0.0f, 0.595f, 0.451f, 0.452f, -0.513f, 0.0f, 0.602f, 0.452f, 0.453f, + -0.506f, 0.0f, 0.608f, 0.453f, 0.453f, -0.5f, 0.0f, 0.613f, 0.453f, 0.454f, -0.493f, 0.0f, 0.619f, 0.453f, 0.454f, -0.486f, 0.0f, 0.625f, 0.453f, 0.454f, + -0.479f, 0.0f, 0.631f, 0.453f, 0.454f, -0.472f, 0.0f, 0.637f, 0.453f, 0.454f, -0.464f, 0.0f, 0.642f, 0.453f, 0.454f, -0.457f, 0.0f, 0.649f, 0.453f, 0.454f, + -0.45f, 0.0f, 0.655f, 0.453f, 0.453f, -0.443f, 0.0f, 0.661f, 0.453f, 0.453f, -0.435f, 0.0f, 0.667f, 0.453f, 0.454f, -0.427f, 0.0f, 0.672f, 0.453f, 0.454f, + -0.419f, 0.0f, 0.677f, 0.453f, 0.454f, -0.411f, 0.0f, 0.682f, 0.453f, 0.453f, -0.403f, 0.0f, 0.688f, 0.453f, 0.453f, -0.395f, 0.0f, 0.692f, 0.453f, 0.454f, + -0.387f, 0.0f, 0.697f, 0.453f, 0.454f, -0.379f, 0.0f, 0.702f, 0.453f, 0.454f, -0.372f, 0.0f, 0.707f, 0.454f, 0.454f, -0.364f, 0.0f, 0.712f, 0.454f, 0.454f, + -0.356f, 0.0f, 0.716f, 0.454f, 0.454f, -0.349f, 0.0f, 0.721f, 0.454f, 0.454f, -0.342f, 0.0f, 0.725f, 0.454f, 0.454f, -0.334f, 0.0f, 0.73f, 0.454f, 0.454f, + -0.326f, 0.0f, 0.733f, 0.454f, 0.454f, -0.318f, 0.0f, 0.737f, 0.454f, 0.454f, -0.31f, 0.0f, 0.74f, 0.454f, 0.454f, -0.301f, 0.0f, 0.743f, 0.454f, 0.454f, + -0.293f, 0.0f, 0.746f, 0.454f, 0.455f, -0.284f, 0.0f, 0.749f, 0.454f, 0.455f, -0.274f, 0.0f, 0.752f, 0.455f, 0.455f, -0.265f, 0.0f, 0.755f, 0.455f, 0.455f, + -0.255f, 0.0f, 0.757f, 0.455f, 0.455f, -0.245f, 0.0f, 0.76f, 0.456f, 0.455f, -0.234f, 0.0f, 0.762f, 0.457f, 0.456f, -0.223f, 0.0f, 0.764f, 0.458f, 0.458f, + -0.212f, 0.0f, 0.766f, 0.459f, 0.46f, -0.201f, 0.0f, 0.769f, 0.461f, 0.46f, -0.189f, 0.0f, 0.771f, 0.462f, 0.461f, -0.177f, 0.0f, 0.773f, 0.464f, 0.463f, + -0.166f, 0.0f, 0.775f, 0.465f, 0.465f, -0.153f, 0.0f, 0.777f, 0.467f, 0.467f, -0.141f, 0.0f, 0.779f, 0.469f, 0.469f, -0.128f, 0.0f, 0.781f, 0.472f, 0.472f, + -0.116f, 0.0f, 0.782f, 0.474f, 0.473f, -0.101f, 0.0f, 0.782f, 0.477f, 0.477f, -0.087f, 0.0f, 0.783f, 0.482f, 0.477f, -0.073f, 0.0f, 0.783f, 0.489f, 0.483f, + -0.059f, 0.0f, 0.783f, 0.497f, 0.5f, -0.046f, 0.0f, 0.784f, 0.503f, 0.509f, -0.033f, 0.0f, 0.784f, 0.508f, 0.51f, -0.022f, 0.0f, 0.784f, 0.51f, 0.512f, + -0.011f, 0.0f, 0.785f, 0.512f, 0.512f, -0.0f, 0.0f, 0.786f, 0.513f, 0.512f, 0.011f, 0.0f, 0.786f, 0.515f, 0.513f, 0.022f, 0.0f, 0.786f, 0.517f, 0.517f, + 0.032f, 0.0f, 0.786f, 0.52f, 0.519f, 0.044f, 0.0f, 0.786f, 0.522f, 0.524f, 0.055f, 0.0f, 0.785f, 0.525f, 0.525f, 0.066f, 0.0f, 0.785f, 0.527f, 0.525f, + 0.076f, 0.0f, 0.784f, 0.53f, 0.53f, 0.086f, 0.0f, 0.783f, 0.532f, 0.533f, 0.097f, 0.0f, 0.782f, 0.535f, 0.534f, 0.108f, 0.0f, 0.782f, 0.538f, 0.541f, + 0.119f, 0.0f, 0.781f, 0.54f, 0.542f, 0.13f, 0.0f, 0.781f, 0.543f, 0.543f, 0.141f, 0.0f, 0.78f, 0.545f, 0.545f, 0.154f, 0.0f, 0.779f, 0.547f, 0.547f, + 0.165f, 0.0f, 0.777f, 0.549f, 0.548f, 0.177f, 0.0f, 0.775f, 0.55f, 0.552f, 0.188f, 0.0f, 0.772f, 0.552f, 0.552f, 0.199f, 0.0f, 0.77f, 0.553f, 0.553f, + 0.209f, 0.0f, 0.767f, 0.554f, 0.554f, 0.218f, 0.0f, 0.765f, 0.555f, 0.556f, 0.226f, 0.0f, 0.763f, 0.556f, 0.557f, 0.235f, 0.0f, 0.761f, 0.557f, 0.557f, + 0.244f, 0.0f, 0.758f, 0.558f, 0.558f, 0.253f, 0.0f, 0.755f, 0.559f, 0.559f, 0.263f, 0.0f, 0.752f, 0.56f, 0.559f, 0.272f, 0.0f, 0.749f, 0.561f, 0.56f, + 0.285f, 0.0f, 0.745f, 0.562f, 0.56f, 0.299f, 0.0f, 0.741f, 0.563f, 0.563f, 0.316f, 0.0f, 0.736f, 0.564f, 0.564f, 0.331f, 0.0f, 0.728f, 0.565f, 0.567f, + 0.349f, 0.0f, 0.718f, 0.565f, 0.568f, 0.365f, 0.0f, 0.708f, 0.566f, 0.568f, 0.38f, 0.0f, 0.699f, 0.566f, 0.568f, 0.39f, 0.0f, 0.693f, 0.566f, 0.568f, + 0.397f, 0.0f, 0.687f, 0.566f, 0.569f, 0.4f, 0.0f, 0.683f, 0.566f, 0.569f, 0.401f, 0.0f, 0.681f, 0.565f, 0.57f, 0.4f, 0.0f, 0.679f, 0.565f, 0.57f, + 0.397f, 0.0f, 0.678f, 0.564f, 0.57f, 0.393f, 0.0f, 0.678f, 0.564f, 0.565f, 0.387f, 0.0f, 0.678f, 0.563f, 0.559f, 0.379f, 0.0f, 0.679f, 0.562f, 0.558f, + 0.37f, 0.0f, 0.681f, 0.561f, 0.557f, 0.357f, 0.0f, 0.684f, 0.561f, 0.557f, 0.342f, 0.0f, 0.689f, 0.56f, 0.557f, 0.324f, 0.0f, 0.694f, 0.56f, 0.557f, + 0.307f, 0.0f, 0.697f, 0.559f, 0.558f, 0.291f, 0.0f, 0.699f, 0.559f, 0.558f, 0.274f, 0.0f, 0.701f, 0.559f, 0.557f, 0.26f, 0.0f, 0.703f, 0.558f, 0.558f, + 0.246f, 0.0f, 0.705f, 0.558f, 0.558f, 0.235f, 0.0f, 0.707f, 0.558f, 0.558f, 0.224f, 0.0f, 0.709f, 0.558f, 0.558f, 0.214f, 0.0f, 0.711f, 0.558f, 0.558f, + 0.203f, 0.0f, 0.713f, 0.558f, 0.559f, 0.192f, 0.0f, 0.714f, 0.558f, 0.558f, 0.181f, 0.0f, 0.714f, 0.557f, 0.557f, 0.17f, 0.0f, 0.714f, 0.557f, 0.557f, + 0.16f, 0.0f, 0.715f, 0.557f, 0.556f, 0.149f, 0.0f, 0.715f, 0.557f, 0.556f, 0.139f, 0.0f, 0.716f, 0.557f, 0.556f, 0.129f, 0.0f, 0.716f, 0.558f, 0.556f, + 0.119f, 0.0f, 0.717f, 0.558f, 0.556f, 0.109f, 0.0f, 0.717f, 0.558f, 0.557f, 0.099f, 0.0f, 0.718f, 0.558f, 0.557f, 0.089f, 0.0f, 0.718f, 0.559f, 0.557f, + 0.079f, 0.0f, 0.718f, 0.559f, 0.558f, 0.068f, 0.0f, 0.719f, 0.559f, 0.559f, 0.057f, 0.0f, 0.719f, 0.56f, 0.56f, 0.046f, 0.0f, 0.718f, 0.56f, 0.561f, + 0.035f, 0.0f, 0.718f, 0.561f, 0.561f, 0.024f, 0.0f, 0.718f, 0.561f, 0.562f, 0.013f, 0.0f, 0.717f, 0.562f, 0.562f, 0.002f, 0.0f, 0.717f, 0.562f, 0.563f, + -0.01f, 0.0f, 0.717f, 0.563f, 0.564f, -0.021f, 0.0f, 0.717f, 0.563f, 0.564f, -0.032f, 0.0f, 0.716f, 0.563f, 0.564f, -0.044f, 0.0f, 0.715f, 0.564f, 0.564f, + -0.055f, 0.0f, 0.714f, 0.564f, 0.565f, -0.066f, 0.0f, 0.713f, 0.564f, 0.565f, -0.078f, 0.0f, 0.712f, 0.564f, 0.564f, -0.089f, 0.0f, 0.711f, 0.564f, 0.564f, + -0.101f, 0.0f, 0.709f, 0.565f, 0.564f, -0.112f, 0.0f, 0.708f, 0.565f, 0.564f, -0.124f, 0.0f, 0.707f, 0.565f, 0.564f, -0.135f, 0.0f, 0.705f, 0.565f, 0.564f, + -0.146f, 0.0f, 0.704f, 0.566f, 0.564f, -0.158f, 0.0f, 0.702f, 0.566f, 0.564f, -0.169f, 0.0f, 0.7f, 0.566f, 0.566f, -0.18f, 0.0f, 0.698f, 0.567f, 0.568f, + -0.191f, 0.0f, 0.696f, 0.567f, 0.568f, -0.203f, 0.0f, 0.693f, 0.567f, 0.568f, -0.215f, 0.0f, 0.69f, 0.567f, 0.568f, -0.227f, 0.0f, 0.687f, 0.567f, 0.568f, + -0.238f, 0.0f, 0.684f, 0.567f, 0.568f, -0.25f, 0.0f, 0.681f, 0.567f, 0.569f, -0.262f, 0.0f, 0.678f, 0.567f, 0.569f, -0.273f, 0.0f, 0.675f, 0.567f, 0.567f, + -0.284f, 0.0f, 0.673f, 0.567f, 0.566f, -0.295f, 0.0f, 0.671f, 0.567f, 0.567f, -0.305f, 0.0f, 0.669f, 0.566f, 0.567f, -0.316f, 0.0f, 0.666f, 0.566f, 0.567f, + -0.326f, 0.0f, 0.663f, 0.565f, 0.566f, -0.337f, 0.0f, 0.66f, 0.565f, 0.566f, -0.348f, 0.0f, 0.655f, 0.564f, 0.564f, -0.359f, 0.0f, 0.652f, 0.563f, 0.564f, + -0.369f, 0.0f, 0.648f, 0.562f, 0.563f, -0.379f, 0.0f, 0.644f, 0.561f, 0.56f, -0.389f, 0.0f, 0.64f, 0.561f, 0.559f, -0.399f, 0.0f, 0.636f, 0.56f, 0.559f, + -0.409f, 0.0f, 0.633f, 0.559f, 0.559f, -0.419f, 0.0f, 0.629f, 0.559f, 0.559f, -0.428f, 0.0f, 0.625f, 0.559f, 0.558f, -0.438f, 0.0f, 0.62f, 0.559f, 0.559f, + -0.447f, 0.0f, 0.615f, 0.559f, 0.559f, -0.457f, 0.0f, 0.61f, 0.559f, 0.559f, -0.466f, 0.0f, 0.605f, 0.559f, 0.559f, -0.474f, 0.0f, 0.6f, 0.559f, 0.559f, + -0.483f, 0.0f, 0.595f, 0.559f, 0.559f, -0.492f, 0.0f, 0.591f, 0.559f, 0.559f, -0.5f, 0.0f, 0.586f, 0.559f, 0.559f, -0.508f, 0.0f, 0.58f, 0.559f, 0.559f, + -0.515f, 0.0f, 0.574f, 0.559f, 0.559f, -0.523f, 0.0f, 0.568f, 0.559f, 0.559f, -0.531f, 0.0f, 0.562f, 0.559f, 0.558f, -0.54f, 0.0f, 0.556f, 0.559f, 0.558f, + -0.548f, 0.0f, 0.549f, 0.559f, 0.559f, -0.556f, 0.0f, 0.543f, 0.559f, 0.559f, -0.562f, 0.0f, 0.537f, 0.559f, 0.559f, -0.568f, 0.0f, 0.531f, 0.559f, 0.559f, + -0.574f, 0.0f, 0.524f, 0.559f, 0.559f, -0.58f, 0.0f, 0.518f, 0.558f, 0.559f, -0.586f, 0.0f, 0.512f, 0.557f, 0.558f, -0.591f, 0.0f, 0.506f, 0.555f, 0.557f, + -0.597f, 0.0f, 0.5f, 0.551f, 0.556f, -0.603f, 0.0f, 0.493f, 0.546f, 0.547f, -0.609f, 0.0f, 0.487f, 0.541f, 0.538f, -0.614f, 0.0f, 0.48f, 0.536f, 0.535f, + -0.621f, 0.0f, 0.473f, 0.534f, 0.534f, -0.628f, 0.0f, 0.467f, 0.534f, 0.534f, -0.637f, 0.0f, 0.459f, 0.534f, 0.534f, -0.642f, 0.0f, 0.452f, 0.532f, 0.532f, + -0.65f, 0.0f, 0.445f, 0.528f, 0.528f, -0.654f, 0.0f, 0.438f, 0.525f, 0.525f, -0.659f, 0.0f, 0.431f, 0.522f, 0.522f, +}; + +static const float data11[1 * GP_PRIM_DATABUF_SIZE] = { + -0.525f, 0.0f, 0.174f, 0.124f, 0.124f, +}; + +static const float data12[123 * GP_PRIM_DATABUF_SIZE] = { + -0.53f, 0.0f, 0.193f, 0.147f, 0.147f, -0.532f, 0.0f, 0.186f, 0.316f, 0.316f, -0.534f, 0.0f, 0.18f, 0.353f, 0.353f, -0.535f, 0.0f, 0.173f, 0.382f, 0.382f, + -0.537f, 0.0f, 0.165f, 0.384f, 0.384f, -0.538f, 0.0f, 0.155f, 0.387f, 0.387f, -0.539f, 0.0f, 0.145f, 0.393f, 0.393f, -0.54f, -0.0f, 0.134f, 0.399f, 0.399f, + -0.541f, -0.0f, 0.123f, 0.4f, 0.4f, -0.542f, -0.0f, 0.11f, 0.401f, 0.401f, -0.542f, 0.0f, 0.094f, 0.402f, 0.402f, -0.54f, 0.0f, 0.078f, 0.403f, 0.403f, + -0.538f, 0.0f, 0.061f, 0.404f, 0.404f, -0.535f, 0.0f, 0.045f, 0.404f, 0.404f, -0.531f, 0.0f, 0.031f, 0.404f, 0.404f, -0.526f, 0.0f, 0.018f, 0.404f, 0.404f, + -0.52f, -0.0f, 0.005f, 0.405f, 0.405f, -0.513f, -0.0f, -0.01f, 0.405f, 0.405f, -0.505f, -0.0f, -0.024f, 0.405f, 0.405f, -0.495f, -0.0f, -0.037f, 0.405f, 0.405f, + -0.485f, 0.0f, -0.051f, 0.405f, 0.405f, -0.474f, 0.0f, -0.064f, 0.406f, 0.406f, -0.462f, 0.0f, -0.076f, 0.405f, 0.405f, -0.451f, 0.0f, -0.086f, 0.406f, 0.406f, + -0.442f, 0.0f, -0.094f, 0.406f, 0.406f, -0.432f, 0.0f, -0.102f, 0.406f, 0.406f, -0.422f, 0.0f, -0.108f, 0.405f, 0.405f, -0.411f, 0.0f, -0.114f, 0.406f, 0.406f, + -0.4f, 0.0f, -0.119f, 0.405f, 0.405f, -0.389f, 0.0f, -0.122f, 0.406f, 0.406f, -0.378f, 0.0f, -0.125f, 0.407f, 0.407f, -0.365f, 0.0f, -0.127f, 0.412f, 0.412f, + -0.354f, 0.0f, -0.129f, 0.418f, 0.418f, -0.342f, 0.0f, -0.131f, 0.44f, 0.44f, -0.33f, 0.0f, -0.131f, 0.448f, 0.448f, -0.317f, 0.0f, -0.131f, 0.469f, 0.469f, + -0.305f, 0.0f, -0.13f, 0.477f, 0.477f, -0.293f, 0.0f, -0.128f, 0.482f, 0.482f, -0.278f, 0.0f, -0.125f, 0.494f, 0.494f, -0.266f, 0.0f, -0.121f, 0.5f, 0.5f, + -0.253f, 0.0f, -0.116f, 0.507f, 0.507f, -0.242f, 0.0f, -0.111f, 0.509f, 0.509f, -0.231f, 0.0f, -0.105f, 0.511f, 0.511f, -0.222f, 0.0f, -0.099f, 0.511f, 0.511f, + -0.213f, 0.0f, -0.092f, 0.512f, 0.512f, -0.206f, 0.0f, -0.084f, 0.513f, 0.513f, -0.199f, 0.0f, -0.076f, 0.514f, 0.514f, -0.192f, 0.0f, -0.067f, 0.515f, 0.515f, + -0.186f, -0.0f, -0.058f, 0.516f, 0.516f, -0.18f, -0.0f, -0.049f, 0.516f, 0.516f, -0.175f, -0.0f, -0.04f, 0.515f, 0.515f, -0.17f, -0.0f, -0.03f, 0.515f, 0.515f, + -0.166f, -0.0f, -0.02f, 0.516f, 0.516f, -0.163f, -0.0f, -0.01f, 0.504f, 0.504f, -0.159f, -0.0f, 0.002f, 0.502f, 0.502f, -0.155f, -0.0f, 0.014f, 0.501f, 0.501f, + -0.152f, -0.0f, 0.027f, 0.502f, 0.502f, -0.149f, -0.0f, 0.043f, 0.5f, 0.5f, -0.148f, -0.0f, 0.058f, 0.49f, 0.49f, -0.147f, -0.0f, 0.075f, 0.47f, 0.47f, + -0.146f, -0.0f, 0.09f, 0.463f, 0.463f, -0.146f, -0.0f, 0.105f, 0.454f, 0.454f, -0.146f, -0.0f, 0.12f, 0.427f, 0.427f, -0.148f, 0.0f, 0.133f, 0.413f, 0.413f, + -0.15f, 0.0f, 0.144f, 0.4f, 0.4f, -0.153f, 0.0f, 0.152f, 0.383f, 0.383f, -0.156f, 0.0f, 0.157f, 0.369f, 0.369f, -0.158f, 0.0f, 0.16f, 0.36f, 0.36f, + -0.16f, 0.0f, 0.158f, 0.349f, 0.349f, -0.162f, 0.0f, 0.154f, 0.364f, 0.364f, -0.164f, 0.0f, 0.147f, 0.37f, 0.37f, -0.166f, 0.0f, 0.139f, 0.378f, 0.378f, + -0.168f, 0.0f, 0.13f, 0.386f, 0.386f, -0.172f, 0.0f, 0.119f, 0.394f, 0.394f, -0.176f, -0.0f, 0.108f, 0.405f, 0.405f, -0.18f, -0.0f, 0.096f, 0.412f, 0.412f, + -0.185f, -0.0f, 0.084f, 0.417f, 0.417f, -0.191f, -0.0f, 0.073f, 0.425f, 0.425f, -0.196f, -0.0f, 0.063f, 0.431f, 0.431f, -0.202f, -0.0f, 0.053f, 0.441f, 0.441f, + -0.208f, -0.0f, 0.043f, 0.444f, 0.444f, -0.214f, -0.0f, 0.034f, 0.451f, 0.451f, -0.22f, 0.0f, 0.026f, 0.46f, 0.46f, -0.226f, 0.0f, 0.018f, 0.463f, 0.463f, + -0.232f, 0.0f, 0.01f, 0.474f, 0.474f, -0.239f, 0.0f, 0.004f, 0.477f, 0.477f, -0.247f, 0.0f, -0.003f, 0.48f, 0.48f, -0.255f, 0.0f, -0.008f, 0.483f, 0.483f, + -0.264f, 0.0f, -0.013f, 0.497f, 0.497f, -0.274f, 0.0f, -0.018f, 0.501f, 0.501f, -0.285f, 0.0f, -0.022f, 0.505f, 0.505f, -0.297f, 0.0f, -0.024f, 0.509f, 0.509f, + -0.311f, 0.0f, -0.025f, 0.51f, 0.51f, -0.325f, 0.0f, -0.024f, 0.512f, 0.512f, -0.339f, 0.0f, -0.023f, 0.512f, 0.512f, -0.354f, 0.0f, -0.022f, 0.513f, 0.513f, + -0.368f, 0.0f, -0.02f, 0.513f, 0.513f, -0.382f, 0.0f, -0.017f, 0.514f, 0.514f, -0.397f, 0.0f, -0.013f, 0.514f, 0.514f, -0.41f, 0.0f, -0.007f, 0.514f, 0.514f, + -0.422f, 0.0f, 0.001f, 0.513f, 0.513f, -0.434f, 0.0f, 0.009f, 0.514f, 0.514f, -0.446f, 0.0f, 0.018f, 0.514f, 0.514f, -0.458f, 0.0f, 0.028f, 0.514f, 0.514f, + -0.47f, -0.0f, 0.039f, 0.514f, 0.514f, -0.48f, 0.0f, 0.048f, 0.514f, 0.514f, -0.487f, 0.0f, 0.057f, 0.514f, 0.514f, -0.493f, 0.0f, 0.068f, 0.514f, 0.514f, + -0.498f, 0.0f, 0.08f, 0.514f, 0.514f, -0.502f, 0.0f, 0.092f, 0.514f, 0.514f, -0.506f, 0.0f, 0.104f, 0.514f, 0.514f, -0.509f, -0.0f, 0.116f, 0.515f, 0.515f, + -0.511f, -0.0f, 0.125f, 0.515f, 0.515f, -0.513f, -0.0f, 0.133f, 0.515f, 0.515f, -0.515f, -0.0f, 0.141f, 0.515f, 0.515f, -0.517f, 0.0f, 0.148f, 0.515f, 0.515f, + -0.519f, 0.0f, 0.155f, 0.514f, 0.514f, -0.52f, 0.0f, 0.161f, 0.514f, 0.514f, -0.522f, 0.0f, 0.168f, 0.514f, 0.514f, -0.523f, 0.0f, 0.174f, 0.514f, 0.514f, + -0.525f, 0.0f, 0.18f, 0.514f, 0.514f, -0.526f, 0.0f, 0.185f, 0.514f, 0.514f, -0.527f, 0.0f, 0.191f, 0.513f, 0.513f, +}; + +static const float data13[125 * GP_PRIM_DATABUF_SIZE] = { + 0.184f, 0.0f, 0.22f, 0.026f, 0.026f, 0.182f, 0.0f, 0.21f, 0.275f, 0.275f, 0.18f, 0.0f, 0.203f, 0.301f, 0.301f, 0.178f, 0.0f, 0.195f, 0.322f, 0.322f, + 0.176f, 0.0f, 0.186f, 0.343f, 0.343f, 0.173f, 0.0f, 0.176f, 0.36f, 0.36f, 0.17f, -0.0f, 0.166f, 0.367f, 0.367f, 0.168f, -0.0f, 0.156f, 0.38f, 0.38f, + 0.165f, -0.0f, 0.145f, 0.385f, 0.385f, 0.163f, -0.0f, 0.132f, 0.391f, 0.391f, 0.161f, -0.0f, 0.119f, 0.401f, 0.401f, 0.16f, -0.0f, 0.103f, 0.405f, 0.405f, + 0.161f, -0.0f, 0.086f, 0.405f, 0.405f, 0.163f, -0.0f, 0.068f, 0.407f, 0.407f, 0.165f, 0.0f, 0.051f, 0.409f, 0.409f, 0.168f, 0.0f, 0.034f, 0.409f, 0.409f, + 0.172f, 0.0f, 0.018f, 0.409f, 0.409f, 0.177f, 0.0f, 0.004f, 0.409f, 0.409f, 0.183f, 0.0f, -0.008f, 0.411f, 0.411f, 0.19f, 0.0f, -0.022f, 0.411f, 0.411f, + 0.196f, 0.0f, -0.034f, 0.411f, 0.411f, 0.203f, 0.0f, -0.045f, 0.411f, 0.411f, 0.211f, 0.0f, -0.055f, 0.411f, 0.411f, 0.219f, 0.0f, -0.064f, 0.411f, 0.411f, + 0.227f, 0.0f, -0.072f, 0.411f, 0.411f, 0.235f, 0.0f, -0.08f, 0.412f, 0.412f, 0.244f, 0.0f, -0.087f, 0.412f, 0.412f, 0.253f, 0.0f, -0.094f, 0.413f, 0.413f, + 0.262f, 0.0f, -0.1f, 0.413f, 0.413f, 0.273f, 0.0f, -0.105f, 0.413f, 0.413f, 0.284f, 0.0f, -0.11f, 0.413f, 0.413f, 0.295f, 0.0f, -0.114f, 0.419f, 0.419f, + 0.307f, 0.0f, -0.117f, 0.425f, 0.425f, 0.321f, -0.0f, -0.118f, 0.433f, 0.433f, 0.334f, -0.0f, -0.12f, 0.446f, 0.446f, 0.347f, -0.0f, -0.12f, 0.474f, 0.474f, + 0.36f, -0.0f, -0.12f, 0.481f, 0.481f, 0.374f, -0.0f, -0.119f, 0.491f, 0.491f, 0.387f, -0.0f, -0.118f, 0.494f, 0.494f, 0.401f, 0.0f, -0.116f, 0.5f, 0.5f, + 0.414f, 0.0f, -0.112f, 0.505f, 0.505f, 0.426f, -0.0f, -0.107f, 0.51f, 0.51f, 0.438f, -0.0f, -0.101f, 0.513f, 0.513f, 0.449f, -0.0f, -0.094f, 0.515f, 0.515f, + 0.46f, -0.0f, -0.086f, 0.517f, 0.517f, 0.47f, -0.0f, -0.078f, 0.519f, 0.519f, 0.478f, -0.0f, -0.07f, 0.52f, 0.52f, 0.486f, -0.0f, -0.061f, 0.522f, 0.522f, + 0.493f, -0.0f, -0.052f, 0.523f, 0.523f, 0.499f, -0.0f, -0.044f, 0.522f, 0.522f, 0.505f, -0.0f, -0.035f, 0.522f, 0.522f, 0.51f, -0.0f, -0.027f, 0.523f, 0.523f, + 0.514f, -0.0f, -0.018f, 0.523f, 0.523f, 0.517f, -0.0f, -0.009f, 0.523f, 0.523f, 0.52f, -0.0f, -0.001f, 0.524f, 0.524f, 0.522f, -0.0f, 0.008f, 0.523f, 0.523f, + 0.525f, -0.0f, 0.018f, 0.521f, 0.522f, 0.527f, -0.0f, 0.027f, 0.515f, 0.514f, 0.529f, -0.0f, 0.036f, 0.512f, 0.512f, 0.531f, -0.0f, 0.045f, 0.509f, 0.51f, + 0.533f, -0.0f, 0.053f, 0.506f, 0.505f, 0.535f, -0.0f, 0.062f, 0.503f, 0.503f, 0.536f, -0.0f, 0.071f, 0.5f, 0.5f, 0.538f, -0.0f, 0.08f, 0.496f, 0.497f, + 0.538f, -0.0f, 0.09f, 0.491f, 0.492f, 0.539f, -0.0f, 0.1f, 0.485f, 0.486f, 0.539f, 0.0f, 0.11f, 0.475f, 0.476f, 0.539f, 0.0f, 0.12f, 0.46f, 0.459f, + 0.539f, 0.0f, 0.13f, 0.444f, 0.448f, 0.538f, 0.0f, 0.139f, 0.406f, 0.405f, 0.537f, 0.0f, 0.144f, 0.399f, 0.399f, 0.536f, 0.0f, 0.146f, 0.395f, 0.395f, + 0.535f, 0.0f, 0.144f, 0.412f, 0.412f, 0.533f, 0.0f, 0.139f, 0.413f, 0.413f, 0.53f, 0.0f, 0.131f, 0.414f, 0.413f, 0.528f, 0.0f, 0.122f, 0.419f, 0.418f, + 0.525f, 0.0f, 0.112f, 0.425f, 0.424f, 0.521f, 0.0f, 0.102f, 0.444f, 0.444f, 0.518f, 0.0f, 0.094f, 0.451f, 0.452f, 0.514f, 0.0f, 0.085f, 0.457f, 0.457f, + 0.509f, 0.0f, 0.078f, 0.461f, 0.46f, 0.504f, 0.0f, 0.069f, 0.469f, 0.468f, 0.499f, 0.0f, 0.06f, 0.481f, 0.481f, 0.493f, 0.0f, 0.052f, 0.489f, 0.489f, + 0.487f, 0.0f, 0.044f, 0.492f, 0.492f, 0.481f, 0.0f, 0.037f, 0.501f, 0.5f, 0.474f, 0.0f, 0.029f, 0.513f, 0.513f, 0.467f, 0.0f, 0.022f, 0.521f, 0.521f, + 0.458f, 0.0f, 0.015f, 0.524f, 0.524f, 0.449f, 0.0f, 0.008f, 0.525f, 0.525f, 0.439f, 0.0f, 0.001f, 0.528f, 0.528f, 0.427f, 0.0f, -0.005f, 0.532f, 0.532f, + 0.416f, 0.0f, -0.011f, 0.533f, 0.533f, 0.401f, 0.0f, -0.015f, 0.537f, 0.537f, 0.386f, 0.0f, -0.018f, 0.539f, 0.539f, 0.371f, 0.0f, -0.02f, 0.538f, 0.538f, + 0.356f, 0.0f, -0.021f, 0.543f, 0.543f, 0.341f, 0.0f, -0.023f, 0.543f, 0.543f, 0.326f, 0.0f, -0.023f, 0.543f, 0.543f, 0.312f, 0.0f, -0.022f, 0.543f, 0.543f, + 0.298f, 0.0f, -0.018f, 0.543f, 0.543f, 0.286f, 0.0f, -0.014f, 0.543f, 0.543f, 0.273f, 0.0f, -0.006f, 0.543f, 0.543f, 0.26f, 0.0f, 0.004f, 0.543f, 0.543f, + 0.247f, 0.0f, 0.013f, 0.543f, 0.543f, 0.235f, 0.0f, 0.022f, 0.543f, 0.543f, 0.225f, 0.0f, 0.033f, 0.543f, 0.543f, 0.215f, 0.0f, 0.045f, 0.542f, 0.542f, + 0.206f, 0.0f, 0.061f, 0.54f, 0.54f, 0.199f, 0.0f, 0.078f, 0.542f, 0.542f, 0.193f, 0.0f, 0.094f, 0.542f, 0.542f, 0.189f, -0.0f, 0.109f, 0.541f, 0.541f, + 0.186f, -0.0f, 0.119f, 0.542f, 0.542f, 0.185f, -0.0f, 0.127f, 0.542f, 0.542f, 0.184f, -0.0f, 0.135f, 0.542f, 0.542f, 0.184f, -0.0f, 0.142f, 0.542f, 0.542f, + 0.183f, -0.0f, 0.149f, 0.541f, 0.541f, 0.183f, -0.0f, 0.156f, 0.538f, 0.538f, 0.183f, -0.0f, 0.163f, 0.539f, 0.539f, 0.183f, -0.0f, 0.17f, 0.54f, 0.54f, + 0.183f, 0.0f, 0.177f, 0.54f, 0.54f, 0.183f, 0.0f, 0.184f, 0.54f, 0.54f, 0.183f, 0.0f, 0.191f, 0.54f, 0.54f, 0.184f, 0.0f, 0.196f, 0.539f, 0.539f, + 0.184f, 0.0f, 0.204f, 0.518f, 0.518f, +}; + +static const float data14[45 * GP_PRIM_DATABUF_SIZE] = { + -0.096f, -0.0f, -0.305f, 0.01f, 0.01f, -0.09f, -0.0f, -0.313f, 0.121f, 0.362f, -0.086f, -0.0f, -0.318f, 0.179f, 0.368f, -0.081f, -0.0f, -0.325f, 0.234f, 0.37f, + -0.075f, -0.0f, -0.331f, 0.272f, 0.37f, -0.068f, -0.0f, -0.338f, 0.302f, 0.371f, -0.061f, -0.0f, -0.345f, 0.324f, 0.374f, -0.053f, -0.0f, -0.352f, 0.34f, 0.377f, + -0.044f, -0.0f, -0.358f, 0.352f, 0.378f, -0.035f, -0.0f, -0.362f, 0.362f, 0.377f, -0.026f, -0.0f, -0.366f, 0.37f, 0.378f, -0.018f, -0.0f, -0.368f, 0.377f, 0.378f, + -0.009f, -0.0f, -0.369f, 0.383f, 0.376f, -0.001f, -0.0f, -0.369f, 0.389f, 0.369f, 0.007f, -0.0f, -0.368f, 0.395f, 0.364f, 0.015f, -0.0f, -0.367f, 0.4f, 0.388f, + 0.023f, -0.0f, -0.365f, 0.405f, 0.41f, 0.03f, -0.0f, -0.363f, 0.41f, 0.429f, 0.038f, -0.0f, -0.36f, 0.414f, 0.438f, 0.044f, -0.0f, -0.357f, 0.417f, 0.441f, + 0.05f, -0.0f, -0.355f, 0.419f, 0.444f, 0.055f, -0.0f, -0.352f, 0.42f, 0.441f, 0.06f, -0.0f, -0.349f, 0.421f, 0.445f, 0.063f, -0.0f, -0.347f, 0.421f, 0.446f, + 0.065f, -0.0f, -0.344f, 0.42f, 0.443f, 0.065f, -0.0f, -0.342f, 0.42f, 0.437f, 0.065f, -0.0f, -0.341f, 0.419f, 0.413f, 0.063f, -0.0f, -0.339f, 0.418f, 0.404f, + 0.061f, -0.0f, -0.338f, 0.418f, 0.403f, 0.057f, -0.0f, -0.337f, 0.418f, 0.402f, 0.052f, -0.0f, -0.337f, 0.418f, 0.407f, 0.046f, -0.0f, -0.337f, 0.419f, 0.411f, + 0.04f, 0.0f, -0.336f, 0.42f, 0.416f, 0.032f, 0.0f, -0.337f, 0.422f, 0.421f, 0.023f, 0.0f, -0.339f, 0.424f, 0.425f, 0.014f, 0.0f, -0.34f, 0.426f, 0.427f, + 0.003f, 0.0f, -0.341f, 0.428f, 0.427f, -0.007f, 0.0f, -0.341f, 0.43f, 0.433f, -0.018f, 0.0f, -0.339f, 0.432f, 0.437f, -0.027f, 0.0f, -0.335f, 0.434f, 0.438f, + -0.037f, 0.0f, -0.33f, 0.435f, 0.437f, -0.046f, -0.0f, -0.326f, 0.436f, 0.438f, -0.055f, -0.0f, -0.321f, 0.436f, 0.44f, -0.062f, -0.0f, -0.316f, 0.437f, 0.439f, + -0.073f, -0.0f, -0.31f, 0.437f, 0.437f, +}; + +static const float data16[84 * GP_PRIM_DATABUF_SIZE] = { + 0.737f, 0.0f, 0.177f, 0.148f, 0.148f, 0.735f, 0.0f, 0.164f, 0.214f, 0.39f, 0.734f, 0.0f, 0.155f, 0.254f, 0.402f, 0.732f, 0.0f, 0.143f, 0.295f, 0.413f, + 0.73f, 0.0f, 0.132f, 0.328f, 0.415f, 0.728f, 0.0f, 0.121f, 0.355f, 0.415f, 0.726f, 0.0f, 0.109f, 0.375f, 0.416f, 0.724f, 0.0f, 0.097f, 0.39f, 0.417f, + 0.721f, 0.0f, 0.086f, 0.401f, 0.418f, 0.719f, 0.0f, 0.074f, 0.408f, 0.419f, 0.716f, 0.0f, 0.062f, 0.413f, 0.42f, 0.713f, 0.0f, 0.05f, 0.416f, 0.42f, + 0.71f, 0.0f, 0.039f, 0.418f, 0.421f, 0.707f, 0.0f, 0.028f, 0.42f, 0.421f, 0.703f, 0.0f, 0.017f, 0.421f, 0.422f, 0.7f, 0.0f, 0.006f, 0.421f, 0.422f, + 0.696f, 0.0f, -0.005f, 0.422f, 0.422f, 0.693f, 0.0f, -0.015f, 0.422f, 0.422f, 0.689f, 0.0f, -0.025f, 0.423f, 0.423f, 0.685f, 0.0f, -0.034f, 0.423f, 0.423f, + 0.681f, 0.0f, -0.044f, 0.423f, 0.423f, 0.677f, 0.0f, -0.053f, 0.423f, 0.423f, 0.672f, 0.0f, -0.062f, 0.423f, 0.423f, 0.668f, 0.0f, -0.071f, 0.422f, 0.424f, + 0.662f, 0.0f, -0.08f, 0.422f, 0.424f, 0.657f, 0.0f, -0.088f, 0.422f, 0.422f, 0.651f, 0.0f, -0.095f, 0.421f, 0.419f, 0.645f, 0.0f, -0.103f, 0.42f, 0.419f, + 0.638f, 0.0f, -0.109f, 0.42f, 0.419f, 0.631f, 0.0f, -0.115f, 0.419f, 0.419f, 0.624f, 0.0f, -0.12f, 0.419f, 0.419f, 0.617f, 0.0f, -0.125f, 0.419f, 0.419f, + 0.61f, 0.0f, -0.129f, 0.418f, 0.418f, 0.602f, 0.0f, -0.133f, 0.418f, 0.416f, 0.594f, 0.0f, -0.137f, 0.417f, 0.416f, 0.587f, 0.0f, -0.14f, 0.417f, 0.415f, + 0.579f, 0.0f, -0.142f, 0.417f, 0.416f, 0.571f, 0.0f, -0.144f, 0.417f, 0.415f, 0.564f, 0.0f, -0.145f, 0.417f, 0.416f, 0.556f, 0.0f, -0.146f, 0.417f, 0.415f, + 0.549f, 0.0f, -0.146f, 0.417f, 0.415f, 0.541f, 0.0f, -0.146f, 0.417f, 0.415f, 0.535f, 0.0f, -0.145f, 0.417f, 0.416f, 0.53f, 0.0f, -0.143f, 0.418f, 0.418f, + 0.526f, 0.0f, -0.14f, 0.418f, 0.418f, 0.524f, 0.0f, -0.136f, 0.42f, 0.418f, 0.524f, 0.0f, -0.132f, 0.422f, 0.416f, 0.527f, 0.0f, -0.126f, 0.424f, 0.424f, + 0.531f, 0.0f, -0.121f, 0.427f, 0.428f, 0.536f, 0.0f, -0.115f, 0.43f, 0.433f, 0.542f, 0.0f, -0.109f, 0.433f, 0.436f, 0.548f, 0.0f, -0.102f, 0.435f, 0.436f, + 0.555f, 0.0f, -0.095f, 0.436f, 0.437f, 0.562f, 0.0f, -0.088f, 0.437f, 0.438f, 0.568f, 0.0f, -0.081f, 0.437f, 0.438f, 0.575f, 0.0f, -0.073f, 0.438f, 0.438f, + 0.581f, 0.0f, -0.065f, 0.438f, 0.438f, 0.587f, 0.0f, -0.058f, 0.438f, 0.438f, 0.593f, 0.0f, -0.05f, 0.438f, 0.438f, 0.599f, 0.0f, -0.041f, 0.438f, 0.438f, + 0.605f, 0.0f, -0.033f, 0.438f, 0.438f, 0.61f, 0.0f, -0.024f, 0.438f, 0.438f, 0.615f, 0.0f, -0.015f, 0.438f, 0.438f, 0.621f, 0.0f, -0.006f, 0.438f, 0.438f, + 0.626f, 0.0f, 0.004f, 0.438f, 0.438f, 0.631f, 0.0f, 0.013f, 0.437f, 0.438f, 0.636f, 0.0f, 0.023f, 0.436f, 0.438f, 0.641f, 0.0f, 0.032f, 0.434f, 0.438f, + 0.647f, 0.0f, 0.042f, 0.432f, 0.437f, 0.652f, 0.0f, 0.051f, 0.431f, 0.429f, 0.657f, 0.0f, 0.06f, 0.429f, 0.426f, 0.662f, 0.0f, 0.069f, 0.427f, 0.425f, + 0.668f, 0.0f, 0.078f, 0.425f, 0.425f, 0.673f, 0.0f, 0.087f, 0.423f, 0.424f, 0.678f, 0.0f, 0.095f, 0.42f, 0.422f, 0.683f, 0.0f, 0.104f, 0.416f, 0.42f, + 0.688f, 0.0f, 0.112f, 0.411f, 0.421f, 0.693f, 0.0f, 0.12f, 0.403f, 0.417f, 0.698f, 0.0f, 0.128f, 0.394f, 0.411f, 0.702f, 0.0f, 0.135f, 0.382f, 0.404f, + 0.707f, 0.0f, 0.143f, 0.369f, 0.388f, 0.711f, 0.0f, 0.15f, 0.352f, 0.371f, 0.714f, 0.0f, 0.155f, 0.338f, 0.352f, 0.719f, 0.0f, 0.164f, 0.315f, 0.315f, +}; + +static const float data15[44 * GP_PRIM_DATABUF_SIZE] = { + -0.085f, 0.0f, -0.816f, 0.138f, 0.138f, -0.079f, 0.0f, -0.825f, 0.246f, 0.309f, -0.074f, 0.0f, -0.832f, 0.302f, 0.34f, -0.067f, 0.0f, -0.84f, 0.335f, 0.352f, + -0.059f, 0.0f, -0.848f, 0.357f, 0.374f, -0.05f, 0.0f, -0.855f, 0.371f, 0.378f, -0.041f, 0.0f, -0.861f, 0.382f, 0.383f, -0.031f, 0.0f, -0.866f, 0.391f, 0.396f, + -0.021f, 0.0f, -0.871f, 0.398f, 0.401f, -0.011f, 0.0f, -0.874f, 0.404f, 0.407f, -0.001f, 0.0f, -0.877f, 0.409f, 0.411f, 0.01f, 0.0f, -0.878f, 0.415f, 0.412f, + 0.02f, 0.0f, -0.878f, 0.422f, 0.417f, 0.031f, 0.0f, -0.878f, 0.43f, 0.421f, 0.042f, 0.0f, -0.876f, 0.438f, 0.437f, 0.052f, 0.0f, -0.873f, 0.445f, 0.451f, + 0.062f, 0.0f, -0.868f, 0.451f, 0.459f, 0.071f, 0.0f, -0.863f, 0.456f, 0.463f, 0.08f, 0.0f, -0.857f, 0.46f, 0.465f, 0.087f, 0.0f, -0.85f, 0.462f, 0.465f, + 0.094f, 0.0f, -0.842f, 0.461f, 0.465f, 0.098f, 0.0f, -0.835f, 0.458f, 0.467f, 0.101f, 0.0f, -0.827f, 0.451f, 0.457f, 0.103f, 0.0f, -0.82f, 0.436f, 0.451f, + 0.102f, 0.0f, -0.815f, 0.422f, 0.418f, 0.1f, 0.0f, -0.811f, 0.419f, 0.378f, 0.096f, 0.0f, -0.814f, 0.436f, 0.447f, 0.089f, 0.0f, -0.817f, 0.454f, 0.465f, + 0.082f, 0.0f, -0.821f, 0.465f, 0.47f, 0.072f, 0.0f, -0.825f, 0.473f, 0.477f, 0.061f, 0.0f, -0.828f, 0.477f, 0.479f, 0.049f, 0.0f, -0.832f, 0.48f, 0.485f, + 0.036f, 0.0f, -0.834f, 0.483f, 0.48f, 0.023f, 0.0f, -0.836f, 0.484f, 0.485f, 0.01f, 0.0f, -0.838f, 0.486f, 0.487f, -0.003f, 0.0f, -0.84f, 0.486f, 0.488f, + -0.016f, 0.0f, -0.84f, 0.486f, 0.489f, -0.027f, 0.0f, -0.84f, 0.485f, 0.485f, -0.039f, 0.0f, -0.839f, 0.484f, 0.484f, -0.049f, 0.0f, -0.837f, 0.483f, 0.485f, + -0.058f, 0.0f, -0.834f, 0.48f, 0.481f, -0.066f, 0.0f, -0.83f, 0.473f, 0.479f, -0.072f, 0.0f, -0.827f, 0.462f, 0.472f, -0.081f, 0.0f, -0.823f, 0.442f, 0.442f, +}; + +static const float data17[56 * GP_PRIM_DATABUF_SIZE] = { + -1.007f, -0.0f, 0.183f, 0.022f, 0.022f, -1.003f, -0.0f, 0.181f, 0.192f, 0.436f, -0.998f, -0.0f, 0.18f, 0.28f, 0.451f, -0.99f, -0.0f, 0.178f, 0.355f, 0.459f, + -0.98f, -0.0f, 0.175f, 0.402f, 0.464f, -0.967f, -0.0f, 0.169f, 0.432f, 0.467f, -0.952f, -0.0f, 0.152f, 0.449f, 0.468f, -0.943f, 0.0f, 0.138f, 0.459f, 0.469f, + -0.939f, 0.0f, 0.128f, 0.464f, 0.469f, -0.934f, 0.0f, 0.119f, 0.467f, 0.47f, -0.929f, 0.0f, 0.11f, 0.469f, 0.47f, -0.924f, 0.0f, 0.101f, 0.47f, 0.47f, + -0.919f, 0.0f, 0.092f, 0.47f, 0.471f, -0.913f, 0.0f, 0.082f, 0.471f, 0.471f, -0.908f, 0.0f, 0.072f, 0.471f, 0.471f, -0.903f, 0.0f, 0.063f, 0.472f, 0.472f, + -0.897f, 0.0f, 0.053f, 0.472f, 0.472f, -0.892f, 0.0f, 0.044f, 0.473f, 0.473f, -0.886f, 0.0f, 0.035f, 0.473f, 0.473f, -0.881f, 0.0f, 0.026f, 0.473f, 0.473f, + -0.876f, 0.0f, 0.018f, 0.473f, 0.473f, -0.87f, 0.0f, 0.012f, 0.472f, 0.473f, -0.865f, 0.0f, 0.006f, 0.47f, 0.473f, -0.86f, 0.0f, 0.003f, 0.468f, 0.473f, + -0.855f, 0.0f, 0.001f, 0.466f, 0.469f, -0.85f, 0.0f, 0.001f, 0.463f, 0.469f, -0.846f, 0.0f, 0.003f, 0.46f, 0.45f, -0.843f, 0.0f, 0.008f, 0.458f, 0.454f, + -0.84f, 0.0f, 0.014f, 0.456f, 0.454f, -0.838f, 0.0f, 0.021f, 0.455f, 0.454f, -0.836f, 0.0f, 0.03f, 0.453f, 0.455f, -0.835f, 0.0f, 0.039f, 0.451f, 0.455f, + -0.835f, 0.0f, 0.049f, 0.449f, 0.453f, -0.836f, 0.0f, 0.059f, 0.447f, 0.445f, -0.837f, 0.0f, 0.068f, 0.445f, 0.441f, -0.84f, 0.0f, 0.078f, 0.443f, 0.44f, + -0.843f, 0.0f, 0.087f, 0.442f, 0.44f, -0.846f, 0.0f, 0.095f, 0.442f, 0.44f, -0.851f, -0.0f, 0.103f, 0.441f, 0.441f, -0.855f, -0.0f, 0.111f, 0.441f, 0.44f, + -0.86f, -0.0f, 0.119f, 0.441f, 0.441f, -0.865f, -0.0f, 0.127f, 0.441f, 0.441f, -0.871f, -0.0f, 0.134f, 0.441f, 0.441f, -0.877f, -0.0f, 0.141f, 0.441f, 0.441f, + -0.883f, -0.0f, 0.149f, 0.441f, 0.442f, -0.889f, -0.0f, 0.156f, 0.441f, 0.441f, -0.896f, -0.0f, 0.163f, 0.441f, 0.442f, -0.904f, -0.0f, 0.169f, 0.442f, 0.441f, + -0.913f, -0.0f, 0.176f, 0.442f, 0.441f, -0.925f, -0.0f, 0.183f, 0.443f, 0.441f, -0.941f, -0.0f, 0.19f, 0.444f, 0.442f, -0.956f, -0.0f, 0.195f, 0.446f, 0.443f, + -0.971f, -0.0f, 0.198f, 0.448f, 0.443f, -0.983f, -0.0f, 0.198f, 0.451f, 0.452f, -0.992f, -0.0f, 0.198f, 0.454f, 0.456f, -1.001f, 0.0f, 0.196f, 0.457f, 0.457f, +}; + +static const float data18[59 * GP_PRIM_DATABUF_SIZE] = { + 0.782f, 0.0f, 0.099f, 0.04f, 0.04f, 0.779f, 0.0f, 0.088f, 0.108f, 0.34f, 0.777f, 0.0f, 0.08f, 0.149f, 0.35f, 0.774f, 0.0f, 0.071f, 0.194f, 0.352f, + 0.772f, 0.0f, 0.062f, 0.231f, 0.352f, 0.771f, 0.0f, 0.053f, 0.263f, 0.353f, 0.769f, 0.0f, 0.044f, 0.289f, 0.353f, 0.768f, 0.0f, 0.036f, 0.31f, 0.353f, + 0.767f, 0.0f, 0.029f, 0.327f, 0.353f, 0.767f, 0.0f, 0.023f, 0.341f, 0.353f, 0.767f, 0.0f, 0.017f, 0.353f, 0.353f, 0.768f, 0.0f, 0.013f, 0.363f, 0.353f, + 0.769f, 0.0f, 0.01f, 0.373f, 0.353f, 0.771f, 0.0f, 0.009f, 0.382f, 0.351f, 0.773f, 0.0f, 0.008f, 0.39f, 0.393f, 0.776f, 0.0f, 0.009f, 0.399f, 0.41f, + 0.779f, 0.0f, 0.011f, 0.407f, 0.425f, 0.783f, 0.0f, 0.015f, 0.415f, 0.434f, 0.787f, 0.0f, 0.019f, 0.423f, 0.44f, 0.792f, 0.0f, 0.024f, 0.429f, 0.441f, + 0.797f, 0.0f, 0.03f, 0.435f, 0.444f, 0.802f, 0.0f, 0.037f, 0.441f, 0.447f, 0.807f, 0.0f, 0.044f, 0.445f, 0.453f, 0.813f, 0.0f, 0.051f, 0.449f, 0.457f, + 0.819f, 0.0f, 0.058f, 0.452f, 0.458f, 0.825f, 0.0f, 0.066f, 0.455f, 0.46f, 0.831f, 0.0f, 0.074f, 0.457f, 0.462f, 0.838f, 0.0f, 0.082f, 0.459f, 0.462f, + 0.845f, 0.0f, 0.09f, 0.461f, 0.462f, 0.852f, 0.0f, 0.098f, 0.462f, 0.463f, 0.859f, 0.0f, 0.106f, 0.463f, 0.464f, 0.867f, 0.0f, 0.113f, 0.464f, 0.464f, + 0.874f, 0.0f, 0.121f, 0.465f, 0.465f, 0.882f, 0.0f, 0.129f, 0.465f, 0.465f, 0.889f, 0.0f, 0.136f, 0.466f, 0.466f, 0.897f, 0.0f, 0.143f, 0.466f, 0.467f, + 0.904f, 0.0f, 0.15f, 0.467f, 0.466f, 0.911f, 0.0f, 0.157f, 0.467f, 0.467f, 0.916f, 0.0f, 0.163f, 0.468f, 0.468f, 0.921f, 0.0f, 0.169f, 0.468f, 0.469f, + 0.924f, 0.0f, 0.173f, 0.468f, 0.469f, 0.926f, 0.0f, 0.177f, 0.469f, 0.468f, 0.925f, 0.0f, 0.18f, 0.469f, 0.468f, 0.922f, 0.0f, 0.181f, 0.469f, 0.469f, + 0.918f, 0.0f, 0.181f, 0.469f, 0.469f, 0.912f, 0.0f, 0.18f, 0.469f, 0.469f, 0.905f, 0.0f, 0.178f, 0.468f, 0.47f, 0.898f, 0.0f, 0.175f, 0.466f, 0.471f, + 0.89f, 0.0f, 0.172f, 0.462f, 0.469f, 0.882f, 0.0f, 0.168f, 0.454f, 0.468f, 0.874f, 0.0f, 0.164f, 0.442f, 0.467f, 0.866f, 0.0f, 0.159f, 0.423f, 0.467f, + 0.858f, 0.0f, 0.154f, 0.398f, 0.468f, 0.851f, 0.0f, 0.149f, 0.366f, 0.468f, 0.844f, 0.0f, 0.144f, 0.326f, 0.469f, 0.837f, 0.0f, 0.139f, 0.282f, 0.469f, + 0.83f, 0.0f, 0.134f, 0.231f, 0.467f, 0.824f, 0.0f, 0.13f, 0.184f, 0.415f, 0.816f, 0.0f, 0.124f, 0.111f, 0.111f, +}; + +static const float data19[100 * GP_PRIM_DATABUF_SIZE] = { + -0.279f, 0.0f, 0.568f, 0.154f, 0.154f, -0.266f, 0.0f, 0.569f, 0.249f, 0.318f, -0.258f, 0.0f, 0.57f, 0.296f, 0.357f, -0.248f, 0.0f, 0.571f, 0.337f, 0.383f, + -0.238f, 0.0f, 0.571f, 0.363f, 0.396f, -0.229f, 0.0f, 0.571f, 0.381f, 0.403f, -0.219f, 0.0f, 0.57f, 0.392f, 0.407f, -0.209f, 0.0f, 0.568f, 0.399f, 0.407f, + -0.2f, 0.0f, 0.566f, 0.403f, 0.408f, -0.19f, 0.0f, 0.563f, 0.406f, 0.41f, -0.181f, 0.0f, 0.559f, 0.407f, 0.41f, -0.171f, 0.0f, 0.555f, 0.409f, 0.41f, + -0.161f, 0.0f, 0.551f, 0.409f, 0.411f, -0.152f, 0.0f, 0.546f, 0.41f, 0.411f, -0.142f, 0.0f, 0.542f, 0.41f, 0.412f, -0.132f, 0.0f, 0.537f, 0.411f, 0.411f, + -0.122f, 0.0f, 0.533f, 0.411f, 0.411f, -0.112f, 0.0f, 0.528f, 0.411f, 0.412f, -0.102f, 0.0f, 0.524f, 0.411f, 0.412f, -0.092f, 0.0f, 0.519f, 0.41f, 0.412f, + -0.081f, 0.0f, 0.515f, 0.407f, 0.411f, -0.071f, 0.0f, 0.511f, 0.403f, 0.408f, -0.061f, 0.0f, 0.507f, 0.399f, 0.401f, -0.051f, 0.0f, 0.503f, 0.394f, 0.395f, + -0.041f, 0.0f, 0.499f, 0.39f, 0.388f, -0.031f, 0.0f, 0.495f, 0.386f, 0.383f, -0.021f, 0.0f, 0.491f, 0.383f, 0.38f, -0.011f, 0.0f, 0.488f, 0.381f, 0.378f, + -0.001f, 0.0f, 0.486f, 0.379f, 0.377f, 0.009f, 0.0f, 0.484f, 0.378f, 0.377f, 0.019f, 0.0f, 0.483f, 0.377f, 0.375f, 0.03f, 0.0f, 0.482f, 0.377f, 0.375f, + 0.041f, 0.0f, 0.482f, 0.378f, 0.376f, 0.051f, 0.0f, 0.483f, 0.379f, 0.376f, 0.062f, 0.0f, 0.484f, 0.381f, 0.376f, 0.073f, 0.0f, 0.486f, 0.385f, 0.379f, + 0.085f, 0.0f, 0.488f, 0.389f, 0.382f, 0.096f, 0.0f, 0.491f, 0.395f, 0.392f, 0.108f, 0.0f, 0.494f, 0.402f, 0.4f, 0.12f, 0.0f, 0.497f, 0.409f, 0.409f, + 0.132f, 0.0f, 0.501f, 0.415f, 0.416f, 0.144f, 0.0f, 0.505f, 0.421f, 0.427f, 0.157f, 0.0f, 0.509f, 0.425f, 0.43f, 0.17f, 0.0f, 0.513f, 0.429f, 0.433f, + 0.181f, 0.0f, 0.517f, 0.431f, 0.433f, 0.192f, 0.0f, 0.52f, 0.433f, 0.434f, 0.201f, 0.0f, 0.522f, 0.433f, 0.435f, 0.208f, 0.0f, 0.524f, 0.433f, 0.435f, + 0.213f, 0.0f, 0.524f, 0.432f, 0.436f, 0.216f, 0.0f, 0.523f, 0.431f, 0.435f, 0.217f, 0.0f, 0.521f, 0.43f, 0.426f, 0.215f, 0.0f, 0.518f, 0.429f, 0.427f, + 0.213f, 0.0f, 0.515f, 0.428f, 0.427f, 0.208f, 0.0f, 0.511f, 0.428f, 0.427f, 0.203f, 0.0f, 0.506f, 0.428f, 0.427f, 0.196f, 0.0f, 0.502f, 0.428f, 0.427f, + 0.189f, 0.0f, 0.497f, 0.428f, 0.427f, 0.181f, 0.0f, 0.492f, 0.428f, 0.427f, 0.173f, 0.0f, 0.487f, 0.428f, 0.428f, 0.163f, 0.0f, 0.482f, 0.429f, 0.428f, + 0.154f, 0.0f, 0.477f, 0.429f, 0.429f, 0.145f, 0.0f, 0.472f, 0.43f, 0.43f, 0.135f, 0.0f, 0.467f, 0.431f, 0.431f, 0.125f, 0.0f, 0.462f, 0.432f, 0.43f, + 0.116f, 0.0f, 0.457f, 0.433f, 0.431f, 0.106f, 0.0f, 0.453f, 0.435f, 0.434f, 0.096f, 0.0f, 0.448f, 0.436f, 0.436f, 0.086f, 0.0f, 0.444f, 0.437f, 0.438f, + 0.076f, 0.0f, 0.44f, 0.438f, 0.44f, 0.065f, 0.0f, 0.436f, 0.439f, 0.441f, 0.055f, 0.0f, 0.433f, 0.44f, 0.441f, 0.044f, 0.0f, 0.431f, 0.441f, 0.442f, + 0.033f, 0.0f, 0.429f, 0.441f, 0.442f, 0.022f, 0.0f, 0.427f, 0.441f, 0.442f, 0.011f, 0.0f, 0.426f, 0.442f, 0.443f, -0.0f, 0.0f, 0.426f, 0.442f, 0.442f, + -0.011f, 0.0f, 0.426f, 0.442f, 0.442f, -0.022f, 0.0f, 0.427f, 0.442f, 0.442f, -0.033f, 0.0f, 0.429f, 0.442f, 0.442f, -0.042f, 0.0f, 0.432f, 0.441f, 0.442f, + -0.052f, 0.0f, 0.435f, 0.441f, 0.441f, -0.061f, 0.0f, 0.439f, 0.441f, 0.441f, -0.07f, 0.0f, 0.443f, 0.441f, 0.441f, -0.078f, 0.0f, 0.448f, 0.441f, 0.441f, + -0.087f, 0.0f, 0.453f, 0.441f, 0.442f, -0.095f, 0.0f, 0.458f, 0.441f, 0.441f, -0.104f, 0.0f, 0.463f, 0.44f, 0.44f, -0.113f, 0.0f, 0.468f, 0.44f, 0.44f, + -0.122f, 0.0f, 0.473f, 0.44f, 0.44f, -0.132f, 0.0f, 0.479f, 0.44f, 0.44f, -0.143f, 0.0f, 0.485f, 0.44f, 0.44f, -0.154f, 0.0f, 0.491f, 0.44f, 0.44f, + -0.165f, 0.0f, 0.498f, 0.44f, 0.44f, -0.176f, 0.0f, 0.504f, 0.439f, 0.439f, -0.187f, 0.0f, 0.51f, 0.435f, 0.44f, -0.198f, 0.0f, 0.516f, 0.424f, 0.44f, + -0.209f, 0.0f, 0.522f, 0.393f, 0.44f, -0.219f, 0.0f, 0.527f, 0.324f, 0.44f, -0.228f, 0.0f, 0.532f, 0.222f, 0.404f, -0.241f, 0.0f, 0.538f, 0.037f, 0.037f, +}; + +static const float data20[136 * GP_PRIM_DATABUF_SIZE] = { + 0.331f, 0.0f, -0.036f, 0.065f, 0.065f, 0.322f, 0.0f, -0.034f, 0.239f, 0.293f, 0.317f, 0.0f, -0.032f, 0.316f, 0.339f, 0.31f, 0.0f, -0.029f, 0.348f, 0.355f, + 0.302f, 0.0f, -0.027f, 0.364f, 0.368f, 0.295f, 0.0f, -0.023f, 0.373f, 0.374f, 0.287f, 0.0f, -0.02f, 0.381f, 0.381f, 0.279f, 0.0f, -0.015f, 0.388f, 0.391f, + 0.271f, 0.0f, -0.01f, 0.392f, 0.394f, 0.263f, 0.0f, -0.004f, 0.395f, 0.396f, 0.255f, 0.0f, 0.002f, 0.397f, 0.397f, 0.247f, 0.0f, 0.008f, 0.399f, 0.4f, + 0.239f, 0.0f, 0.016f, 0.401f, 0.401f, 0.232f, -0.0f, 0.024f, 0.404f, 0.404f, 0.226f, 0.0f, 0.031f, 0.406f, 0.407f, 0.221f, 0.0f, 0.038f, 0.409f, 0.409f, + 0.215f, 0.0f, 0.045f, 0.412f, 0.412f, 0.21f, 0.0f, 0.054f, 0.415f, 0.415f, 0.205f, 0.0f, 0.063f, 0.417f, 0.417f, 0.201f, 0.0f, 0.073f, 0.42f, 0.421f, + 0.197f, 0.0f, 0.083f, 0.421f, 0.421f, 0.193f, -0.0f, 0.094f, 0.423f, 0.423f, 0.19f, -0.0f, 0.104f, 0.424f, 0.424f, 0.187f, -0.0f, 0.114f, 0.424f, 0.425f, + 0.185f, -0.0f, 0.125f, 0.425f, 0.425f, 0.183f, -0.0f, 0.135f, 0.425f, 0.425f, 0.182f, -0.0f, 0.146f, 0.426f, 0.425f, 0.181f, -0.0f, 0.157f, 0.426f, 0.425f, + 0.18f, -0.0f, 0.168f, 0.426f, 0.426f, 0.18f, -0.0f, 0.179f, 0.427f, 0.427f, 0.181f, -0.0f, 0.189f, 0.427f, 0.427f, 0.182f, -0.0f, 0.199f, 0.427f, 0.427f, + 0.183f, -0.0f, 0.208f, 0.427f, 0.428f, 0.185f, -0.0f, 0.218f, 0.428f, 0.427f, 0.187f, -0.0f, 0.226f, 0.428f, 0.427f, 0.19f, -0.0f, 0.235f, 0.429f, 0.427f, + 0.192f, -0.0f, 0.243f, 0.43f, 0.428f, 0.196f, -0.0f, 0.252f, 0.431f, 0.431f, 0.199f, -0.0f, 0.26f, 0.431f, 0.432f, 0.203f, -0.0f, 0.268f, 0.432f, 0.433f, + 0.207f, -0.0f, 0.276f, 0.433f, 0.433f, 0.212f, -0.0f, 0.283f, 0.434f, 0.434f, 0.216f, -0.0f, 0.291f, 0.434f, 0.435f, 0.221f, -0.0f, 0.298f, 0.435f, 0.436f, + 0.227f, -0.0f, 0.305f, 0.435f, 0.435f, 0.232f, -0.0f, 0.311f, 0.436f, 0.436f, 0.238f, -0.0f, 0.317f, 0.436f, 0.436f, 0.243f, -0.0f, 0.323f, 0.436f, 0.436f, + 0.249f, -0.0f, 0.329f, 0.437f, 0.436f, 0.255f, -0.0f, 0.334f, 0.438f, 0.437f, 0.262f, -0.0f, 0.339f, 0.44f, 0.437f, 0.268f, -0.0f, 0.344f, 0.442f, 0.441f, + 0.274f, 0.0f, 0.348f, 0.444f, 0.446f, 0.281f, 0.0f, 0.352f, 0.445f, 0.447f, 0.287f, 0.0f, 0.355f, 0.446f, 0.447f, 0.293f, 0.0f, 0.358f, 0.446f, 0.447f, + 0.299f, 0.0f, 0.361f, 0.447f, 0.447f, 0.306f, 0.0f, 0.363f, 0.447f, 0.448f, 0.312f, 0.0f, 0.366f, 0.447f, 0.448f, 0.318f, 0.0f, 0.368f, 0.448f, 0.448f, + 0.325f, 0.0f, 0.369f, 0.448f, 0.448f, 0.331f, 0.0f, 0.371f, 0.448f, 0.448f, 0.338f, 0.0f, 0.372f, 0.448f, 0.448f, 0.345f, 0.0f, 0.372f, 0.448f, 0.448f, + 0.352f, 0.0f, 0.372f, 0.448f, 0.448f, 0.359f, 0.0f, 0.372f, 0.448f, 0.449f, 0.366f, 0.0f, 0.371f, 0.448f, 0.448f, 0.373f, 0.0f, 0.37f, 0.448f, 0.449f, + 0.38f, 0.0f, 0.369f, 0.449f, 0.449f, 0.387f, 0.0f, 0.367f, 0.449f, 0.449f, 0.393f, 0.0f, 0.365f, 0.449f, 0.449f, 0.4f, 0.0f, 0.363f, 0.449f, 0.45f, + 0.406f, 0.0f, 0.36f, 0.45f, 0.45f, 0.412f, -0.0f, 0.357f, 0.45f, 0.45f, 0.418f, -0.0f, 0.354f, 0.45f, 0.451f, 0.424f, -0.0f, 0.351f, 0.45f, 0.451f, + 0.43f, -0.0f, 0.347f, 0.45f, 0.451f, 0.436f, -0.0f, 0.343f, 0.45f, 0.451f, 0.443f, -0.0f, 0.339f, 0.45f, 0.45f, 0.449f, -0.0f, 0.334f, 0.45f, 0.451f, + 0.455f, -0.0f, 0.329f, 0.451f, 0.451f, 0.46f, -0.0f, 0.323f, 0.451f, 0.451f, 0.466f, -0.0f, 0.318f, 0.451f, 0.451f, 0.472f, -0.0f, 0.311f, 0.452f, 0.452f, + 0.477f, -0.0f, 0.305f, 0.452f, 0.453f, 0.482f, -0.0f, 0.298f, 0.452f, 0.453f, 0.487f, -0.0f, 0.291f, 0.453f, 0.453f, 0.492f, -0.0f, 0.284f, 0.453f, 0.453f, + 0.496f, -0.0f, 0.277f, 0.453f, 0.453f, 0.5f, -0.0f, 0.269f, 0.453f, 0.454f, 0.504f, -0.0f, 0.261f, 0.453f, 0.454f, 0.508f, -0.0f, 0.252f, 0.454f, 0.454f, + 0.511f, -0.0f, 0.244f, 0.454f, 0.454f, 0.514f, -0.0f, 0.235f, 0.454f, 0.455f, 0.517f, -0.0f, 0.225f, 0.454f, 0.455f, 0.519f, -0.0f, 0.216f, 0.454f, 0.455f, + 0.521f, -0.0f, 0.205f, 0.455f, 0.455f, 0.523f, -0.0f, 0.194f, 0.455f, 0.455f, 0.524f, -0.0f, 0.182f, 0.455f, 0.455f, 0.524f, -0.0f, 0.169f, 0.455f, 0.456f, + 0.524f, -0.0f, 0.157f, 0.455f, 0.456f, 0.523f, -0.0f, 0.145f, 0.455f, 0.456f, 0.522f, -0.0f, 0.133f, 0.455f, 0.456f, 0.52f, -0.0f, 0.122f, 0.456f, 0.456f, + 0.518f, -0.0f, 0.11f, 0.456f, 0.456f, 0.515f, -0.0f, 0.1f, 0.456f, 0.456f, 0.513f, -0.0f, 0.09f, 0.456f, 0.457f, 0.509f, -0.0f, 0.081f, 0.456f, 0.457f, + 0.506f, -0.0f, 0.072f, 0.457f, 0.457f, 0.502f, -0.0f, 0.064f, 0.457f, 0.457f, 0.498f, -0.0f, 0.056f, 0.457f, 0.457f, 0.494f, -0.0f, 0.049f, 0.457f, 0.457f, + 0.49f, -0.0f, 0.041f, 0.458f, 0.457f, 0.485f, -0.0f, 0.034f, 0.458f, 0.457f, 0.48f, -0.0f, 0.028f, 0.458f, 0.458f, 0.475f, -0.0f, 0.022f, 0.458f, 0.458f, + 0.47f, -0.0f, 0.016f, 0.458f, 0.458f, 0.465f, -0.0f, 0.011f, 0.459f, 0.458f, 0.46f, -0.0f, 0.006f, 0.459f, 0.458f, 0.454f, -0.0f, 0.001f, 0.46f, 0.459f, + 0.449f, 0.0f, -0.003f, 0.464f, 0.463f, 0.443f, 0.0f, -0.007f, 0.467f, 0.468f, 0.438f, 0.0f, -0.011f, 0.469f, 0.469f, 0.432f, 0.0f, -0.015f, 0.471f, 0.47f, + 0.426f, 0.0f, -0.018f, 0.477f, 0.478f, 0.42f, 0.0f, -0.021f, 0.478f, 0.478f, 0.414f, 0.0f, -0.024f, 0.478f, 0.478f, 0.408f, 0.0f, -0.027f, 0.479f, 0.479f, + 0.402f, 0.0f, -0.029f, 0.48f, 0.48f, 0.395f, 0.0f, -0.031f, 0.48f, 0.48f, 0.389f, 0.0f, -0.033f, 0.482f, 0.482f, 0.382f, 0.0f, -0.035f, 0.482f, 0.482f, + 0.376f, 0.0f, -0.036f, 0.482f, 0.482f, 0.369f, 0.0f, -0.037f, 0.48f, 0.482f, 0.364f, 0.0f, -0.037f, 0.457f, 0.485f, 0.356f, 0.0f, -0.038f, 0.32f, 0.32f, +}; + +static const float data21[353 * GP_PRIM_DATABUF_SIZE] = { + -0.382f, 0.0f, 0.397f, 0.0f, 1.0f, -0.386f, 0.0f, 0.394f, 0.0f, 1.0f, -0.389f, 0.0f, 0.392f, 0.0f, 1.0f, -0.392f, 0.0f, 0.39f, 0.0f, 1.0f, + -0.395f, 0.0f, 0.388f, 0.0f, 1.0f, -0.399f, 0.0f, 0.385f, 0.0f, 1.0f, -0.402f, 0.0f, 0.383f, 0.0f, 1.0f, -0.405f, 0.0f, 0.381f, 0.0f, 1.0f, + -0.408f, 0.0f, 0.379f, 0.0f, 1.0f, -0.411f, 0.0f, 0.377f, 0.0f, 1.0f, -0.414f, 0.0f, 0.375f, 0.0f, 1.0f, -0.417f, 0.0f, 0.372f, 0.0f, 1.0f, + -0.42f, 0.0f, 0.37f, 0.0f, 1.0f, -0.423f, 0.0f, 0.368f, 0.0f, 1.0f, -0.425f, 0.0f, 0.366f, 0.0f, 1.0f, -0.428f, 0.0f, 0.364f, 0.0f, 1.0f, + -0.431f, 0.0f, 0.362f, 0.0f, 1.0f, -0.433f, 0.0f, 0.359f, 0.0f, 1.0f, -0.436f, 0.0f, 0.357f, 0.0f, 1.0f, -0.438f, 0.0f, 0.355f, 0.0f, 1.0f, + -0.441f, 0.0f, 0.353f, 0.0f, 1.0f, -0.443f, 0.0f, 0.351f, 0.0f, 1.0f, -0.445f, 0.0f, 0.349f, 0.0f, 1.0f, -0.447f, 0.0f, 0.346f, 0.0f, 1.0f, + -0.45f, 0.0f, 0.344f, 0.0f, 1.0f, -0.452f, 0.0f, 0.342f, 0.0f, 1.0f, -0.454f, 0.0f, 0.34f, 0.0f, 1.0f, -0.456f, 0.0f, 0.337f, 0.0f, 1.0f, + -0.458f, 0.0f, 0.335f, 0.0f, 1.0f, -0.46f, 0.0f, 0.333f, 0.0f, 1.0f, -0.462f, 0.0f, 0.33f, 0.0f, 1.0f, -0.464f, 0.0f, 0.328f, 0.0f, 1.0f, + -0.466f, 0.0f, 0.326f, 0.0f, 1.0f, -0.468f, 0.0f, 0.323f, 0.0f, 1.0f, -0.47f, 0.0f, 0.321f, 0.0f, 1.0f, -0.472f, 0.0f, 0.319f, 0.0f, 1.0f, + -0.474f, 0.0f, 0.316f, 0.0f, 1.0f, -0.475f, 0.0f, 0.314f, 0.0f, 1.0f, -0.477f, 0.0f, 0.311f, 0.0f, 1.0f, -0.479f, 0.0f, 0.309f, 0.0f, 1.0f, + -0.481f, 0.0f, 0.307f, 0.0f, 1.0f, -0.482f, 0.0f, 0.304f, 0.0f, 1.0f, -0.484f, 0.0f, 0.302f, 0.0f, 1.0f, -0.486f, 0.0f, 0.299f, 0.0f, 1.0f, + -0.487f, 0.0f, 0.297f, 0.0f, 1.0f, -0.489f, 0.0f, 0.294f, 0.0f, 1.0f, -0.49f, 0.0f, 0.292f, 0.0f, 1.0f, -0.492f, 0.0f, 0.289f, 0.0f, 1.0f, + -0.494f, 0.0f, 0.286f, 0.0f, 1.0f, -0.495f, 0.0f, 0.284f, 0.0f, 1.0f, -0.497f, 0.0f, 0.281f, 0.0f, 1.0f, -0.498f, 0.0f, 0.279f, 0.001f, 1.0f, + -0.499f, 0.0f, 0.276f, 0.001f, 1.0f, -0.501f, 0.0f, 0.273f, 0.002f, 1.0f, -0.502f, 0.0f, 0.271f, 0.003f, 1.0f, -0.504f, 0.0f, 0.268f, 0.005f, 1.0f, + -0.505f, 0.0f, 0.265f, 0.008f, 1.0f, -0.506f, 0.0f, 0.262f, 0.011f, 1.0f, -0.508f, 0.0f, 0.259f, 0.016f, 1.0f, -0.509f, 0.0f, 0.256f, 0.021f, 1.0f, + -0.51f, 0.0f, 0.254f, 0.027f, 1.0f, -0.512f, 0.0f, 0.251f, 0.035f, 1.0f, -0.513f, 0.0f, 0.248f, 0.043f, 1.0f, -0.514f, 0.0f, 0.245f, 0.053f, 1.0f, + -0.515f, 0.0f, 0.242f, 0.064f, 1.0f, -0.516f, 0.0f, 0.239f, 0.076f, 1.0f, -0.517f, 0.0f, 0.235f, 0.09f, 1.0f, -0.519f, 0.0f, 0.232f, 0.105f, 1.0f, + -0.52f, 0.0f, 0.229f, 0.122f, 1.0f, -0.521f, 0.0f, 0.226f, 0.14f, 1.0f, -0.521f, 0.0f, 0.222f, 0.159f, 1.0f, -0.522f, 0.0f, 0.219f, 0.179f, 1.0f, + -0.523f, 0.0f, 0.216f, 0.2f, 1.0f, -0.524f, 0.0f, 0.212f, 0.221f, 1.0f, -0.525f, 0.0f, 0.209f, 0.243f, 1.0f, -0.526f, 0.0f, 0.205f, 0.265f, 1.0f, + -0.526f, 0.0f, 0.202f, 0.286f, 1.0f, -0.527f, 0.0f, 0.198f, 0.306f, 1.0f, -0.527f, 0.0f, 0.195f, 0.326f, 1.0f, -0.528f, 0.0f, 0.191f, 0.345f, 1.0f, + -0.528f, 0.0f, 0.187f, 0.363f, 1.0f, -0.529f, 0.0f, 0.184f, 0.38f, 1.0f, -0.529f, 0.0f, 0.18f, 0.395f, 1.0f, -0.529f, 0.0f, 0.176f, 0.41f, 1.0f, + -0.53f, 0.0f, 0.173f, 0.424f, 1.0f, -0.53f, 0.0f, 0.169f, 0.438f, 1.0f, -0.53f, 0.0f, 0.165f, 0.452f, 1.0f, -0.53f, 0.0f, 0.161f, 0.465f, 1.0f, + -0.53f, 0.0f, 0.157f, 0.478f, 1.0f, -0.53f, 0.0f, 0.154f, 0.492f, 1.0f, -0.53f, 0.0f, 0.15f, 0.505f, 1.0f, -0.53f, 0.0f, 0.146f, 0.517f, 1.0f, + -0.53f, 0.0f, 0.142f, 0.53f, 1.0f, -0.529f, 0.0f, 0.138f, 0.542f, 1.0f, -0.529f, 0.0f, 0.134f, 0.553f, 1.0f, -0.528f, 0.0f, 0.13f, 0.564f, 1.0f, + -0.528f, 0.0f, 0.127f, 0.574f, 1.0f, -0.527f, 0.0f, 0.123f, 0.583f, 1.0f, -0.527f, 0.0f, 0.119f, 0.592f, 1.0f, -0.526f, 0.0f, 0.115f, 0.6f, 1.0f, + -0.526f, 0.0f, 0.111f, 0.608f, 1.0f, -0.525f, 0.0f, 0.108f, 0.615f, 1.0f, -0.524f, 0.0f, 0.104f, 0.622f, 1.0f, -0.523f, 0.0f, 0.1f, 0.628f, 1.0f, + -0.522f, 0.0f, 0.097f, 0.635f, 1.0f, -0.521f, 0.0f, 0.093f, 0.641f, 1.0f, -0.52f, 0.0f, 0.089f, 0.647f, 1.0f, -0.519f, 0.0f, 0.086f, 0.653f, 1.0f, + -0.518f, 0.0f, 0.082f, 0.659f, 1.0f, -0.517f, 0.0f, 0.079f, 0.664f, 1.0f, -0.515f, 0.0f, 0.075f, 0.67f, 1.0f, -0.514f, 0.0f, 0.072f, 0.675f, 1.0f, + -0.513f, 0.0f, 0.069f, 0.68f, 1.0f, -0.511f, 0.0f, 0.065f, 0.685f, 1.0f, -0.51f, 0.0f, 0.062f, 0.69f, 1.0f, -0.509f, 0.0f, 0.059f, 0.695f, 1.0f, + -0.507f, 0.0f, 0.056f, 0.7f, 1.0f, -0.505f, 0.0f, 0.053f, 0.704f, 1.0f, -0.504f, 0.0f, 0.049f, 0.709f, 1.0f, -0.502f, 0.0f, 0.046f, 0.714f, 1.0f, + -0.5f, 0.0f, 0.043f, 0.719f, 1.0f, -0.499f, 0.0f, 0.04f, 0.724f, 1.0f, -0.497f, 0.0f, 0.038f, 0.73f, 1.0f, -0.495f, 0.0f, 0.035f, 0.735f, 1.0f, + -0.493f, 0.0f, 0.032f, 0.741f, 1.0f, -0.491f, 0.0f, 0.029f, 0.748f, 1.0f, -0.489f, 0.0f, 0.026f, 0.754f, 1.0f, -0.488f, -0.0f, 0.024f, 0.76f, 1.0f, + -0.486f, -0.0f, 0.022f, 0.767f, 1.0f, -0.485f, -0.0f, 0.019f, 0.773f, 1.0f, -0.483f, -0.0f, 0.017f, 0.779f, 1.0f, -0.482f, -0.0f, 0.015f, 0.785f, 1.0f, + -0.48f, -0.0f, 0.013f, 0.79f, 1.0f, -0.478f, -0.0f, 0.01f, 0.795f, 1.0f, -0.476f, -0.0f, 0.008f, 0.8f, 1.0f, -0.474f, -0.0f, 0.006f, 0.804f, 1.0f, + -0.472f, -0.0f, 0.004f, 0.808f, 1.0f, -0.47f, -0.0f, 0.002f, 0.811f, 1.0f, -0.468f, -0.0f, -0.0f, 0.814f, 1.0f, -0.466f, -0.0f, -0.002f, 0.816f, 1.0f, + -0.464f, -0.0f, -0.004f, 0.818f, 1.0f, -0.461f, -0.0f, -0.006f, 0.82f, 1.0f, -0.459f, -0.0f, -0.008f, 0.822f, 1.0f, -0.456f, -0.0f, -0.01f, 0.823f, 1.0f, + -0.454f, -0.0f, -0.012f, 0.825f, 1.0f, -0.451f, -0.0f, -0.014f, 0.826f, 1.0f, -0.448f, -0.0f, -0.016f, 0.827f, 1.0f, -0.445f, -0.0f, -0.018f, 0.828f, 1.0f, + -0.442f, -0.0f, -0.02f, 0.829f, 1.0f, -0.439f, -0.0f, -0.022f, 0.829f, 1.0f, -0.436f, -0.0f, -0.024f, 0.83f, 1.0f, -0.433f, -0.0f, -0.026f, 0.83f, 1.0f, + -0.43f, -0.0f, -0.027f, 0.83f, 1.0f, -0.426f, -0.0f, -0.029f, 0.83f, 1.0f, -0.423f, 0.0f, -0.031f, 0.83f, 1.0f, -0.42f, 0.0f, -0.032f, 0.83f, 1.0f, + -0.417f, 0.0f, -0.033f, 0.831f, 1.0f, -0.414f, 0.0f, -0.034f, 0.831f, 1.0f, -0.411f, 0.0f, -0.035f, 0.831f, 1.0f, -0.408f, 0.0f, -0.037f, 0.831f, 1.0f, + -0.405f, 0.0f, -0.038f, 0.831f, 1.0f, -0.402f, 0.0f, -0.039f, 0.831f, 1.0f, -0.399f, 0.0f, -0.039f, 0.831f, 1.0f, -0.396f, 0.0f, -0.04f, 0.832f, 1.0f, + -0.393f, 0.0f, -0.041f, 0.832f, 1.0f, -0.389f, 0.0f, -0.042f, 0.832f, 1.0f, -0.386f, 0.0f, -0.043f, 0.832f, 1.0f, -0.383f, 0.0f, -0.044f, 0.832f, 1.0f, + -0.379f, 0.0f, -0.044f, 0.832f, 1.0f, -0.376f, 0.0f, -0.045f, 0.832f, 1.0f, -0.372f, 0.0f, -0.045f, 0.832f, 1.0f, -0.369f, 0.0f, -0.046f, 0.832f, 1.0f, + -0.366f, 0.0f, -0.047f, 0.832f, 1.0f, -0.362f, 0.0f, -0.047f, 0.832f, 1.0f, -0.359f, 0.0f, -0.047f, 0.831f, 1.0f, -0.355f, 0.0f, -0.048f, 0.831f, 1.0f, + -0.352f, 0.0f, -0.048f, 0.83f, 1.0f, -0.348f, 0.0f, -0.048f, 0.83f, 1.0f, -0.345f, 0.0f, -0.049f, 0.829f, 1.0f, -0.341f, 0.0f, -0.049f, 0.828f, 1.0f, + -0.338f, 0.0f, -0.049f, 0.827f, 1.0f, -0.334f, 0.0f, -0.049f, 0.826f, 1.0f, -0.331f, 0.0f, -0.049f, 0.823f, 1.0f, -0.327f, 0.0f, -0.049f, 0.82f, 1.0f, + -0.323f, 0.0f, -0.048f, 0.816f, 1.0f, -0.32f, 0.0f, -0.048f, 0.811f, 1.0f, -0.316f, 0.0f, -0.048f, 0.804f, 1.0f, -0.313f, 0.0f, -0.048f, 0.797f, 1.0f, + -0.309f, 0.0f, -0.047f, 0.79f, 1.0f, -0.306f, 0.0f, -0.047f, 0.782f, 1.0f, -0.302f, 0.0f, -0.046f, 0.774f, 1.0f, -0.299f, 0.0f, -0.045f, 0.767f, 1.0f, + -0.295f, 0.0f, -0.044f, 0.76f, 1.0f, -0.292f, 0.0f, -0.044f, 0.753f, 1.0f, -0.288f, 0.0f, -0.043f, 0.748f, 1.0f, -0.285f, 0.0f, -0.042f, 0.742f, 1.0f, + -0.282f, 0.0f, -0.041f, 0.738f, 1.0f, -0.278f, 0.0f, -0.04f, 0.734f, 1.0f, -0.275f, 0.0f, -0.039f, 0.73f, 1.0f, -0.272f, 0.0f, -0.037f, 0.726f, 1.0f, + -0.269f, 0.0f, -0.036f, 0.723f, 1.0f, -0.266f, 0.0f, -0.035f, 0.72f, 1.0f, -0.263f, 0.0f, -0.034f, 0.717f, 1.0f, -0.26f, 0.0f, -0.032f, 0.713f, 1.0f, + -0.257f, 0.0f, -0.031f, 0.71f, 1.0f, -0.255f, 0.0f, -0.029f, 0.706f, 1.0f, -0.252f, 0.0f, -0.028f, 0.702f, 1.0f, -0.249f, 0.0f, -0.026f, 0.698f, 1.0f, + -0.247f, 0.0f, -0.025f, 0.693f, 1.0f, -0.244f, 0.0f, -0.023f, 0.688f, 1.0f, -0.242f, 0.0f, -0.021f, 0.684f, 1.0f, -0.239f, 0.0f, -0.02f, 0.679f, 1.0f, + -0.237f, 0.0f, -0.018f, 0.675f, 1.0f, -0.234f, 0.0f, -0.016f, 0.671f, 1.0f, -0.232f, 0.0f, -0.014f, 0.667f, 1.0f, -0.23f, 0.0f, -0.013f, 0.663f, 1.0f, + -0.228f, 0.0f, -0.011f, 0.66f, 1.0f, -0.225f, 0.0f, -0.009f, 0.657f, 1.0f, -0.223f, 0.0f, -0.007f, 0.654f, 1.0f, -0.221f, 0.0f, -0.005f, 0.651f, 1.0f, + -0.219f, 0.0f, -0.003f, 0.649f, 1.0f, -0.217f, 0.0f, -0.001f, 0.645f, 1.0f, -0.215f, 0.0f, 0.002f, 0.642f, 1.0f, -0.213f, 0.0f, 0.004f, 0.639f, 1.0f, + -0.211f, 0.0f, 0.006f, 0.635f, 1.0f, -0.209f, 0.0f, 0.008f, 0.631f, 1.0f, -0.207f, 0.0f, 0.011f, 0.627f, 1.0f, -0.206f, 0.0f, 0.013f, 0.623f, 1.0f, + -0.204f, 0.0f, 0.016f, 0.619f, 1.0f, -0.202f, 0.0f, 0.018f, 0.615f, 1.0f, -0.2f, 0.0f, 0.021f, 0.61f, 1.0f, -0.199f, 0.0f, 0.023f, 0.606f, 1.0f, + -0.197f, 0.0f, 0.026f, 0.602f, 1.0f, -0.195f, 0.0f, 0.029f, 0.598f, 1.0f, -0.194f, 0.0f, 0.032f, 0.595f, 1.0f, -0.192f, 0.0f, 0.034f, 0.592f, 1.0f, + -0.191f, 0.0f, 0.037f, 0.589f, 1.0f, -0.19f, 0.0f, 0.04f, 0.587f, 1.0f, -0.188f, 0.0f, 0.043f, 0.585f, 1.0f, -0.187f, 0.0f, 0.046f, 0.584f, 1.0f, + -0.186f, 0.0f, 0.05f, 0.583f, 1.0f, -0.185f, 0.0f, 0.053f, 0.582f, 1.0f, -0.183f, 0.0f, 0.056f, 0.581f, 1.0f, -0.182f, 0.0f, 0.059f, 0.581f, 1.0f, + -0.181f, 0.0f, 0.062f, 0.581f, 1.0f, -0.18f, 0.0f, 0.066f, 0.581f, 1.0f, -0.179f, 0.0f, 0.069f, 0.58f, 1.0f, -0.178f, 0.0f, 0.072f, 0.58f, 1.0f, + -0.177f, 0.0f, 0.076f, 0.58f, 1.0f, -0.177f, 0.0f, 0.079f, 0.58f, 1.0f, -0.176f, 0.0f, 0.083f, 0.58f, 1.0f, -0.175f, 0.0f, 0.086f, 0.58f, 1.0f, + -0.174f, 0.0f, 0.09f, 0.58f, 1.0f, -0.174f, 0.0f, 0.093f, 0.58f, 1.0f, -0.173f, 0.0f, 0.097f, 0.58f, 1.0f, -0.172f, 0.0f, 0.1f, 0.58f, 1.0f, + -0.172f, 0.0f, 0.104f, 0.58f, 1.0f, -0.171f, 0.0f, 0.108f, 0.579f, 1.0f, -0.171f, 0.0f, 0.111f, 0.579f, 1.0f, -0.17f, 0.0f, 0.115f, 0.578f, 1.0f, + -0.17f, 0.0f, 0.119f, 0.578f, 1.0f, -0.17f, 0.0f, 0.122f, 0.577f, 1.0f, -0.169f, 0.0f, 0.126f, 0.577f, 1.0f, -0.169f, 0.0f, 0.13f, 0.576f, 1.0f, + -0.169f, 0.0f, 0.134f, 0.576f, 1.0f, -0.169f, 0.0f, 0.137f, 0.575f, 1.0f, -0.169f, 0.0f, 0.141f, 0.575f, 1.0f, -0.169f, 0.0f, 0.145f, 0.574f, 1.0f, + -0.169f, 0.0f, 0.149f, 0.572f, 1.0f, -0.169f, 0.0f, 0.153f, 0.571f, 1.0f, -0.169f, 0.0f, 0.157f, 0.569f, 1.0f, -0.169f, 0.0f, 0.16f, 0.566f, 1.0f, + -0.169f, 0.0f, 0.164f, 0.562f, 1.0f, -0.17f, 0.0f, 0.168f, 0.558f, 1.0f, -0.17f, 0.0f, 0.172f, 0.553f, 1.0f, -0.17f, 0.0f, 0.176f, 0.547f, 1.0f, + -0.171f, 0.0f, 0.18f, 0.539f, 1.0f, -0.171f, 0.0f, 0.183f, 0.531f, 1.0f, -0.172f, 0.0f, 0.187f, 0.522f, 1.0f, -0.172f, 0.0f, 0.191f, 0.513f, 1.0f, + -0.173f, 0.0f, 0.194f, 0.503f, 1.0f, -0.173f, 0.0f, 0.198f, 0.493f, 1.0f, -0.174f, 0.0f, 0.202f, 0.483f, 1.0f, -0.175f, 0.0f, 0.205f, 0.473f, 1.0f, + -0.176f, 0.0f, 0.209f, 0.464f, 1.0f, -0.177f, 0.0f, 0.212f, 0.455f, 1.0f, -0.178f, 0.0f, 0.215f, 0.446f, 1.0f, -0.178f, 0.0f, 0.219f, 0.438f, 1.0f, + -0.179f, 0.0f, 0.222f, 0.428f, 1.0f, -0.18f, 0.0f, 0.226f, 0.418f, 1.0f, -0.182f, 0.0f, 0.229f, 0.407f, 1.0f, -0.183f, 0.0f, 0.232f, 0.394f, 1.0f, + -0.184f, 0.0f, 0.236f, 0.38f, 1.0f, -0.185f, 0.0f, 0.239f, 0.364f, 1.0f, -0.186f, 0.0f, 0.242f, 0.348f, 1.0f, -0.187f, 0.0f, 0.245f, 0.33f, 1.0f, + -0.188f, 0.0f, 0.249f, 0.311f, 1.0f, -0.19f, 0.0f, 0.252f, 0.293f, 1.0f, -0.191f, 0.0f, 0.255f, 0.275f, 1.0f, -0.192f, 0.0f, 0.258f, 0.258f, 1.0f, + -0.194f, 0.0f, 0.261f, 0.242f, 1.0f, -0.195f, 0.0f, 0.264f, 0.228f, 1.0f, -0.196f, 0.0f, 0.267f, 0.214f, 1.0f, -0.198f, 0.0f, 0.27f, 0.202f, 1.0f, + -0.199f, 0.0f, 0.273f, 0.191f, 1.0f, -0.201f, 0.0f, 0.276f, 0.181f, 1.0f, -0.202f, 0.0f, 0.279f, 0.171f, 1.0f, -0.204f, 0.0f, 0.282f, 0.162f, 1.0f, + -0.205f, 0.0f, 0.285f, 0.152f, 1.0f, -0.206f, 0.0f, 0.287f, 0.143f, 1.0f, -0.208f, 0.0f, 0.29f, 0.134f, 1.0f, -0.21f, 0.0f, 0.293f, 0.126f, 1.0f, + -0.211f, 0.0f, 0.295f, 0.117f, 1.0f, -0.213f, 0.0f, 0.298f, 0.109f, 1.0f, -0.214f, 0.0f, 0.301f, 0.101f, 1.0f, -0.216f, 0.0f, 0.303f, 0.094f, 1.0f, + -0.217f, 0.0f, 0.306f, 0.087f, 1.0f, -0.219f, 0.0f, 0.308f, 0.081f, 1.0f, -0.221f, 0.0f, 0.311f, 0.076f, 1.0f, -0.223f, 0.0f, 0.313f, 0.071f, 1.0f, + -0.224f, 0.0f, 0.316f, 0.067f, 1.0f, -0.226f, 0.0f, 0.318f, 0.065f, 1.0f, -0.228f, 0.0f, 0.321f, 0.062f, 1.0f, -0.23f, 0.0f, 0.323f, 0.061f, 1.0f, + -0.232f, 0.0f, 0.326f, 0.06f, 1.0f, -0.233f, 0.0f, 0.328f, 0.06f, 1.0f, -0.235f, 0.0f, 0.331f, 0.061f, 1.0f, -0.237f, 0.0f, 0.334f, 0.061f, 1.0f, + -0.239f, 0.0f, 0.336f, 0.062f, 1.0f, -0.241f, 0.0f, 0.339f, 0.063f, 1.0f, -0.243f, 0.0f, 0.341f, 0.064f, 1.0f, -0.245f, 0.0f, 0.344f, 0.065f, 1.0f, + -0.248f, 0.0f, 0.346f, 0.065f, 1.0f, -0.25f, 0.0f, 0.349f, 0.065f, 1.0f, -0.252f, 0.0f, 0.351f, 0.064f, 1.0f, -0.254f, 0.0f, 0.354f, 0.062f, 1.0f, + -0.256f, 0.0f, 0.356f, 0.06f, 1.0f, -0.258f, 0.0f, 0.359f, 0.058f, 1.0f, -0.261f, 0.0f, 0.361f, 0.055f, 1.0f, -0.263f, 0.0f, 0.364f, 0.051f, 1.0f, + -0.265f, 0.0f, 0.366f, 0.046f, 1.0f, -0.267f, 0.0f, 0.368f, 0.04f, 1.0f, -0.269f, 0.0f, 0.37f, 0.034f, 1.0f, -0.272f, 0.0f, 0.373f, 0.027f, 1.0f, + -0.274f, 0.0f, 0.375f, 0.019f, 1.0f, -0.276f, 0.0f, 0.377f, 0.012f, 1.0f, -0.278f, 0.0f, 0.379f, 0.007f, 1.0f, -0.28f, 0.0f, 0.381f, 0.003f, 1.0f, + -0.282f, 0.0f, 0.383f, 0.001f, 1.0f, -0.284f, 0.0f, 0.385f, 0.0f, 1.0f, -0.286f, 0.0f, 0.387f, 0.0f, 1.0f, -0.287f, 0.0f, 0.388f, 0.0f, 1.0f, + -0.289f, 0.0f, 0.39f, 0.0f, 1.0f, +}; + +static const float data22[309 * GP_PRIM_DATABUF_SIZE] = { + 0.294f, 0.0f, 0.372f, 0.0f, 1.0f, 0.291f, 0.0f, 0.37f, 0.001f, 1.0f, 0.289f, 0.0f, 0.368f, 0.002f, 1.0f, 0.286f, 0.0f, 0.366f, 0.003f, 1.0f, + 0.284f, 0.0f, 0.364f, 0.006f, 1.0f, 0.282f, 0.0f, 0.362f, 0.01f, 1.0f, 0.279f, 0.0f, 0.36f, 0.015f, 1.0f, 0.277f, 0.0f, 0.358f, 0.022f, 1.0f, + 0.274f, 0.0f, 0.356f, 0.03f, 1.0f, 0.272f, 0.0f, 0.353f, 0.04f, 1.0f, 0.269f, 0.0f, 0.351f, 0.051f, 1.0f, 0.267f, 0.0f, 0.349f, 0.062f, 1.0f, + 0.265f, 0.0f, 0.347f, 0.074f, 1.0f, 0.262f, 0.0f, 0.344f, 0.086f, 1.0f, 0.26f, 0.0f, 0.342f, 0.097f, 1.0f, 0.258f, 0.0f, 0.34f, 0.108f, 1.0f, + 0.256f, 0.0f, 0.337f, 0.119f, 1.0f, 0.253f, 0.0f, 0.335f, 0.128f, 1.0f, 0.251f, 0.0f, 0.333f, 0.137f, 1.0f, 0.249f, 0.0f, 0.33f, 0.145f, 1.0f, + 0.247f, 0.0f, 0.328f, 0.153f, 1.0f, 0.246f, 0.0f, 0.325f, 0.161f, 1.0f, 0.244f, 0.0f, 0.323f, 0.168f, 1.0f, 0.242f, 0.0f, 0.321f, 0.176f, 1.0f, + 0.24f, 0.0f, 0.318f, 0.183f, 1.0f, 0.239f, 0.0f, 0.316f, 0.191f, 1.0f, 0.237f, 0.0f, 0.314f, 0.198f, 1.0f, 0.235f, 0.0f, 0.311f, 0.206f, 1.0f, + 0.233f, 0.0f, 0.309f, 0.214f, 1.0f, 0.231f, 0.0f, 0.306f, 0.223f, 1.0f, 0.23f, 0.0f, 0.304f, 0.231f, 1.0f, 0.228f, 0.0f, 0.301f, 0.24f, 1.0f, + 0.226f, 0.0f, 0.299f, 0.248f, 1.0f, 0.224f, 0.0f, 0.296f, 0.256f, 1.0f, 0.223f, 0.0f, 0.294f, 0.264f, 1.0f, 0.221f, 0.0f, 0.291f, 0.272f, 1.0f, + 0.219f, 0.0f, 0.288f, 0.28f, 1.0f, 0.218f, 0.0f, 0.286f, 0.287f, 1.0f, 0.216f, 0.0f, 0.283f, 0.294f, 1.0f, 0.214f, 0.0f, 0.281f, 0.301f, 1.0f, + 0.213f, 0.0f, 0.278f, 0.307f, 1.0f, 0.211f, 0.0f, 0.275f, 0.314f, 1.0f, 0.21f, 0.0f, 0.273f, 0.32f, 1.0f, 0.208f, 0.0f, 0.27f, 0.327f, 1.0f, + 0.206f, 0.0f, 0.267f, 0.333f, 1.0f, 0.205f, 0.0f, 0.265f, 0.339f, 1.0f, 0.204f, 0.0f, 0.262f, 0.345f, 1.0f, 0.202f, 0.0f, 0.259f, 0.351f, 1.0f, + 0.201f, 0.0f, 0.256f, 0.357f, 1.0f, 0.199f, 0.0f, 0.253f, 0.362f, 1.0f, 0.198f, 0.0f, 0.25f, 0.368f, 1.0f, 0.197f, 0.0f, 0.247f, 0.373f, 1.0f, + 0.195f, 0.0f, 0.244f, 0.379f, 1.0f, 0.194f, 0.0f, 0.241f, 0.383f, 1.0f, 0.193f, 0.0f, 0.238f, 0.388f, 1.0f, 0.192f, 0.0f, 0.235f, 0.392f, 1.0f, + 0.191f, 0.0f, 0.232f, 0.396f, 1.0f, 0.19f, 0.0f, 0.229f, 0.399f, 1.0f, 0.189f, 0.0f, 0.226f, 0.402f, 1.0f, 0.188f, 0.0f, 0.222f, 0.405f, 1.0f, + 0.187f, 0.0f, 0.219f, 0.407f, 1.0f, 0.186f, 0.0f, 0.216f, 0.409f, 1.0f, 0.185f, 0.0f, 0.213f, 0.411f, 1.0f, 0.184f, 0.0f, 0.209f, 0.412f, 1.0f, + 0.183f, 0.0f, 0.206f, 0.413f, 1.0f, 0.183f, 0.0f, 0.203f, 0.414f, 1.0f, 0.182f, 0.0f, 0.199f, 0.415f, 1.0f, 0.181f, 0.0f, 0.196f, 0.416f, 1.0f, + 0.181f, 0.0f, 0.193f, 0.417f, 1.0f, 0.18f, 0.0f, 0.189f, 0.417f, 1.0f, 0.18f, 0.0f, 0.186f, 0.418f, 1.0f, 0.179f, 0.0f, 0.182f, 0.419f, 1.0f, + 0.179f, 0.0f, 0.179f, 0.421f, 1.0f, 0.179f, 0.0f, 0.176f, 0.422f, 1.0f, 0.178f, 0.0f, 0.172f, 0.423f, 1.0f, 0.178f, 0.0f, 0.169f, 0.425f, 1.0f, + 0.178f, 0.0f, 0.165f, 0.427f, 1.0f, 0.178f, 0.0f, 0.162f, 0.429f, 1.0f, 0.178f, 0.0f, 0.158f, 0.431f, 1.0f, 0.178f, 0.0f, 0.155f, 0.434f, 1.0f, + 0.178f, 0.0f, 0.152f, 0.436f, 1.0f, 0.178f, 0.0f, 0.148f, 0.439f, 1.0f, 0.178f, 0.0f, 0.145f, 0.442f, 1.0f, 0.178f, 0.0f, 0.141f, 0.446f, 1.0f, + 0.178f, 0.0f, 0.138f, 0.449f, 1.0f, 0.178f, 0.0f, 0.134f, 0.453f, 1.0f, 0.178f, 0.0f, 0.131f, 0.458f, 1.0f, 0.179f, 0.0f, 0.127f, 0.462f, 1.0f, + 0.179f, 0.0f, 0.124f, 0.467f, 1.0f, 0.179f, 0.0f, 0.12f, 0.472f, 1.0f, 0.18f, 0.0f, 0.117f, 0.478f, 1.0f, 0.18f, 0.0f, 0.113f, 0.483f, 1.0f, + 0.181f, 0.0f, 0.11f, 0.489f, 1.0f, 0.182f, 0.0f, 0.106f, 0.494f, 1.0f, 0.182f, 0.0f, 0.103f, 0.5f, 1.0f, 0.183f, 0.0f, 0.099f, 0.505f, 1.0f, + 0.184f, 0.0f, 0.096f, 0.511f, 1.0f, 0.185f, 0.0f, 0.092f, 0.516f, 1.0f, 0.185f, 0.0f, 0.089f, 0.521f, 1.0f, 0.186f, 0.0f, 0.086f, 0.525f, 1.0f, + 0.187f, 0.0f, 0.082f, 0.53f, 1.0f, 0.188f, 0.0f, 0.079f, 0.534f, 1.0f, 0.189f, 0.0f, 0.076f, 0.537f, 1.0f, 0.191f, 0.0f, 0.073f, 0.541f, 1.0f, + 0.192f, 0.0f, 0.069f, 0.544f, 1.0f, 0.193f, 0.0f, 0.066f, 0.547f, 1.0f, 0.194f, 0.0f, 0.063f, 0.55f, 1.0f, 0.196f, 0.0f, 0.061f, 0.553f, 1.0f, + 0.197f, 0.0f, 0.058f, 0.556f, 1.0f, 0.198f, 0.0f, 0.055f, 0.559f, 1.0f, 0.2f, 0.0f, 0.052f, 0.562f, 1.0f, 0.201f, 0.0f, 0.049f, 0.564f, 1.0f, + 0.203f, 0.0f, 0.047f, 0.566f, 1.0f, 0.205f, 0.0f, 0.044f, 0.569f, 1.0f, 0.206f, 0.0f, 0.042f, 0.571f, 1.0f, 0.208f, 0.0f, 0.039f, 0.573f, 1.0f, + 0.21f, 0.0f, 0.037f, 0.575f, 1.0f, 0.212f, 0.0f, 0.035f, 0.576f, 1.0f, 0.214f, 0.0f, 0.032f, 0.578f, 1.0f, 0.215f, 0.0f, 0.03f, 0.579f, 1.0f, + 0.217f, 0.0f, 0.028f, 0.581f, 1.0f, 0.22f, 0.0f, 0.025f, 0.582f, 1.0f, 0.222f, 0.0f, 0.023f, 0.583f, 1.0f, 0.224f, 0.0f, 0.021f, 0.585f, 1.0f, + 0.226f, 0.0f, 0.019f, 0.587f, 1.0f, 0.228f, 0.0f, 0.016f, 0.589f, 1.0f, 0.231f, 0.0f, 0.014f, 0.592f, 1.0f, 0.233f, 0.0f, 0.012f, 0.596f, 1.0f, + 0.236f, 0.0f, 0.01f, 0.599f, 1.0f, 0.238f, 0.0f, 0.008f, 0.604f, 1.0f, 0.241f, 0.0f, 0.006f, 0.608f, 1.0f, 0.243f, 0.0f, 0.004f, 0.612f, 1.0f, + 0.246f, 0.0f, 0.002f, 0.615f, 1.0f, 0.249f, 0.0f, 0.0f, 0.619f, 1.0f, 0.251f, 0.0f, -0.002f, 0.622f, 1.0f, 0.254f, 0.0f, -0.003f, 0.624f, 1.0f, + 0.257f, 0.0f, -0.005f, 0.626f, 1.0f, 0.26f, 0.0f, -0.007f, 0.628f, 1.0f, 0.263f, 0.0f, -0.008f, 0.63f, 1.0f, 0.266f, 0.0f, -0.01f, 0.632f, 1.0f, + 0.269f, 0.0f, -0.011f, 0.634f, 1.0f, 0.272f, 0.0f, -0.013f, 0.636f, 1.0f, 0.275f, 0.0f, -0.014f, 0.638f, 1.0f, 0.278f, 0.0f, -0.015f, 0.64f, 1.0f, + 0.281f, 0.0f, -0.017f, 0.642f, 1.0f, 0.284f, 0.0f, -0.018f, 0.644f, 1.0f, 0.288f, 0.0f, -0.019f, 0.647f, 1.0f, 0.291f, 0.0f, -0.02f, 0.649f, 1.0f, + 0.294f, 0.0f, -0.021f, 0.651f, 1.0f, 0.297f, 0.0f, -0.022f, 0.653f, 1.0f, 0.301f, 0.0f, -0.023f, 0.656f, 1.0f, 0.304f, 0.0f, -0.024f, 0.658f, 1.0f, + 0.307f, 0.0f, -0.025f, 0.659f, 1.0f, 0.31f, 0.0f, -0.026f, 0.661f, 1.0f, 0.314f, 0.0f, -0.027f, 0.662f, 1.0f, 0.317f, 0.0f, -0.027f, 0.664f, 1.0f, + 0.32f, 0.0f, -0.028f, 0.665f, 1.0f, 0.324f, 0.0f, -0.028f, 0.665f, 1.0f, 0.327f, 0.0f, -0.029f, 0.666f, 1.0f, 0.33f, 0.0f, -0.029f, 0.666f, 1.0f, + 0.334f, 0.0f, -0.029f, 0.667f, 1.0f, 0.337f, 0.0f, -0.03f, 0.667f, 1.0f, 0.341f, 0.0f, -0.03f, 0.668f, 1.0f, 0.344f, 0.0f, -0.03f, 0.668f, 1.0f, + 0.348f, 0.0f, -0.03f, 0.668f, 1.0f, 0.351f, 0.0f, -0.03f, 0.668f, 1.0f, 0.354f, 0.0f, -0.03f, 0.668f, 1.0f, 0.358f, 0.0f, -0.029f, 0.668f, 1.0f, + 0.361f, 0.0f, -0.029f, 0.668f, 1.0f, 0.365f, 0.0f, -0.029f, 0.668f, 1.0f, 0.368f, 0.0f, -0.028f, 0.668f, 1.0f, 0.372f, 0.0f, -0.028f, 0.668f, 1.0f, + 0.375f, 0.0f, -0.027f, 0.668f, 1.0f, 0.378f, 0.0f, -0.027f, 0.668f, 1.0f, 0.382f, 0.0f, -0.026f, 0.667f, 1.0f, 0.385f, 0.0f, -0.025f, 0.667f, 1.0f, + 0.388f, 0.0f, -0.025f, 0.666f, 1.0f, 0.392f, 0.0f, -0.024f, 0.666f, 1.0f, 0.395f, 0.0f, -0.023f, 0.665f, 1.0f, 0.398f, 0.0f, -0.022f, 0.664f, 1.0f, + 0.401f, 0.0f, -0.021f, 0.664f, 1.0f, 0.405f, 0.0f, -0.02f, 0.663f, 1.0f, 0.408f, 0.0f, -0.019f, 0.663f, 1.0f, 0.411f, 0.0f, -0.018f, 0.662f, 1.0f, + 0.414f, 0.0f, -0.017f, 0.662f, 1.0f, 0.417f, 0.0f, -0.016f, 0.662f, 1.0f, 0.42f, 0.0f, -0.015f, 0.662f, 1.0f, 0.423f, 0.0f, -0.014f, 0.661f, 1.0f, + 0.426f, 0.0f, -0.012f, 0.661f, 1.0f, 0.429f, 0.0f, -0.011f, 0.661f, 1.0f, 0.432f, 0.0f, -0.01f, 0.661f, 1.0f, 0.434f, 0.0f, -0.009f, 0.66f, 1.0f, + 0.437f, 0.0f, -0.007f, 0.66f, 1.0f, 0.44f, 0.0f, -0.006f, 0.659f, 1.0f, 0.442f, 0.0f, -0.005f, 0.659f, 1.0f, 0.445f, 0.0f, -0.003f, 0.658f, 1.0f, + 0.448f, 0.0f, -0.002f, 0.658f, 1.0f, 0.45f, 0.0f, -0.001f, 0.657f, 1.0f, 0.452f, 0.0f, 0.001f, 0.656f, 1.0f, 0.455f, 0.0f, 0.002f, 0.655f, 1.0f, + 0.457f, 0.0f, 0.004f, 0.654f, 1.0f, 0.459f, 0.0f, 0.005f, 0.653f, 1.0f, 0.462f, 0.0f, 0.007f, 0.652f, 1.0f, 0.464f, 0.0f, 0.009f, 0.651f, 1.0f, + 0.466f, 0.0f, 0.01f, 0.65f, 1.0f, 0.468f, 0.0f, 0.012f, 0.65f, 1.0f, 0.47f, 0.0f, 0.014f, 0.649f, 1.0f, 0.472f, 0.0f, 0.016f, 0.648f, 1.0f, + 0.474f, 0.0f, 0.018f, 0.647f, 1.0f, 0.476f, 0.0f, 0.019f, 0.646f, 1.0f, 0.478f, 0.0f, 0.021f, 0.645f, 1.0f, 0.479f, 0.0f, 0.023f, 0.644f, 1.0f, + 0.481f, 0.0f, 0.025f, 0.643f, 1.0f, 0.483f, 0.0f, 0.027f, 0.642f, 1.0f, 0.485f, 0.0f, 0.03f, 0.642f, 1.0f, 0.486f, 0.0f, 0.032f, 0.641f, 1.0f, + 0.488f, 0.0f, 0.034f, 0.64f, 1.0f, 0.49f, 0.0f, 0.036f, 0.639f, 1.0f, 0.491f, 0.0f, 0.038f, 0.638f, 1.0f, 0.493f, 0.0f, 0.041f, 0.637f, 1.0f, + 0.494f, 0.0f, 0.043f, 0.636f, 1.0f, 0.496f, 0.0f, 0.045f, 0.635f, 1.0f, 0.497f, 0.0f, 0.048f, 0.635f, 1.0f, 0.499f, 0.0f, 0.05f, 0.634f, 1.0f, + 0.5f, 0.0f, 0.053f, 0.633f, 1.0f, 0.502f, 0.0f, 0.055f, 0.632f, 1.0f, 0.503f, 0.0f, 0.058f, 0.631f, 1.0f, 0.505f, 0.0f, 0.06f, 0.63f, 1.0f, + 0.506f, 0.0f, 0.063f, 0.63f, 1.0f, 0.507f, 0.0f, 0.066f, 0.629f, 1.0f, 0.509f, 0.0f, 0.068f, 0.628f, 1.0f, 0.51f, 0.0f, 0.071f, 0.628f, 1.0f, + 0.511f, 0.0f, 0.074f, 0.627f, 1.0f, 0.513f, 0.0f, 0.077f, 0.626f, 1.0f, 0.514f, 0.0f, 0.079f, 0.625f, 1.0f, 0.515f, 0.0f, 0.082f, 0.625f, 1.0f, + 0.516f, 0.0f, 0.085f, 0.624f, 1.0f, 0.518f, 0.0f, 0.088f, 0.623f, 1.0f, 0.519f, 0.0f, 0.091f, 0.622f, 1.0f, 0.52f, 0.0f, 0.094f, 0.62f, 1.0f, + 0.521f, 0.0f, 0.098f, 0.619f, 1.0f, 0.522f, 0.0f, 0.101f, 0.617f, 1.0f, 0.523f, 0.0f, 0.104f, 0.615f, 1.0f, 0.524f, 0.0f, 0.107f, 0.613f, 1.0f, + 0.525f, 0.0f, 0.111f, 0.611f, 1.0f, 0.526f, 0.0f, 0.114f, 0.609f, 1.0f, 0.527f, 0.0f, 0.118f, 0.607f, 1.0f, 0.527f, 0.0f, 0.121f, 0.605f, 1.0f, + 0.528f, 0.0f, 0.124f, 0.603f, 1.0f, 0.529f, 0.0f, 0.128f, 0.602f, 1.0f, 0.529f, 0.0f, 0.132f, 0.6f, 1.0f, 0.53f, 0.0f, 0.135f, 0.599f, 1.0f, + 0.531f, 0.0f, 0.139f, 0.598f, 1.0f, 0.531f, 0.0f, 0.142f, 0.598f, 1.0f, 0.531f, 0.0f, 0.146f, 0.597f, 1.0f, 0.532f, 0.0f, 0.15f, 0.596f, 1.0f, + 0.532f, 0.0f, 0.154f, 0.596f, 1.0f, 0.532f, 0.0f, 0.157f, 0.595f, 1.0f, 0.532f, 0.0f, 0.161f, 0.595f, 1.0f, 0.532f, 0.0f, 0.165f, 0.594f, 1.0f, + 0.532f, 0.0f, 0.169f, 0.593f, 1.0f, 0.532f, 0.0f, 0.173f, 0.592f, 1.0f, 0.532f, 0.0f, 0.177f, 0.591f, 1.0f, 0.532f, 0.0f, 0.181f, 0.59f, 1.0f, + 0.531f, 0.0f, 0.185f, 0.589f, 1.0f, 0.531f, 0.0f, 0.189f, 0.588f, 1.0f, 0.53f, 0.0f, 0.194f, 0.587f, 1.0f, 0.529f, 0.0f, 0.198f, 0.586f, 1.0f, + 0.528f, 0.0f, 0.202f, 0.585f, 1.0f, 0.527f, 0.0f, 0.207f, 0.584f, 1.0f, 0.526f, 0.0f, 0.211f, 0.584f, 1.0f, 0.525f, 0.0f, 0.215f, 0.583f, 1.0f, + 0.523f, 0.0f, 0.22f, 0.583f, 1.0f, 0.522f, 0.0f, 0.224f, 0.583f, 1.0f, 0.52f, 0.0f, 0.229f, 0.582f, 1.0f, 0.518f, 0.0f, 0.234f, 0.582f, 1.0f, + 0.515f, 0.0f, 0.238f, 0.582f, 1.0f, 0.513f, 0.0f, 0.243f, 0.581f, 1.0f, 0.51f, 0.0f, 0.247f, 0.58f, 1.0f, 0.508f, 0.0f, 0.252f, 0.579f, 1.0f, + 0.505f, 0.0f, 0.257f, 0.578f, 1.0f, 0.502f, 0.0f, 0.261f, 0.576f, 1.0f, 0.499f, 0.0f, 0.266f, 0.573f, 1.0f, 0.496f, 0.0f, 0.27f, 0.57f, 1.0f, + 0.492f, 0.0f, 0.275f, 0.566f, 1.0f, 0.489f, 0.0f, 0.279f, 0.561f, 1.0f, 0.485f, 0.0f, 0.284f, 0.555f, 1.0f, 0.481f, 0.0f, 0.288f, 0.548f, 1.0f, + 0.478f, 0.0f, 0.293f, 0.54f, 1.0f, 0.473f, 0.0f, 0.297f, 0.531f, 1.0f, 0.469f, 0.0f, 0.301f, 0.521f, 1.0f, 0.465f, 0.0f, 0.305f, 0.509f, 1.0f, + 0.461f, 0.0f, 0.309f, 0.496f, 1.0f, 0.456f, 0.0f, 0.313f, 0.481f, 1.0f, 0.452f, 0.0f, 0.317f, 0.464f, 1.0f, 0.448f, 0.0f, 0.321f, 0.445f, 1.0f, + 0.443f, 0.0f, 0.324f, 0.424f, 1.0f, 0.438f, 0.0f, 0.328f, 0.401f, 1.0f, 0.434f, 0.0f, 0.331f, 0.374f, 1.0f, 0.429f, 0.0f, 0.334f, 0.346f, 1.0f, + 0.425f, 0.0f, 0.337f, 0.314f, 1.0f, 0.421f, 0.0f, 0.34f, 0.281f, 1.0f, 0.416f, 0.0f, 0.343f, 0.245f, 1.0f, 0.412f, 0.0f, 0.346f, 0.208f, 1.0f, + 0.408f, 0.0f, 0.349f, 0.169f, 1.0f, 0.404f, 0.0f, 0.351f, 0.13f, 1.0f, 0.401f, 0.0f, 0.354f, 0.089f, 1.0f, 0.398f, 0.0f, 0.356f, 0.054f, 1.0f, + 0.394f, 0.0f, 0.359f, 0.0f, 1.0f, +}; + +static const float data23[209 * GP_PRIM_DATABUF_SIZE] = { + -0.751f, 0.0f, 0.173f, 0.0f, 1.0f, -0.751f, 0.0f, 0.168f, 0.0f, 1.0f, -0.75f, 0.0f, 0.164f, 0.0f, 1.0f, -0.75f, 0.0f, 0.16f, 0.0f, 1.0f, + -0.75f, 0.0f, 0.156f, 0.0f, 1.0f, -0.749f, 0.0f, 0.152f, 0.0f, 1.0f, -0.749f, 0.0f, 0.148f, 0.0f, 1.0f, -0.748f, 0.0f, 0.144f, 0.0f, 1.0f, + -0.747f, 0.0f, 0.14f, 0.001f, 1.0f, -0.747f, 0.0f, 0.137f, 0.002f, 1.0f, -0.746f, 0.0f, 0.133f, 0.005f, 1.0f, -0.745f, 0.0f, 0.129f, 0.008f, 1.0f, + -0.745f, 0.0f, 0.125f, 0.013f, 1.0f, -0.744f, 0.0f, 0.122f, 0.02f, 1.0f, -0.743f, 0.0f, 0.118f, 0.028f, 1.0f, -0.742f, 0.0f, 0.115f, 0.038f, 1.0f, + -0.741f, 0.0f, 0.111f, 0.049f, 1.0f, -0.74f, 0.0f, 0.108f, 0.061f, 1.0f, -0.739f, 0.0f, 0.105f, 0.073f, 1.0f, -0.738f, 0.0f, 0.101f, 0.085f, 1.0f, + -0.736f, 0.0f, 0.098f, 0.097f, 1.0f, -0.735f, 0.0f, 0.095f, 0.109f, 1.0f, -0.734f, 0.0f, 0.091f, 0.119f, 1.0f, -0.732f, 0.0f, 0.088f, 0.129f, 1.0f, + -0.731f, 0.0f, 0.085f, 0.138f, 1.0f, -0.729f, 0.0f, 0.082f, 0.146f, 1.0f, -0.728f, 0.0f, 0.079f, 0.153f, 1.0f, -0.726f, 0.0f, 0.076f, 0.158f, 1.0f, + -0.725f, 0.0f, 0.073f, 0.163f, 1.0f, -0.723f, 0.0f, 0.07f, 0.167f, 1.0f, -0.722f, 0.0f, 0.067f, 0.17f, 1.0f, -0.72f, 0.0f, 0.065f, 0.173f, 1.0f, + -0.718f, 0.0f, 0.062f, 0.174f, 1.0f, -0.717f, 0.0f, 0.059f, 0.175f, 1.0f, -0.715f, 0.0f, 0.057f, 0.176f, 1.0f, -0.714f, 0.0f, 0.054f, 0.176f, 1.0f, + -0.712f, 0.0f, 0.051f, 0.176f, 1.0f, -0.71f, 0.0f, 0.049f, 0.176f, 1.0f, -0.709f, 0.0f, 0.046f, 0.176f, 1.0f, -0.707f, 0.0f, 0.043f, 0.176f, 1.0f, + -0.705f, 0.0f, 0.041f, 0.176f, 1.0f, -0.703f, 0.0f, 0.038f, 0.176f, 1.0f, -0.701f, 0.0f, 0.035f, 0.176f, 1.0f, -0.7f, 0.0f, 0.033f, 0.177f, 1.0f, + -0.698f, 0.0f, 0.03f, 0.177f, 1.0f, -0.696f, 0.0f, 0.027f, 0.178f, 1.0f, -0.694f, 0.0f, 0.024f, 0.179f, 1.0f, -0.692f, 0.0f, 0.022f, 0.18f, 1.0f, + -0.69f, 0.0f, 0.019f, 0.181f, 1.0f, -0.688f, 0.0f, 0.016f, 0.182f, 1.0f, -0.685f, 0.0f, 0.013f, 0.184f, 1.0f, -0.683f, 0.0f, 0.01f, 0.187f, 1.0f, + -0.681f, 0.0f, 0.007f, 0.19f, 1.0f, -0.679f, 0.0f, 0.004f, 0.194f, 1.0f, -0.677f, 0.0f, 0.001f, 0.198f, 1.0f, -0.675f, 0.0f, -0.002f, 0.203f, 1.0f, + -0.673f, 0.0f, -0.005f, 0.209f, 1.0f, -0.67f, 0.0f, -0.008f, 0.215f, 1.0f, -0.668f, 0.0f, -0.011f, 0.222f, 1.0f, -0.666f, 0.0f, -0.014f, 0.229f, 1.0f, + -0.664f, 0.0f, -0.017f, 0.237f, 1.0f, -0.661f, 0.0f, -0.02f, 0.246f, 1.0f, -0.659f, 0.0f, -0.023f, 0.255f, 1.0f, -0.657f, 0.0f, -0.025f, 0.264f, 1.0f, + -0.654f, 0.0f, -0.028f, 0.275f, 1.0f, -0.652f, 0.0f, -0.031f, 0.285f, 1.0f, -0.65f, 0.0f, -0.034f, 0.297f, 1.0f, -0.647f, 0.0f, -0.037f, 0.309f, 1.0f, + -0.644f, 0.0f, -0.04f, 0.322f, 1.0f, -0.642f, 0.0f, -0.043f, 0.335f, 1.0f, -0.639f, 0.0f, -0.046f, 0.348f, 1.0f, -0.636f, 0.0f, -0.049f, 0.361f, 1.0f, + -0.633f, 0.0f, -0.052f, 0.374f, 1.0f, -0.63f, 0.0f, -0.055f, 0.386f, 1.0f, -0.627f, 0.0f, -0.058f, 0.397f, 1.0f, -0.624f, 0.0f, -0.061f, 0.408f, 1.0f, + -0.62f, 0.0f, -0.064f, 0.418f, 1.0f, -0.617f, 0.0f, -0.067f, 0.427f, 1.0f, -0.614f, 0.0f, -0.07f, 0.435f, 1.0f, -0.611f, 0.0f, -0.073f, 0.443f, 1.0f, + -0.607f, 0.0f, -0.075f, 0.451f, 1.0f, -0.604f, 0.0f, -0.078f, 0.458f, 1.0f, -0.6f, 0.0f, -0.081f, 0.465f, 1.0f, -0.597f, 0.0f, -0.084f, 0.472f, 1.0f, + -0.593f, 0.0f, -0.086f, 0.479f, 1.0f, -0.59f, 0.0f, -0.089f, 0.486f, 1.0f, -0.586f, 0.0f, -0.092f, 0.492f, 1.0f, -0.583f, 0.0f, -0.094f, 0.499f, 1.0f, + -0.579f, 0.0f, -0.097f, 0.505f, 1.0f, -0.575f, 0.0f, -0.099f, 0.512f, 1.0f, -0.571f, 0.0f, -0.102f, 0.518f, 1.0f, -0.567f, 0.0f, -0.105f, 0.524f, 1.0f, + -0.563f, 0.0f, -0.107f, 0.53f, 1.0f, -0.559f, 0.0f, -0.11f, 0.536f, 1.0f, -0.555f, 0.0f, -0.112f, 0.541f, 1.0f, -0.551f, 0.0f, -0.115f, 0.546f, 1.0f, + -0.546f, 0.0f, -0.117f, 0.551f, 1.0f, -0.542f, 0.0f, -0.12f, 0.555f, 1.0f, -0.538f, 0.0f, -0.122f, 0.559f, 1.0f, -0.533f, 0.0f, -0.125f, 0.562f, 1.0f, + -0.529f, 0.0f, -0.127f, 0.565f, 1.0f, -0.525f, 0.0f, -0.129f, 0.568f, 1.0f, -0.52f, 0.0f, -0.132f, 0.57f, 1.0f, -0.516f, 0.0f, -0.134f, 0.572f, 1.0f, + -0.512f, 0.0f, -0.137f, 0.574f, 1.0f, -0.508f, 0.0f, -0.139f, 0.576f, 1.0f, -0.503f, 0.0f, -0.141f, 0.577f, 1.0f, -0.499f, 0.0f, -0.144f, 0.578f, 1.0f, + -0.495f, 0.0f, -0.146f, 0.579f, 1.0f, -0.491f, 0.0f, -0.148f, 0.579f, 1.0f, -0.487f, 0.0f, -0.151f, 0.578f, 1.0f, -0.483f, 0.0f, -0.153f, 0.577f, 1.0f, + -0.479f, 0.0f, -0.155f, 0.574f, 1.0f, -0.475f, 0.0f, -0.158f, 0.571f, 1.0f, -0.471f, 0.0f, -0.16f, 0.567f, 1.0f, -0.467f, 0.0f, -0.162f, 0.561f, 1.0f, + -0.463f, 0.0f, -0.165f, 0.555f, 1.0f, -0.459f, 0.0f, -0.167f, 0.548f, 1.0f, -0.456f, 0.0f, -0.169f, 0.54f, 1.0f, -0.452f, 0.0f, -0.172f, 0.532f, 1.0f, + -0.448f, 0.0f, -0.174f, 0.523f, 1.0f, -0.445f, 0.0f, -0.176f, 0.514f, 1.0f, -0.441f, 0.0f, -0.179f, 0.505f, 1.0f, -0.438f, 0.0f, -0.181f, 0.497f, 1.0f, + -0.435f, 0.0f, -0.183f, 0.488f, 1.0f, -0.431f, 0.0f, -0.185f, 0.48f, 1.0f, -0.428f, 0.0f, -0.188f, 0.472f, 1.0f, -0.425f, 0.0f, -0.19f, 0.464f, 1.0f, + -0.422f, 0.0f, -0.192f, 0.457f, 1.0f, -0.419f, 0.0f, -0.194f, 0.451f, 1.0f, -0.416f, 0.0f, -0.196f, 0.444f, 1.0f, -0.413f, 0.0f, -0.198f, 0.439f, 1.0f, + -0.41f, 0.0f, -0.2f, 0.434f, 1.0f, -0.407f, 0.0f, -0.202f, 0.429f, 1.0f, -0.404f, 0.0f, -0.204f, 0.426f, 1.0f, -0.401f, 0.0f, -0.206f, 0.422f, 1.0f, + -0.398f, 0.0f, -0.208f, 0.419f, 1.0f, -0.396f, 0.0f, -0.21f, 0.417f, 1.0f, -0.393f, 0.0f, -0.212f, 0.415f, 1.0f, -0.39f, 0.0f, -0.213f, 0.413f, 1.0f, + -0.388f, 0.0f, -0.215f, 0.412f, 1.0f, -0.385f, 0.0f, -0.217f, 0.411f, 1.0f, -0.382f, 0.0f, -0.219f, 0.41f, 1.0f, -0.38f, 0.0f, -0.221f, 0.41f, 1.0f, + -0.377f, 0.0f, -0.222f, 0.409f, 1.0f, -0.375f, 0.0f, -0.224f, 0.409f, 1.0f, -0.372f, 0.0f, -0.226f, 0.409f, 1.0f, -0.37f, 0.0f, -0.228f, 0.409f, 1.0f, + -0.367f, 0.0f, -0.229f, 0.409f, 1.0f, -0.365f, 0.0f, -0.231f, 0.409f, 1.0f, -0.362f, 0.0f, -0.233f, 0.409f, 1.0f, -0.36f, 0.0f, -0.235f, 0.409f, 1.0f, + -0.357f, 0.0f, -0.236f, 0.409f, 1.0f, -0.355f, 0.0f, -0.238f, 0.409f, 1.0f, -0.352f, 0.0f, -0.24f, 0.408f, 1.0f, -0.35f, 0.0f, -0.242f, 0.408f, 1.0f, + -0.348f, 0.0f, -0.243f, 0.407f, 1.0f, -0.345f, 0.0f, -0.245f, 0.406f, 1.0f, -0.343f, 0.0f, -0.247f, 0.405f, 1.0f, -0.34f, 0.0f, -0.249f, 0.404f, 1.0f, + -0.338f, 0.0f, -0.251f, 0.403f, 1.0f, -0.336f, 0.0f, -0.253f, 0.401f, 1.0f, -0.333f, 0.0f, -0.255f, 0.399f, 1.0f, -0.331f, 0.0f, -0.256f, 0.397f, 1.0f, + -0.329f, 0.0f, -0.258f, 0.394f, 1.0f, -0.327f, 0.0f, -0.26f, 0.391f, 1.0f, -0.324f, 0.0f, -0.262f, 0.387f, 1.0f, -0.322f, 0.0f, -0.264f, 0.383f, 1.0f, + -0.32f, 0.0f, -0.266f, 0.379f, 1.0f, -0.318f, 0.0f, -0.268f, 0.374f, 1.0f, -0.316f, 0.0f, -0.27f, 0.368f, 1.0f, -0.314f, 0.0f, -0.272f, 0.362f, 1.0f, + -0.312f, 0.0f, -0.275f, 0.356f, 1.0f, -0.309f, 0.0f, -0.277f, 0.349f, 1.0f, -0.307f, 0.0f, -0.279f, 0.341f, 1.0f, -0.305f, 0.0f, -0.281f, 0.333f, 1.0f, + -0.303f, 0.0f, -0.283f, 0.325f, 1.0f, -0.301f, 0.0f, -0.286f, 0.316f, 1.0f, -0.299f, 0.0f, -0.288f, 0.307f, 1.0f, -0.297f, 0.0f, -0.29f, 0.298f, 1.0f, + -0.295f, 0.0f, -0.293f, 0.289f, 1.0f, -0.293f, 0.0f, -0.295f, 0.279f, 1.0f, -0.291f, 0.0f, -0.298f, 0.269f, 1.0f, -0.29f, 0.0f, -0.3f, 0.259f, 1.0f, + -0.288f, 0.0f, -0.303f, 0.249f, 1.0f, -0.286f, 0.0f, -0.306f, 0.238f, 1.0f, -0.284f, 0.0f, -0.308f, 0.227f, 1.0f, -0.282f, 0.0f, -0.311f, 0.215f, 1.0f, + -0.28f, 0.0f, -0.314f, 0.203f, 1.0f, -0.278f, 0.0f, -0.317f, 0.191f, 1.0f, -0.277f, 0.0f, -0.32f, 0.178f, 1.0f, -0.275f, 0.0f, -0.323f, 0.165f, 1.0f, + -0.273f, 0.0f, -0.326f, 0.151f, 1.0f, -0.271f, 0.0f, -0.33f, 0.138f, 1.0f, -0.27f, 0.0f, -0.333f, 0.124f, 1.0f, -0.268f, 0.0f, -0.336f, 0.11f, 1.0f, + -0.267f, 0.0f, -0.34f, 0.097f, 1.0f, -0.265f, 0.0f, -0.343f, 0.085f, 1.0f, -0.264f, 0.0f, -0.346f, 0.073f, 1.0f, -0.262f, 0.0f, -0.35f, 0.062f, 1.0f, + -0.261f, 0.0f, -0.353f, 0.052f, 1.0f, -0.259f, 0.0f, -0.357f, 0.043f, 1.0f, -0.258f, 0.0f, -0.36f, 0.035f, 1.0f, -0.257f, 0.0f, -0.363f, 0.028f, 1.0f, + -0.255f, 0.0f, -0.366f, 0.021f, 1.0f, -0.254f, 0.0f, -0.369f, 0.016f, 1.0f, -0.253f, 0.0f, -0.372f, 0.01f, 1.0f, -0.252f, 0.0f, -0.375f, 0.006f, 1.0f, + -0.251f, 0.0f, -0.379f, 0.0f, 1.0f, +}; + +static const float data24[133 * GP_PRIM_DATABUF_SIZE] = { + 0.233f, 0.0f, -0.376f, 0.021f, 1.0f, 0.234f, 0.0f, -0.372f, 0.08f, 1.0f, 0.234f, 0.0f, -0.369f, 0.116f, 1.0f, 0.234f, 0.0f, -0.366f, 0.156f, 1.0f, + 0.235f, 0.0f, -0.362f, 0.191f, 1.0f, 0.236f, 0.0f, -0.359f, 0.222f, 1.0f, 0.236f, 0.0f, -0.356f, 0.248f, 1.0f, 0.237f, 0.0f, -0.353f, 0.27f, 1.0f, + 0.238f, 0.0f, -0.35f, 0.289f, 1.0f, 0.239f, 0.0f, -0.346f, 0.304f, 1.0f, 0.24f, 0.0f, -0.343f, 0.319f, 1.0f, 0.241f, 0.0f, -0.34f, 0.334f, 1.0f, + 0.242f, 0.0f, -0.337f, 0.35f, 1.0f, 0.243f, 0.0f, -0.335f, 0.367f, 1.0f, 0.244f, 0.0f, -0.332f, 0.385f, 1.0f, 0.245f, 0.0f, -0.329f, 0.401f, 1.0f, + 0.247f, 0.0f, -0.327f, 0.415f, 1.0f, 0.248f, 0.0f, -0.324f, 0.426f, 1.0f, 0.249f, 0.0f, -0.322f, 0.435f, 1.0f, 0.251f, 0.0f, -0.32f, 0.443f, 1.0f, + 0.252f, 0.0f, -0.318f, 0.449f, 1.0f, 0.254f, 0.0f, -0.316f, 0.455f, 1.0f, 0.255f, 0.0f, -0.314f, 0.461f, 1.0f, 0.257f, 0.0f, -0.312f, 0.467f, 1.0f, + 0.258f, 0.0f, -0.311f, 0.474f, 1.0f, 0.26f, 0.0f, -0.309f, 0.48f, 1.0f, 0.262f, 0.0f, -0.307f, 0.487f, 1.0f, 0.263f, 0.0f, -0.305f, 0.493f, 1.0f, + 0.265f, 0.0f, -0.303f, 0.499f, 1.0f, 0.267f, 0.0f, -0.3f, 0.505f, 1.0f, 0.269f, 0.0f, -0.298f, 0.511f, 1.0f, 0.271f, 0.0f, -0.296f, 0.518f, 1.0f, + 0.273f, 0.0f, -0.294f, 0.524f, 1.0f, 0.276f, 0.0f, -0.291f, 0.531f, 1.0f, 0.278f, 0.0f, -0.289f, 0.539f, 1.0f, 0.281f, 0.0f, -0.287f, 0.546f, 1.0f, + 0.283f, 0.0f, -0.284f, 0.552f, 1.0f, 0.286f, 0.0f, -0.281f, 0.557f, 1.0f, 0.289f, 0.0f, -0.279f, 0.561f, 1.0f, 0.292f, 0.0f, -0.276f, 0.565f, 1.0f, + 0.294f, 0.0f, -0.274f, 0.568f, 1.0f, 0.297f, 0.0f, -0.271f, 0.57f, 1.0f, 0.3f, 0.0f, -0.269f, 0.572f, 1.0f, 0.303f, 0.0f, -0.267f, 0.574f, 1.0f, + 0.306f, 0.0f, -0.264f, 0.575f, 1.0f, 0.308f, 0.0f, -0.262f, 0.576f, 1.0f, 0.311f, 0.0f, -0.26f, 0.577f, 1.0f, 0.314f, 0.0f, -0.257f, 0.578f, 1.0f, + 0.316f, 0.0f, -0.255f, 0.578f, 1.0f, 0.319f, 0.0f, -0.253f, 0.579f, 1.0f, 0.322f, 0.0f, -0.25f, 0.579f, 1.0f, 0.325f, 0.0f, -0.248f, 0.58f, 1.0f, + 0.328f, 0.0f, -0.246f, 0.58f, 1.0f, 0.331f, 0.0f, -0.243f, 0.58f, 1.0f, 0.334f, 0.0f, -0.241f, 0.58f, 1.0f, 0.337f, 0.0f, -0.239f, 0.58f, 1.0f, + 0.341f, 0.0f, -0.236f, 0.58f, 1.0f, 0.344f, 0.0f, -0.233f, 0.581f, 1.0f, 0.348f, 0.0f, -0.231f, 0.581f, 1.0f, 0.352f, 0.0f, -0.228f, 0.581f, 1.0f, + 0.356f, 0.0f, -0.225f, 0.582f, 1.0f, 0.36f, 0.0f, -0.222f, 0.582f, 1.0f, 0.365f, 0.0f, -0.219f, 0.582f, 1.0f, 0.369f, 0.0f, -0.216f, 0.582f, 1.0f, + 0.374f, 0.0f, -0.214f, 0.582f, 1.0f, 0.378f, 0.0f, -0.211f, 0.582f, 1.0f, 0.383f, 0.0f, -0.208f, 0.583f, 1.0f, 0.387f, 0.0f, -0.205f, 0.583f, 1.0f, + 0.392f, 0.0f, -0.202f, 0.583f, 1.0f, 0.397f, 0.0f, -0.199f, 0.583f, 1.0f, 0.401f, 0.0f, -0.197f, 0.583f, 1.0f, 0.406f, 0.0f, -0.194f, 0.583f, 1.0f, + 0.411f, 0.0f, -0.191f, 0.583f, 1.0f, 0.416f, 0.0f, -0.188f, 0.583f, 1.0f, 0.42f, 0.0f, -0.186f, 0.583f, 1.0f, 0.425f, 0.0f, -0.183f, 0.583f, 1.0f, + 0.43f, 0.0f, -0.18f, 0.583f, 1.0f, 0.434f, 0.0f, -0.178f, 0.583f, 1.0f, 0.439f, 0.0f, -0.175f, 0.583f, 1.0f, 0.444f, 0.0f, -0.172f, 0.583f, 1.0f, + 0.449f, 0.0f, -0.17f, 0.584f, 1.0f, 0.453f, 0.0f, -0.167f, 0.584f, 1.0f, 0.458f, 0.0f, -0.164f, 0.584f, 1.0f, 0.463f, 0.0f, -0.161f, 0.585f, 1.0f, + 0.468f, 0.0f, -0.158f, 0.585f, 1.0f, 0.473f, 0.0f, -0.155f, 0.585f, 1.0f, 0.478f, 0.0f, -0.152f, 0.585f, 1.0f, 0.483f, 0.0f, -0.149f, 0.585f, 1.0f, + 0.488f, 0.0f, -0.146f, 0.585f, 1.0f, 0.492f, 0.0f, -0.143f, 0.585f, 1.0f, 0.497f, 0.0f, -0.14f, 0.586f, 1.0f, 0.501f, 0.0f, -0.137f, 0.586f, 1.0f, + 0.506f, 0.0f, -0.134f, 0.586f, 1.0f, 0.51f, 0.0f, -0.13f, 0.586f, 1.0f, 0.515f, 0.0f, -0.127f, 0.586f, 1.0f, 0.52f, 0.0f, -0.124f, 0.586f, 1.0f, + 0.524f, 0.0f, -0.12f, 0.586f, 1.0f, 0.529f, 0.0f, -0.117f, 0.586f, 1.0f, 0.534f, 0.0f, -0.113f, 0.586f, 1.0f, 0.539f, 0.0f, -0.109f, 0.586f, 1.0f, + 0.544f, 0.0f, -0.105f, 0.586f, 1.0f, 0.55f, 0.0f, -0.1f, 0.586f, 1.0f, 0.555f, 0.0f, -0.095f, 0.586f, 1.0f, 0.561f, 0.0f, -0.09f, 0.586f, 1.0f, + 0.567f, 0.0f, -0.084f, 0.587f, 1.0f, 0.573f, 0.0f, -0.078f, 0.587f, 1.0f, 0.579f, 0.0f, -0.071f, 0.587f, 1.0f, 0.586f, 0.0f, -0.063f, 0.588f, 1.0f, + 0.593f, 0.0f, -0.055f, 0.588f, 1.0f, 0.6f, 0.0f, -0.047f, 0.588f, 1.0f, 0.607f, 0.0f, -0.038f, 0.589f, 1.0f, 0.614f, 0.0f, -0.028f, 0.589f, 1.0f, + 0.621f, 0.0f, -0.018f, 0.589f, 1.0f, 0.629f, 0.0f, -0.007f, 0.589f, 1.0f, 0.636f, 0.0f, 0.004f, 0.589f, 1.0f, 0.643f, 0.0f, 0.015f, 0.59f, 1.0f, + 0.65f, 0.0f, 0.026f, 0.589f, 1.0f, 0.656f, 0.0f, 0.038f, 0.589f, 1.0f, 0.663f, 0.0f, 0.049f, 0.588f, 1.0f, 0.669f, 0.0f, 0.06f, 0.587f, 1.0f, + 0.676f, 0.0f, 0.072f, 0.584f, 1.0f, 0.682f, 0.0f, 0.084f, 0.579f, 1.0f, 0.688f, 0.0f, 0.096f, 0.571f, 1.0f, 0.694f, 0.0f, 0.108f, 0.558f, 1.0f, + 0.7f, 0.0f, 0.12f, 0.54f, 1.0f, 0.706f, 0.0f, 0.133f, 0.514f, 1.0f, 0.712f, 0.0f, 0.145f, 0.478f, 1.0f, 0.718f, 0.0f, 0.158f, 0.431f, 1.0f, + 0.723f, 0.0f, 0.17f, 0.369f, 1.0f, 0.728f, 0.0f, 0.182f, 0.294f, 1.0f, 0.733f, 0.0f, 0.194f, 0.205f, 1.0f, 0.737f, 0.0f, 0.204f, 0.125f, 1.0f, + 0.743f, 0.0f, 0.218f, 0.0f, 1.0f, +}; + +static const float data25[389 * GP_PRIM_DATABUF_SIZE] = { + -0.284f, 0.0f, -0.444f, 0.0f, 1.0f, -0.285f, 0.0f, -0.448f, 0.0f, 1.0f, -0.285f, 0.0f, -0.45f, 0.0f, 1.0f, -0.286f, 0.0f, -0.454f, 0.0f, 1.0f, + -0.286f, 0.0f, -0.457f, 0.0f, 1.0f, -0.287f, 0.0f, -0.46f, 0.0f, 1.0f, -0.288f, 0.0f, -0.463f, 0.0f, 1.0f, -0.289f, 0.0f, -0.466f, 0.0f, 1.0f, + -0.289f, 0.0f, -0.47f, 0.0f, 1.0f, -0.29f, 0.0f, -0.473f, 0.0f, 1.0f, -0.291f, 0.0f, -0.476f, 0.0f, 1.0f, -0.292f, 0.0f, -0.48f, 0.0f, 1.0f, + -0.293f, 0.0f, -0.484f, 0.0f, 1.0f, -0.294f, 0.0f, -0.487f, 0.0f, 1.0f, -0.295f, 0.0f, -0.491f, 0.0f, 1.0f, -0.296f, 0.0f, -0.494f, 0.0f, 1.0f, + -0.297f, 0.0f, -0.498f, 0.0f, 1.0f, -0.298f, 0.0f, -0.502f, 0.0f, 1.0f, -0.299f, 0.0f, -0.505f, 0.0f, 1.0f, -0.3f, 0.0f, -0.509f, 0.0f, 1.0f, + -0.301f, 0.0f, -0.513f, 0.0f, 1.0f, -0.302f, 0.0f, -0.517f, 0.0f, 1.0f, -0.303f, 0.0f, -0.52f, 0.0f, 1.0f, -0.304f, 0.0f, -0.524f, 0.0f, 1.0f, + -0.305f, 0.0f, -0.528f, 0.0f, 1.0f, -0.306f, 0.0f, -0.532f, 0.0f, 1.0f, -0.307f, 0.0f, -0.535f, 0.0f, 1.0f, -0.308f, 0.0f, -0.539f, 0.0f, 1.0f, + -0.309f, 0.0f, -0.543f, 0.0f, 1.0f, -0.31f, 0.0f, -0.547f, 0.0f, 1.0f, -0.311f, 0.0f, -0.55f, 0.0f, 1.0f, -0.312f, 0.0f, -0.554f, 0.0f, 1.0f, + -0.313f, 0.0f, -0.558f, 0.0f, 1.0f, -0.314f, 0.0f, -0.562f, 0.0f, 1.0f, -0.315f, 0.0f, -0.565f, 0.0f, 1.0f, -0.316f, 0.0f, -0.569f, 0.0f, 1.0f, + -0.317f, 0.0f, -0.573f, 0.0f, 1.0f, -0.318f, 0.0f, -0.576f, 0.0f, 1.0f, -0.319f, 0.0f, -0.58f, 0.0f, 1.0f, -0.32f, 0.0f, -0.583f, 0.0f, 1.0f, + -0.321f, 0.0f, -0.587f, 0.0f, 1.0f, -0.322f, 0.0f, -0.591f, 0.0f, 1.0f, -0.323f, 0.0f, -0.594f, 0.0f, 1.0f, -0.323f, 0.0f, -0.598f, 0.0f, 1.0f, + -0.324f, 0.0f, -0.601f, 0.0f, 1.0f, -0.325f, 0.0f, -0.605f, 0.0f, 1.0f, -0.326f, 0.0f, -0.608f, 0.0f, 1.0f, -0.326f, 0.0f, -0.612f, 0.0f, 1.0f, + -0.327f, 0.0f, -0.615f, 0.0f, 1.0f, -0.328f, 0.0f, -0.619f, 0.0f, 1.0f, -0.328f, 0.0f, -0.622f, 0.0f, 1.0f, -0.329f, 0.0f, -0.625f, 0.0f, 1.0f, + -0.33f, 0.0f, -0.629f, 0.0f, 1.0f, -0.33f, 0.0f, -0.632f, 0.0f, 1.0f, -0.331f, 0.0f, -0.635f, 0.001f, 1.0f, -0.331f, 0.0f, -0.639f, 0.001f, 1.0f, + -0.332f, 0.0f, -0.642f, 0.002f, 1.0f, -0.332f, 0.0f, -0.645f, 0.002f, 1.0f, -0.333f, 0.0f, -0.649f, 0.003f, 1.0f, -0.333f, 0.0f, -0.652f, 0.005f, 1.0f, + -0.334f, 0.0f, -0.655f, 0.006f, 1.0f, -0.334f, 0.0f, -0.658f, 0.009f, 1.0f, -0.335f, 0.0f, -0.662f, 0.011f, 1.0f, -0.335f, 0.0f, -0.665f, 0.015f, 1.0f, + -0.335f, 0.0f, -0.668f, 0.019f, 1.0f, -0.336f, 0.0f, -0.672f, 0.024f, 1.0f, -0.336f, 0.0f, -0.675f, 0.031f, 1.0f, -0.337f, 0.0f, -0.678f, 0.038f, 1.0f, + -0.337f, 0.0f, -0.682f, 0.046f, 1.0f, -0.337f, 0.0f, -0.685f, 0.056f, 1.0f, -0.338f, 0.0f, -0.689f, 0.067f, 1.0f, -0.338f, 0.0f, -0.692f, 0.079f, 1.0f, + -0.338f, 0.0f, -0.696f, 0.093f, 1.0f, -0.339f, 0.0f, -0.699f, 0.107f, 1.0f, -0.339f, 0.0f, -0.703f, 0.123f, 1.0f, -0.34f, 0.0f, -0.706f, 0.139f, 1.0f, + -0.34f, 0.0f, -0.71f, 0.157f, 1.0f, -0.34f, 0.0f, -0.714f, 0.174f, 1.0f, -0.34f, 0.0f, -0.717f, 0.193f, 1.0f, -0.341f, 0.0f, -0.721f, 0.211f, 1.0f, + -0.341f, 0.0f, -0.725f, 0.23f, 1.0f, -0.341f, 0.0f, -0.729f, 0.248f, 1.0f, -0.342f, 0.0f, -0.732f, 0.266f, 1.0f, -0.342f, 0.0f, -0.736f, 0.284f, 1.0f, + -0.342f, 0.0f, -0.74f, 0.302f, 1.0f, -0.342f, 0.0f, -0.744f, 0.318f, 1.0f, -0.342f, 0.0f, -0.748f, 0.334f, 1.0f, -0.342f, 0.0f, -0.752f, 0.349f, 1.0f, + -0.343f, 0.0f, -0.756f, 0.364f, 1.0f, -0.343f, 0.0f, -0.76f, 0.377f, 1.0f, -0.343f, 0.0f, -0.763f, 0.389f, 1.0f, -0.343f, 0.0f, -0.767f, 0.401f, 1.0f, + -0.343f, 0.0f, -0.771f, 0.411f, 1.0f, -0.343f, 0.0f, -0.775f, 0.421f, 1.0f, -0.342f, 0.0f, -0.779f, 0.429f, 1.0f, -0.342f, 0.0f, -0.783f, 0.437f, 1.0f, + -0.342f, 0.0f, -0.786f, 0.444f, 1.0f, -0.342f, 0.0f, -0.79f, 0.451f, 1.0f, -0.342f, 0.0f, -0.794f, 0.456f, 1.0f, -0.341f, 0.0f, -0.797f, 0.461f, 1.0f, + -0.341f, 0.0f, -0.801f, 0.466f, 1.0f, -0.34f, 0.0f, -0.805f, 0.469f, 1.0f, -0.34f, 0.0f, -0.808f, 0.473f, 1.0f, -0.339f, 0.0f, -0.812f, 0.476f, 1.0f, + -0.339f, 0.0f, -0.815f, 0.478f, 1.0f, -0.338f, 0.0f, -0.818f, 0.48f, 1.0f, -0.338f, 0.0f, -0.822f, 0.482f, 1.0f, -0.337f, 0.0f, -0.825f, 0.483f, 1.0f, + -0.336f, 0.0f, -0.828f, 0.484f, 1.0f, -0.335f, 0.0f, -0.831f, 0.485f, 1.0f, -0.334f, 0.0f, -0.834f, 0.486f, 1.0f, -0.333f, 0.0f, -0.837f, 0.487f, 1.0f, + -0.332f, 0.0f, -0.84f, 0.487f, 1.0f, -0.331f, 0.0f, -0.843f, 0.487f, 1.0f, -0.33f, 0.0f, -0.846f, 0.488f, 1.0f, -0.329f, 0.0f, -0.849f, 0.488f, 1.0f, + -0.328f, 0.0f, -0.852f, 0.488f, 1.0f, -0.326f, 0.0f, -0.855f, 0.488f, 1.0f, -0.325f, 0.0f, -0.857f, 0.488f, 1.0f, -0.324f, 0.0f, -0.86f, 0.488f, 1.0f, + -0.322f, 0.0f, -0.863f, 0.488f, 1.0f, -0.321f, 0.0f, -0.865f, 0.488f, 1.0f, -0.319f, 0.0f, -0.868f, 0.488f, 1.0f, -0.318f, 0.0f, -0.871f, 0.488f, 1.0f, + -0.316f, 0.0f, -0.873f, 0.489f, 1.0f, -0.314f, 0.0f, -0.876f, 0.489f, 1.0f, -0.312f, 0.0f, -0.878f, 0.489f, 1.0f, -0.311f, 0.0f, -0.881f, 0.489f, 1.0f, + -0.309f, 0.0f, -0.883f, 0.489f, 1.0f, -0.307f, 0.0f, -0.885f, 0.489f, 1.0f, -0.305f, 0.0f, -0.888f, 0.49f, 1.0f, -0.303f, 0.0f, -0.89f, 0.491f, 1.0f, + -0.301f, 0.0f, -0.892f, 0.491f, 1.0f, -0.298f, 0.0f, -0.894f, 0.492f, 1.0f, -0.296f, 0.0f, -0.897f, 0.494f, 1.0f, -0.294f, 0.0f, -0.899f, 0.495f, 1.0f, + -0.292f, 0.0f, -0.901f, 0.497f, 1.0f, -0.289f, 0.0f, -0.903f, 0.5f, 1.0f, -0.287f, 0.0f, -0.905f, 0.502f, 1.0f, -0.284f, 0.0f, -0.907f, 0.505f, 1.0f, + -0.282f, 0.0f, -0.909f, 0.509f, 1.0f, -0.279f, 0.0f, -0.912f, 0.512f, 1.0f, -0.277f, 0.0f, -0.914f, 0.517f, 1.0f, -0.274f, 0.0f, -0.916f, 0.521f, 1.0f, + -0.271f, 0.0f, -0.918f, 0.526f, 1.0f, -0.269f, 0.0f, -0.919f, 0.531f, 1.0f, -0.266f, 0.0f, -0.921f, 0.537f, 1.0f, -0.263f, 0.0f, -0.923f, 0.543f, 1.0f, + -0.26f, 0.0f, -0.925f, 0.548f, 1.0f, -0.257f, 0.0f, -0.927f, 0.554f, 1.0f, -0.255f, 0.0f, -0.929f, 0.56f, 1.0f, -0.252f, 0.0f, -0.931f, 0.566f, 1.0f, + -0.249f, 0.0f, -0.932f, 0.571f, 1.0f, -0.246f, 0.0f, -0.934f, 0.577f, 1.0f, -0.243f, 0.0f, -0.936f, 0.582f, 1.0f, -0.24f, 0.0f, -0.938f, 0.587f, 1.0f, + -0.237f, 0.0f, -0.939f, 0.592f, 1.0f, -0.234f, 0.0f, -0.941f, 0.597f, 1.0f, -0.231f, 0.0f, -0.943f, 0.601f, 1.0f, -0.228f, 0.0f, -0.944f, 0.605f, 1.0f, + -0.225f, 0.0f, -0.946f, 0.609f, 1.0f, -0.222f, 0.0f, -0.948f, 0.613f, 1.0f, -0.219f, 0.0f, -0.949f, 0.617f, 1.0f, -0.216f, 0.0f, -0.951f, 0.62f, 1.0f, + -0.213f, 0.0f, -0.953f, 0.624f, 1.0f, -0.21f, 0.0f, -0.954f, 0.627f, 1.0f, -0.207f, 0.0f, -0.956f, 0.63f, 1.0f, -0.204f, 0.0f, -0.958f, 0.633f, 1.0f, + -0.201f, 0.0f, -0.959f, 0.636f, 1.0f, -0.198f, 0.0f, -0.961f, 0.639f, 1.0f, -0.195f, 0.0f, -0.962f, 0.641f, 1.0f, -0.191f, 0.0f, -0.964f, 0.643f, 1.0f, + -0.188f, 0.0f, -0.965f, 0.646f, 1.0f, -0.185f, 0.0f, -0.967f, 0.648f, 1.0f, -0.181f, 0.0f, -0.968f, 0.649f, 1.0f, -0.178f, 0.0f, -0.969f, 0.651f, 1.0f, + -0.175f, 0.0f, -0.971f, 0.653f, 1.0f, -0.171f, 0.0f, -0.972f, 0.654f, 1.0f, -0.168f, 0.0f, -0.973f, 0.655f, 1.0f, -0.165f, 0.0f, -0.974f, 0.657f, 1.0f, + -0.161f, 0.0f, -0.976f, 0.658f, 1.0f, -0.158f, 0.0f, -0.977f, 0.659f, 1.0f, -0.154f, 0.0f, -0.978f, 0.66f, 1.0f, -0.151f, 0.0f, -0.979f, 0.661f, 1.0f, + -0.148f, 0.0f, -0.98f, 0.662f, 1.0f, -0.144f, 0.0f, -0.981f, 0.664f, 1.0f, -0.141f, 0.0f, -0.982f, 0.665f, 1.0f, -0.137f, 0.0f, -0.983f, 0.667f, 1.0f, + -0.134f, 0.0f, -0.984f, 0.669f, 1.0f, -0.13f, 0.0f, -0.985f, 0.671f, 1.0f, -0.127f, 0.0f, -0.986f, 0.673f, 1.0f, -0.124f, 0.0f, -0.987f, 0.675f, 1.0f, + -0.12f, 0.0f, -0.988f, 0.678f, 1.0f, -0.117f, 0.0f, -0.989f, 0.68f, 1.0f, -0.113f, 0.0f, -0.99f, 0.683f, 1.0f, -0.11f, 0.0f, -0.991f, 0.685f, 1.0f, + -0.107f, 0.0f, -0.992f, 0.688f, 1.0f, -0.103f, 0.0f, -0.992f, 0.691f, 1.0f, -0.1f, 0.0f, -0.993f, 0.693f, 1.0f, -0.097f, 0.0f, -0.994f, 0.696f, 1.0f, + -0.093f, 0.0f, -0.995f, 0.698f, 1.0f, -0.09f, 0.0f, -0.996f, 0.701f, 1.0f, -0.087f, 0.0f, -0.997f, 0.703f, 1.0f, -0.084f, 0.0f, -0.997f, 0.705f, 1.0f, + -0.08f, 0.0f, -0.998f, 0.707f, 1.0f, -0.077f, 0.0f, -0.999f, 0.708f, 1.0f, -0.074f, 0.0f, -1.0f, 0.71f, 1.0f, -0.07f, 0.0f, -1.0f, 0.712f, 1.0f, + -0.067f, 0.0f, -1.001f, 0.713f, 1.0f, -0.063f, 0.0f, -1.002f, 0.715f, 1.0f, -0.06f, 0.0f, -1.002f, 0.717f, 1.0f, -0.056f, 0.0f, -1.003f, 0.718f, 1.0f, + -0.053f, 0.0f, -1.003f, 0.72f, 1.0f, -0.049f, 0.0f, -1.004f, 0.723f, 1.0f, -0.045f, 0.0f, -1.004f, 0.725f, 1.0f, -0.041f, 0.0f, -1.005f, 0.728f, 1.0f, + -0.038f, 0.0f, -1.005f, 0.73f, 1.0f, -0.034f, 0.0f, -1.006f, 0.733f, 1.0f, -0.03f, 0.0f, -1.006f, 0.736f, 1.0f, -0.026f, 0.0f, -1.007f, 0.738f, 1.0f, + -0.022f, 0.0f, -1.007f, 0.741f, 1.0f, -0.018f, 0.0f, -1.007f, 0.743f, 1.0f, -0.014f, 0.0f, -1.008f, 0.746f, 1.0f, -0.01f, 0.0f, -1.008f, 0.748f, 1.0f, + -0.006f, 0.0f, -1.009f, 0.75f, 1.0f, -0.001f, 0.0f, -1.009f, 0.752f, 1.0f, 0.003f, 0.0f, -1.009f, 0.754f, 1.0f, 0.007f, 0.0f, -1.01f, 0.755f, 1.0f, + 0.011f, 0.0f, -1.01f, 0.757f, 1.0f, 0.015f, 0.0f, -1.01f, 0.758f, 1.0f, 0.02f, 0.0f, -1.011f, 0.759f, 1.0f, 0.024f, 0.0f, -1.011f, 0.76f, 1.0f, + 0.028f, 0.0f, -1.011f, 0.761f, 1.0f, 0.033f, 0.0f, -1.011f, 0.761f, 1.0f, 0.037f, 0.0f, -1.012f, 0.762f, 1.0f, 0.041f, 0.0f, -1.012f, 0.762f, 1.0f, + 0.045f, 0.0f, -1.012f, 0.763f, 1.0f, 0.05f, 0.0f, -1.012f, 0.763f, 1.0f, 0.054f, 0.0f, -1.012f, 0.764f, 1.0f, 0.058f, 0.0f, -1.013f, 0.764f, 1.0f, + 0.062f, 0.0f, -1.013f, 0.764f, 1.0f, 0.066f, 0.0f, -1.013f, 0.764f, 1.0f, 0.071f, 0.0f, -1.013f, 0.764f, 1.0f, 0.075f, 0.0f, -1.013f, 0.765f, 1.0f, + 0.079f, 0.0f, -1.013f, 0.765f, 1.0f, 0.083f, 0.0f, -1.013f, 0.765f, 1.0f, 0.087f, 0.0f, -1.013f, 0.765f, 1.0f, 0.091f, 0.0f, -1.013f, 0.765f, 1.0f, + 0.095f, 0.0f, -1.013f, 0.765f, 1.0f, 0.099f, 0.0f, -1.013f, 0.766f, 1.0f, 0.103f, 0.0f, -1.013f, 0.766f, 1.0f, 0.108f, 0.0f, -1.012f, 0.766f, 1.0f, + 0.112f, 0.0f, -1.012f, 0.766f, 1.0f, 0.116f, 0.0f, -1.012f, 0.766f, 1.0f, 0.119f, 0.0f, -1.012f, 0.767f, 1.0f, 0.123f, 0.0f, -1.011f, 0.767f, 1.0f, + 0.127f, 0.0f, -1.011f, 0.767f, 1.0f, 0.131f, 0.0f, -1.01f, 0.767f, 1.0f, 0.135f, 0.0f, -1.01f, 0.767f, 1.0f, 0.139f, 0.0f, -1.009f, 0.768f, 1.0f, + 0.143f, 0.0f, -1.009f, 0.768f, 1.0f, 0.147f, 0.0f, -1.008f, 0.768f, 1.0f, 0.151f, 0.0f, -1.007f, 0.769f, 1.0f, 0.154f, 0.0f, -1.007f, 0.769f, 1.0f, + 0.158f, 0.0f, -1.006f, 0.769f, 1.0f, 0.162f, 0.0f, -1.005f, 0.769f, 1.0f, 0.166f, 0.0f, -1.004f, 0.77f, 1.0f, 0.17f, 0.0f, -1.003f, 0.77f, 1.0f, + 0.173f, 0.0f, -1.003f, 0.77f, 1.0f, 0.177f, 0.0f, -1.002f, 0.771f, 1.0f, 0.181f, 0.0f, -1.001f, 0.771f, 1.0f, 0.184f, 0.0f, -1.0f, 0.772f, 1.0f, + 0.188f, 0.0f, -0.999f, 0.772f, 1.0f, 0.192f, 0.0f, -0.998f, 0.773f, 1.0f, 0.195f, 0.0f, -0.997f, 0.773f, 1.0f, 0.199f, 0.0f, -0.996f, 0.774f, 1.0f, + 0.202f, 0.0f, -0.995f, 0.774f, 1.0f, 0.206f, 0.0f, -0.994f, 0.775f, 1.0f, 0.209f, 0.0f, -0.993f, 0.776f, 1.0f, 0.213f, 0.0f, -0.992f, 0.776f, 1.0f, + 0.216f, 0.0f, -0.991f, 0.777f, 1.0f, 0.22f, 0.0f, -0.99f, 0.777f, 1.0f, 0.223f, 0.0f, -0.988f, 0.778f, 1.0f, 0.227f, 0.0f, -0.987f, 0.778f, 1.0f, + 0.23f, 0.0f, -0.986f, 0.778f, 1.0f, 0.233f, 0.0f, -0.985f, 0.779f, 1.0f, 0.237f, 0.0f, -0.983f, 0.779f, 1.0f, 0.24f, 0.0f, -0.982f, 0.779f, 1.0f, + 0.243f, 0.0f, -0.981f, 0.779f, 1.0f, 0.246f, 0.0f, -0.979f, 0.778f, 1.0f, 0.249f, 0.0f, -0.978f, 0.778f, 1.0f, 0.252f, 0.0f, -0.976f, 0.777f, 1.0f, + 0.255f, 0.0f, -0.975f, 0.777f, 1.0f, 0.258f, 0.0f, -0.973f, 0.776f, 1.0f, 0.261f, 0.0f, -0.972f, 0.775f, 1.0f, 0.264f, 0.0f, -0.97f, 0.773f, 1.0f, + 0.267f, 0.0f, -0.968f, 0.772f, 1.0f, 0.269f, 0.0f, -0.967f, 0.77f, 1.0f, 0.272f, 0.0f, -0.965f, 0.769f, 1.0f, 0.275f, 0.0f, -0.963f, 0.767f, 1.0f, + 0.277f, 0.0f, -0.961f, 0.765f, 1.0f, 0.279f, 0.0f, -0.959f, 0.763f, 1.0f, 0.282f, 0.0f, -0.957f, 0.761f, 1.0f, 0.284f, 0.0f, -0.955f, 0.759f, 1.0f, + 0.286f, 0.0f, -0.953f, 0.756f, 1.0f, 0.288f, 0.0f, -0.951f, 0.754f, 1.0f, 0.29f, 0.0f, -0.948f, 0.752f, 1.0f, 0.292f, 0.0f, -0.946f, 0.749f, 1.0f, + 0.294f, 0.0f, -0.944f, 0.746f, 1.0f, 0.296f, 0.0f, -0.941f, 0.744f, 1.0f, 0.298f, 0.0f, -0.939f, 0.741f, 1.0f, 0.3f, 0.0f, -0.937f, 0.738f, 1.0f, + 0.302f, 0.0f, -0.934f, 0.736f, 1.0f, 0.303f, 0.0f, -0.932f, 0.733f, 1.0f, 0.305f, 0.0f, -0.929f, 0.73f, 1.0f, 0.306f, 0.0f, -0.926f, 0.727f, 1.0f, + 0.308f, 0.0f, -0.924f, 0.724f, 1.0f, 0.309f, 0.0f, -0.921f, 0.721f, 1.0f, 0.311f, 0.0f, -0.918f, 0.719f, 1.0f, 0.312f, 0.0f, -0.916f, 0.716f, 1.0f, + 0.313f, 0.0f, -0.913f, 0.713f, 1.0f, 0.315f, 0.0f, -0.91f, 0.71f, 1.0f, 0.316f, 0.0f, -0.907f, 0.707f, 1.0f, 0.317f, 0.0f, -0.904f, 0.704f, 1.0f, + 0.318f, 0.0f, -0.901f, 0.7f, 1.0f, 0.319f, 0.0f, -0.898f, 0.697f, 1.0f, 0.32f, 0.0f, -0.895f, 0.693f, 1.0f, 0.321f, 0.0f, -0.892f, 0.69f, 1.0f, + 0.322f, 0.0f, -0.889f, 0.686f, 1.0f, 0.323f, 0.0f, -0.886f, 0.681f, 1.0f, 0.324f, 0.0f, -0.883f, 0.677f, 1.0f, 0.325f, 0.0f, -0.88f, 0.672f, 1.0f, + 0.326f, 0.0f, -0.876f, 0.667f, 1.0f, 0.326f, 0.0f, -0.873f, 0.661f, 1.0f, 0.327f, 0.0f, -0.87f, 0.655f, 1.0f, 0.328f, 0.0f, -0.867f, 0.649f, 1.0f, + 0.329f, 0.0f, -0.864f, 0.643f, 1.0f, 0.329f, 0.0f, -0.861f, 0.637f, 1.0f, 0.33f, 0.0f, -0.857f, 0.63f, 1.0f, 0.331f, 0.0f, -0.854f, 0.624f, 1.0f, + 0.331f, 0.0f, -0.851f, 0.618f, 1.0f, 0.332f, 0.0f, -0.848f, 0.613f, 1.0f, 0.333f, 0.0f, -0.845f, 0.607f, 1.0f, 0.333f, 0.0f, -0.841f, 0.603f, 1.0f, + 0.334f, 0.0f, -0.838f, 0.598f, 1.0f, 0.334f, 0.0f, -0.835f, 0.594f, 1.0f, 0.335f, 0.0f, -0.832f, 0.591f, 1.0f, 0.335f, 0.0f, -0.828f, 0.588f, 1.0f, + 0.335f, 0.0f, -0.825f, 0.586f, 1.0f, 0.336f, 0.0f, -0.821f, 0.584f, 1.0f, 0.336f, 0.0f, -0.818f, 0.582f, 1.0f, 0.336f, 0.0f, -0.814f, 0.581f, 1.0f, + 0.337f, 0.0f, -0.811f, 0.58f, 1.0f, 0.337f, 0.0f, -0.807f, 0.58f, 1.0f, 0.337f, 0.0f, -0.803f, 0.579f, 1.0f, 0.337f, 0.0f, -0.799f, 0.579f, 1.0f, + 0.337f, 0.0f, -0.795f, 0.578f, 1.0f, 0.337f, 0.0f, -0.79f, 0.578f, 1.0f, 0.337f, 0.0f, -0.786f, 0.578f, 1.0f, 0.338f, 0.0f, -0.782f, 0.577f, 1.0f, + 0.338f, 0.0f, -0.777f, 0.576f, 1.0f, 0.337f, 0.0f, -0.772f, 0.574f, 1.0f, 0.337f, 0.0f, -0.767f, 0.572f, 1.0f, 0.337f, 0.0f, -0.762f, 0.569f, 1.0f, + 0.337f, 0.0f, -0.756f, 0.565f, 1.0f, 0.337f, 0.0f, -0.751f, 0.559f, 1.0f, 0.337f, 0.0f, -0.745f, 0.553f, 1.0f, 0.336f, 0.0f, -0.739f, 0.544f, 1.0f, + 0.336f, 0.0f, -0.732f, 0.534f, 1.0f, 0.335f, 0.0f, -0.725f, 0.521f, 1.0f, 0.334f, 0.0f, -0.718f, 0.505f, 1.0f, 0.333f, 0.0f, -0.711f, 0.487f, 1.0f, + 0.332f, 0.0f, -0.703f, 0.466f, 1.0f, 0.331f, 0.0f, -0.694f, 0.441f, 1.0f, 0.33f, 0.0f, -0.686f, 0.413f, 1.0f, 0.328f, 0.0f, -0.677f, 0.383f, 1.0f, + 0.326f, 0.0f, -0.667f, 0.35f, 1.0f, 0.325f, 0.0f, -0.657f, 0.316f, 1.0f, 0.323f, 0.0f, -0.647f, 0.281f, 1.0f, 0.32f, 0.0f, -0.636f, 0.246f, 1.0f, + 0.318f, 0.0f, -0.625f, 0.212f, 1.0f, 0.316f, 0.0f, -0.614f, 0.18f, 1.0f, 0.313f, 0.0f, -0.603f, 0.149f, 1.0f, 0.311f, 0.0f, -0.592f, 0.12f, 1.0f, + 0.308f, 0.0f, -0.581f, 0.093f, 1.0f, 0.306f, 0.0f, -0.57f, 0.069f, 1.0f, 0.303f, 0.0f, -0.559f, 0.046f, 1.0f, 0.301f, 0.0f, -0.55f, 0.027f, 1.0f, + 0.298f, 0.0f, -0.537f, 0.0f, 1.0f, +}; + +static const float data26[41 * GP_PRIM_DATABUF_SIZE] = { + -0.104f, 0.0f, -0.795f, 0.258f, 1.0f, -0.1f, 0.0f, -0.799f, 0.28f, 1.0f, -0.097f, 0.0f, -0.801f, 0.294f, 1.0f, -0.094f, 0.0f, -0.805f, 0.312f, 1.0f, + -0.09f, 0.0f, -0.808f, 0.328f, 1.0f, -0.086f, 0.0f, -0.811f, 0.345f, 1.0f, -0.082f, 0.0f, -0.815f, 0.361f, 1.0f, -0.078f, 0.0f, -0.818f, 0.377f, 1.0f, + -0.073f, 0.0f, -0.821f, 0.392f, 1.0f, -0.068f, 0.0f, -0.824f, 0.407f, 1.0f, -0.063f, 0.0f, -0.827f, 0.421f, 1.0f, -0.057f, 0.0f, -0.83f, 0.435f, 1.0f, + -0.051f, 0.0f, -0.833f, 0.448f, 1.0f, -0.045f, 0.0f, -0.835f, 0.46f, 1.0f, -0.039f, 0.0f, -0.837f, 0.471f, 1.0f, -0.033f, 0.0f, -0.839f, 0.481f, 1.0f, + -0.026f, 0.0f, -0.841f, 0.491f, 1.0f, -0.019f, 0.0f, -0.842f, 0.5f, 1.0f, -0.012f, 0.0f, -0.843f, 0.508f, 1.0f, -0.005f, 0.0f, -0.843f, 0.515f, 1.0f, + 0.002f, 0.0f, -0.843f, 0.522f, 1.0f, 0.009f, 0.0f, -0.843f, 0.527f, 1.0f, 0.016f, 0.0f, -0.842f, 0.532f, 1.0f, 0.023f, 0.0f, -0.841f, 0.535f, 1.0f, + 0.03f, 0.0f, -0.839f, 0.538f, 1.0f, 0.037f, 0.0f, -0.837f, 0.538f, 1.0f, 0.044f, 0.0f, -0.835f, 0.537f, 1.0f, 0.05f, 0.0f, -0.833f, 0.532f, 1.0f, + 0.056f, 0.0f, -0.83f, 0.524f, 1.0f, 0.062f, 0.0f, -0.827f, 0.513f, 1.0f, 0.068f, 0.0f, -0.823f, 0.496f, 1.0f, 0.074f, 0.0f, -0.82f, 0.474f, 1.0f, + 0.079f, 0.0f, -0.817f, 0.446f, 1.0f, 0.084f, 0.0f, -0.813f, 0.411f, 1.0f, 0.089f, 0.0f, -0.809f, 0.37f, 1.0f, 0.093f, 0.0f, -0.806f, 0.323f, 1.0f, + 0.098f, 0.0f, -0.802f, 0.269f, 1.0f, 0.102f, 0.0f, -0.798f, 0.211f, 1.0f, 0.106f, 0.0f, -0.795f, 0.146f, 1.0f, 0.109f, 0.0f, -0.792f, 0.089f, 1.0f, + 0.114f, 0.0f, -0.787f, 0.0f, 1.0f, +}; + +static const float data27[77 * GP_PRIM_DATABUF_SIZE] = { + -0.105f, 0.0f, -0.259f, 0.214f, 1.0f, -0.103f, 0.0f, -0.253f, 0.263f, 1.0f, -0.101f, 0.0f, -0.249f, 0.291f, 1.0f, -0.099f, 0.0f, -0.244f, 0.324f, 1.0f, + -0.098f, 0.0f, -0.24f, 0.351f, 1.0f, -0.096f, 0.0f, -0.235f, 0.376f, 1.0f, -0.094f, 0.0f, -0.231f, 0.397f, 1.0f, -0.092f, 0.0f, -0.227f, 0.416f, 1.0f, + -0.09f, 0.0f, -0.222f, 0.432f, 1.0f, -0.088f, 0.0f, -0.218f, 0.446f, 1.0f, -0.086f, 0.0f, -0.215f, 0.458f, 1.0f, -0.084f, 0.0f, -0.211f, 0.469f, 1.0f, + -0.082f, 0.0f, -0.208f, 0.478f, 1.0f, -0.079f, 0.0f, -0.205f, 0.486f, 1.0f, -0.077f, 0.0f, -0.203f, 0.494f, 1.0f, -0.075f, 0.0f, -0.2f, 0.501f, 1.0f, + -0.073f, 0.0f, -0.198f, 0.508f, 1.0f, -0.071f, 0.0f, -0.197f, 0.515f, 1.0f, -0.068f, 0.0f, -0.195f, 0.521f, 1.0f, -0.066f, 0.0f, -0.194f, 0.528f, 1.0f, + -0.064f, 0.0f, -0.194f, 0.534f, 1.0f, -0.061f, 0.0f, -0.194f, 0.54f, 1.0f, -0.059f, 0.0f, -0.194f, 0.546f, 1.0f, -0.056f, 0.0f, -0.194f, 0.551f, 1.0f, + -0.054f, 0.0f, -0.195f, 0.555f, 1.0f, -0.051f, 0.0f, -0.196f, 0.559f, 1.0f, -0.049f, 0.0f, -0.198f, 0.562f, 1.0f, -0.046f, 0.0f, -0.2f, 0.565f, 1.0f, + -0.044f, 0.0f, -0.201f, 0.567f, 1.0f, -0.041f, 0.0f, -0.204f, 0.568f, 1.0f, -0.039f, 0.0f, -0.206f, 0.569f, 1.0f, -0.036f, 0.0f, -0.208f, 0.57f, 1.0f, + -0.034f, 0.0f, -0.21f, 0.571f, 1.0f, -0.032f, 0.0f, -0.213f, 0.571f, 1.0f, -0.029f, 0.0f, -0.215f, 0.571f, 1.0f, -0.027f, 0.0f, -0.217f, 0.572f, 1.0f, + -0.024f, 0.0f, -0.219f, 0.572f, 1.0f, -0.022f, 0.0f, -0.221f, 0.572f, 1.0f, -0.019f, 0.0f, -0.222f, 0.572f, 1.0f, -0.016f, 0.0f, -0.224f, 0.572f, 1.0f, + -0.013f, 0.0f, -0.225f, 0.572f, 1.0f, -0.01f, 0.0f, -0.226f, 0.573f, 1.0f, -0.007f, 0.0f, -0.227f, 0.573f, 1.0f, -0.004f, 0.0f, -0.227f, 0.573f, 1.0f, + -0.001f, 0.0f, -0.227f, 0.574f, 1.0f, 0.002f, 0.0f, -0.227f, 0.575f, 1.0f, 0.005f, 0.0f, -0.227f, 0.576f, 1.0f, 0.008f, 0.0f, -0.226f, 0.577f, 1.0f, + 0.011f, 0.0f, -0.225f, 0.578f, 1.0f, 0.015f, 0.0f, -0.224f, 0.579f, 1.0f, 0.018f, 0.0f, -0.222f, 0.58f, 1.0f, 0.021f, 0.0f, -0.221f, 0.581f, 1.0f, + 0.024f, 0.0f, -0.219f, 0.582f, 1.0f, 0.027f, 0.0f, -0.217f, 0.582f, 1.0f, 0.03f, 0.0f, -0.215f, 0.583f, 1.0f, 0.033f, 0.0f, -0.213f, 0.583f, 1.0f, + 0.036f, 0.0f, -0.212f, 0.583f, 1.0f, 0.039f, 0.0f, -0.21f, 0.583f, 1.0f, 0.042f, 0.0f, -0.208f, 0.583f, 1.0f, 0.045f, 0.0f, -0.207f, 0.583f, 1.0f, + 0.048f, 0.0f, -0.205f, 0.583f, 1.0f, 0.051f, 0.0f, -0.204f, 0.583f, 1.0f, 0.054f, 0.0f, -0.203f, 0.583f, 1.0f, 0.058f, 0.0f, -0.203f, 0.583f, 1.0f, + 0.061f, 0.0f, -0.202f, 0.583f, 1.0f, 0.064f, 0.0f, -0.202f, 0.574f, 1.0f, 0.067f, 0.0f, -0.202f, 0.565f, 1.0f, 0.07f, 0.0f, -0.203f, 0.556f, 1.0f, + 0.073f, 0.0f, -0.203f, 0.547f, 1.0f, 0.075f, 0.0f, -0.204f, 0.515f, 1.0f, 0.078f, 0.0f, -0.204f, 0.483f, 1.0f, 0.08f, 0.0f, -0.205f, 0.451f, 1.0f, + 0.083f, 0.0f, -0.206f, 0.419f, 1.0f, 0.085f, 0.0f, -0.207f, 0.314f, 1.0f, 0.087f, 0.0f, -0.208f, 0.21f, 1.0f, 0.089f, 0.0f, -0.209f, 0.105f, 1.0f, + 0.091f, 0.0f, -0.21f, 0.0f, 1.0f, +}; + +static const float data28[257 * GP_PRIM_DATABUF_SIZE] = { + -0.637f, 0.0f, -0.172f, 0.0f, 1.0f, -0.641f, 0.0f, -0.172f, 0.0f, 1.0f, -0.643f, 0.0f, -0.172f, 0.0f, 1.0f, -0.646f, 0.0f, -0.172f, 0.0f, 1.0f, + -0.65f, 0.0f, -0.172f, 0.0f, 1.0f, -0.653f, 0.0f, -0.172f, 0.0f, 1.0f, -0.657f, 0.0f, -0.172f, 0.0f, 1.0f, -0.66f, 0.0f, -0.172f, 0.0f, 1.0f, + -0.664f, 0.0f, -0.171f, 0.0f, 1.0f, -0.668f, 0.0f, -0.171f, 0.0f, 1.0f, -0.672f, 0.0f, -0.171f, 0.0f, 1.0f, -0.677f, 0.0f, -0.171f, 0.0f, 1.0f, + -0.681f, 0.0f, -0.171f, 0.0f, 1.0f, -0.685f, 0.0f, -0.171f, 0.0f, 1.0f, -0.69f, 0.0f, -0.17f, 0.0f, 1.0f, -0.694f, 0.0f, -0.17f, 0.0f, 1.0f, + -0.699f, 0.0f, -0.17f, 0.0f, 1.0f, -0.704f, 0.0f, -0.169f, 0.0f, 1.0f, -0.708f, 0.0f, -0.169f, 0.0f, 1.0f, -0.713f, 0.0f, -0.168f, 0.0f, 1.0f, + -0.717f, 0.0f, -0.168f, 0.0f, 1.0f, -0.722f, 0.0f, -0.167f, 0.0f, 1.0f, -0.727f, 0.0f, -0.167f, 0.0f, 1.0f, -0.731f, 0.0f, -0.166f, 0.0f, 1.0f, + -0.735f, 0.0f, -0.166f, 0.0f, 1.0f, -0.74f, 0.0f, -0.165f, 0.0f, 1.0f, -0.744f, 0.0f, -0.164f, 0.0f, 1.0f, -0.749f, 0.0f, -0.163f, 0.0f, 1.0f, + -0.753f, 0.0f, -0.163f, 0.0f, 1.0f, -0.757f, 0.0f, -0.162f, 0.0f, 1.0f, -0.761f, 0.0f, -0.161f, 0.0f, 1.0f, -0.765f, 0.0f, -0.16f, 0.0f, 1.0f, + -0.769f, 0.0f, -0.159f, 0.0f, 1.0f, -0.773f, 0.0f, -0.158f, 0.0f, 1.0f, -0.777f, 0.0f, -0.157f, 0.0f, 1.0f, -0.781f, 0.0f, -0.156f, 0.001f, 1.0f, + -0.785f, 0.0f, -0.155f, 0.001f, 1.0f, -0.789f, 0.0f, -0.154f, 0.002f, 1.0f, -0.793f, 0.0f, -0.153f, 0.003f, 1.0f, -0.797f, 0.0f, -0.152f, 0.004f, 1.0f, + -0.801f, 0.0f, -0.15f, 0.005f, 1.0f, -0.805f, 0.0f, -0.149f, 0.006f, 1.0f, -0.81f, 0.0f, -0.147f, 0.008f, 1.0f, -0.814f, 0.0f, -0.146f, 0.009f, 1.0f, + -0.818f, 0.0f, -0.144f, 0.011f, 1.0f, -0.823f, 0.0f, -0.143f, 0.014f, 1.0f, -0.827f, 0.0f, -0.141f, 0.016f, 1.0f, -0.831f, 0.0f, -0.139f, 0.019f, 1.0f, + -0.836f, 0.0f, -0.138f, 0.022f, 1.0f, -0.84f, 0.0f, -0.136f, 0.024f, 1.0f, -0.844f, 0.0f, -0.135f, 0.026f, 1.0f, -0.849f, 0.0f, -0.133f, 0.027f, 1.0f, + -0.853f, 0.0f, -0.131f, 0.027f, 1.0f, -0.857f, 0.0f, -0.13f, 0.027f, 1.0f, -0.861f, 0.0f, -0.128f, 0.027f, 1.0f, -0.865f, 0.0f, -0.126f, 0.027f, 1.0f, + -0.868f, 0.0f, -0.125f, 0.026f, 1.0f, -0.872f, 0.0f, -0.123f, 0.025f, 1.0f, -0.876f, 0.0f, -0.121f, 0.025f, 1.0f, -0.879f, 0.0f, -0.119f, 0.024f, 1.0f, + -0.883f, 0.0f, -0.118f, 0.023f, 1.0f, -0.886f, 0.0f, -0.116f, 0.022f, 1.0f, -0.89f, 0.0f, -0.114f, 0.022f, 1.0f, -0.894f, 0.0f, -0.112f, 0.021f, 1.0f, + -0.898f, 0.0f, -0.11f, 0.022f, 1.0f, -0.901f, 0.0f, -0.107f, 0.022f, 1.0f, -0.905f, 0.0f, -0.105f, 0.024f, 1.0f, -0.909f, 0.0f, -0.103f, 0.026f, 1.0f, + -0.913f, 0.0f, -0.1f, 0.029f, 1.0f, -0.917f, 0.0f, -0.098f, 0.032f, 1.0f, -0.921f, 0.0f, -0.095f, 0.035f, 1.0f, -0.926f, 0.0f, -0.092f, 0.039f, 1.0f, + -0.93f, 0.0f, -0.09f, 0.043f, 1.0f, -0.934f, 0.0f, -0.087f, 0.047f, 1.0f, -0.938f, 0.0f, -0.084f, 0.051f, 1.0f, -0.942f, 0.0f, -0.081f, 0.055f, 1.0f, + -0.946f, 0.0f, -0.078f, 0.06f, 1.0f, -0.95f, 0.0f, -0.075f, 0.065f, 1.0f, -0.954f, 0.0f, -0.073f, 0.07f, 1.0f, -0.958f, 0.0f, -0.07f, 0.075f, 1.0f, + -0.961f, 0.0f, -0.067f, 0.081f, 1.0f, -0.965f, 0.0f, -0.064f, 0.087f, 1.0f, -0.968f, 0.0f, -0.061f, 0.092f, 1.0f, -0.972f, 0.0f, -0.058f, 0.098f, 1.0f, + -0.975f, 0.0f, -0.055f, 0.103f, 1.0f, -0.979f, 0.0f, -0.053f, 0.108f, 1.0f, -0.982f, 0.0f, -0.05f, 0.112f, 1.0f, -0.985f, 0.0f, -0.047f, 0.116f, 1.0f, + -0.988f, 0.0f, -0.045f, 0.12f, 1.0f, -0.991f, 0.0f, -0.042f, 0.123f, 1.0f, -0.994f, 0.0f, -0.039f, 0.126f, 1.0f, -0.997f, 0.0f, -0.037f, 0.129f, 1.0f, + -1.0f, 0.0f, -0.034f, 0.131f, 1.0f, -1.003f, 0.0f, -0.031f, 0.133f, 1.0f, -1.005f, 0.0f, -0.029f, 0.135f, 1.0f, -1.008f, 0.0f, -0.026f, 0.137f, 1.0f, + -1.01f, 0.0f, -0.024f, 0.139f, 1.0f, -1.013f, 0.0f, -0.021f, 0.141f, 1.0f, -1.016f, 0.0f, -0.018f, 0.143f, 1.0f, -1.018f, 0.0f, -0.016f, 0.144f, 1.0f, + -1.02f, 0.0f, -0.013f, 0.146f, 1.0f, -1.023f, 0.0f, -0.011f, 0.148f, 1.0f, -1.025f, 0.0f, -0.008f, 0.149f, 1.0f, -1.027f, 0.0f, -0.006f, 0.151f, 1.0f, + -1.029f, 0.0f, -0.003f, 0.152f, 1.0f, -1.032f, 0.0f, -0.001f, 0.154f, 1.0f, -1.034f, 0.0f, 0.001f, 0.154f, 1.0f, -1.036f, 0.0f, 0.004f, 0.155f, 1.0f, + -1.038f, 0.0f, 0.006f, 0.156f, 1.0f, -1.041f, 0.0f, 0.008f, 0.156f, 1.0f, -1.043f, 0.0f, 0.01f, 0.157f, 1.0f, -1.045f, 0.0f, 0.013f, 0.157f, 1.0f, + -1.047f, 0.0f, 0.015f, 0.157f, 1.0f, -1.049f, 0.0f, 0.018f, 0.158f, 1.0f, -1.051f, 0.0f, 0.02f, 0.158f, 1.0f, -1.053f, 0.0f, 0.023f, 0.158f, 1.0f, + -1.055f, 0.0f, 0.025f, 0.158f, 1.0f, -1.057f, 0.0f, 0.028f, 0.158f, 1.0f, -1.059f, 0.0f, 0.03f, 0.158f, 1.0f, -1.061f, 0.0f, 0.033f, 0.158f, 1.0f, + -1.063f, 0.0f, 0.036f, 0.158f, 1.0f, -1.065f, 0.0f, 0.038f, 0.158f, 1.0f, -1.067f, 0.0f, 0.041f, 0.158f, 1.0f, -1.069f, 0.0f, 0.044f, 0.157f, 1.0f, + -1.071f, 0.0f, 0.047f, 0.157f, 1.0f, -1.073f, 0.0f, 0.049f, 0.156f, 1.0f, -1.074f, 0.0f, 0.052f, 0.155f, 1.0f, -1.076f, 0.0f, 0.055f, 0.154f, 1.0f, + -1.078f, 0.0f, 0.058f, 0.153f, 1.0f, -1.08f, 0.0f, 0.061f, 0.152f, 1.0f, -1.082f, 0.0f, 0.064f, 0.15f, 1.0f, -1.083f, 0.0f, 0.067f, 0.148f, 1.0f, + -1.085f, 0.0f, 0.07f, 0.146f, 1.0f, -1.087f, 0.0f, 0.073f, 0.144f, 1.0f, -1.089f, 0.0f, 0.076f, 0.142f, 1.0f, -1.091f, 0.0f, 0.08f, 0.14f, 1.0f, + -1.092f, 0.0f, 0.083f, 0.138f, 1.0f, -1.094f, 0.0f, 0.086f, 0.136f, 1.0f, -1.096f, 0.0f, 0.09f, 0.135f, 1.0f, -1.097f, 0.0f, 0.093f, 0.134f, 1.0f, + -1.099f, 0.0f, 0.096f, 0.134f, 1.0f, -1.101f, 0.0f, 0.1f, 0.134f, 1.0f, -1.103f, 0.0f, 0.103f, 0.136f, 1.0f, -1.104f, 0.0f, 0.107f, 0.139f, 1.0f, + -1.106f, 0.0f, 0.111f, 0.144f, 1.0f, -1.107f, 0.0f, 0.114f, 0.15f, 1.0f, -1.109f, 0.0f, 0.118f, 0.158f, 1.0f, -1.11f, 0.0f, 0.122f, 0.167f, 1.0f, + -1.111f, 0.0f, 0.126f, 0.178f, 1.0f, -1.113f, 0.0f, 0.13f, 0.191f, 1.0f, -1.114f, 0.0f, 0.134f, 0.205f, 1.0f, -1.115f, 0.0f, 0.138f, 0.22f, 1.0f, + -1.116f, 0.0f, 0.142f, 0.237f, 1.0f, -1.117f, 0.0f, 0.146f, 0.254f, 1.0f, -1.118f, 0.0f, 0.15f, 0.272f, 1.0f, -1.119f, 0.0f, 0.155f, 0.291f, 1.0f, + -1.119f, 0.0f, 0.159f, 0.31f, 1.0f, -1.12f, 0.0f, 0.163f, 0.329f, 1.0f, -1.121f, 0.0f, 0.167f, 0.348f, 1.0f, -1.121f, 0.0f, 0.172f, 0.367f, 1.0f, + -1.122f, 0.0f, 0.176f, 0.386f, 1.0f, -1.122f, 0.0f, 0.18f, 0.405f, 1.0f, -1.123f, 0.0f, 0.184f, 0.423f, 1.0f, -1.123f, 0.0f, 0.189f, 0.441f, 1.0f, + -1.124f, 0.0f, 0.193f, 0.458f, 1.0f, -1.124f, 0.0f, 0.197f, 0.475f, 1.0f, -1.124f, 0.0f, 0.202f, 0.492f, 1.0f, -1.124f, 0.0f, 0.206f, 0.508f, 1.0f, + -1.125f, 0.0f, 0.21f, 0.524f, 1.0f, -1.125f, 0.0f, 0.214f, 0.539f, 1.0f, -1.125f, 0.0f, 0.218f, 0.554f, 1.0f, -1.124f, 0.0f, 0.223f, 0.568f, 1.0f, + -1.124f, 0.0f, 0.227f, 0.581f, 1.0f, -1.124f, 0.0f, 0.231f, 0.593f, 1.0f, -1.124f, 0.0f, 0.235f, 0.604f, 1.0f, -1.123f, 0.0f, 0.239f, 0.614f, 1.0f, + -1.123f, 0.0f, 0.243f, 0.624f, 1.0f, -1.122f, 0.0f, 0.247f, 0.632f, 1.0f, -1.122f, 0.0f, 0.251f, 0.64f, 1.0f, -1.121f, 0.0f, 0.255f, 0.646f, 1.0f, + -1.121f, 0.0f, 0.258f, 0.653f, 1.0f, -1.12f, 0.0f, 0.262f, 0.658f, 1.0f, -1.119f, 0.0f, 0.266f, 0.663f, 1.0f, -1.118f, 0.0f, 0.269f, 0.668f, 1.0f, + -1.117f, 0.0f, 0.272f, 0.673f, 1.0f, -1.117f, 0.0f, 0.276f, 0.678f, 1.0f, -1.116f, 0.0f, 0.279f, 0.682f, 1.0f, -1.115f, 0.0f, 0.282f, 0.687f, 1.0f, + -1.113f, 0.0f, 0.285f, 0.692f, 1.0f, -1.112f, 0.0f, 0.289f, 0.697f, 1.0f, -1.111f, 0.0f, 0.292f, 0.702f, 1.0f, -1.11f, 0.0f, 0.294f, 0.708f, 1.0f, + -1.109f, 0.0f, 0.297f, 0.713f, 1.0f, -1.108f, 0.0f, 0.3f, 0.718f, 1.0f, -1.106f, 0.0f, 0.303f, 0.724f, 1.0f, -1.105f, 0.0f, 0.306f, 0.73f, 1.0f, + -1.104f, 0.0f, 0.309f, 0.735f, 1.0f, -1.102f, 0.0f, 0.312f, 0.741f, 1.0f, -1.101f, 0.0f, 0.315f, 0.746f, 1.0f, -1.099f, 0.0f, 0.318f, 0.751f, 1.0f, + -1.098f, 0.0f, 0.321f, 0.756f, 1.0f, -1.096f, 0.0f, 0.323f, 0.761f, 1.0f, -1.094f, 0.0f, 0.326f, 0.766f, 1.0f, -1.093f, 0.0f, 0.329f, 0.771f, 1.0f, + -1.091f, 0.0f, 0.332f, 0.776f, 1.0f, -1.089f, 0.0f, 0.335f, 0.781f, 1.0f, -1.087f, 0.0f, 0.338f, 0.786f, 1.0f, -1.085f, 0.0f, 0.341f, 0.791f, 1.0f, + -1.082f, 0.0f, 0.344f, 0.797f, 1.0f, -1.08f, 0.0f, 0.347f, 0.802f, 1.0f, -1.078f, 0.0f, 0.349f, 0.808f, 1.0f, -1.075f, 0.0f, 0.352f, 0.814f, 1.0f, + -1.072f, 0.0f, 0.355f, 0.82f, 1.0f, -1.069f, 0.0f, 0.358f, 0.826f, 1.0f, -1.066f, 0.0f, 0.36f, 0.831f, 1.0f, -1.063f, 0.0f, 0.363f, 0.837f, 1.0f, + -1.059f, 0.0f, 0.366f, 0.842f, 1.0f, -1.055f, 0.0f, 0.368f, 0.847f, 1.0f, -1.051f, 0.0f, 0.371f, 0.851f, 1.0f, -1.047f, 0.0f, 0.373f, 0.856f, 1.0f, + -1.042f, 0.0f, 0.375f, 0.86f, 1.0f, -1.037f, 0.0f, 0.378f, 0.863f, 1.0f, -1.031f, 0.0f, 0.38f, 0.866f, 1.0f, -1.026f, 0.0f, 0.382f, 0.869f, 1.0f, + -1.02f, 0.0f, 0.384f, 0.871f, 1.0f, -1.014f, 0.0f, 0.386f, 0.873f, 1.0f, -1.007f, 0.0f, 0.387f, 0.875f, 1.0f, -1.0f, 0.0f, 0.389f, 0.876f, 1.0f, + -0.994f, 0.0f, 0.39f, 0.877f, 1.0f, -0.987f, 0.0f, 0.392f, 0.878f, 1.0f, -0.979f, 0.0f, 0.393f, 0.879f, 1.0f, -0.972f, 0.0f, 0.394f, 0.88f, 1.0f, + -0.964f, 0.0f, 0.395f, 0.881f, 1.0f, -0.956f, 0.0f, 0.395f, 0.881f, 1.0f, -0.948f, 0.0f, 0.395f, 0.882f, 1.0f, -0.94f, 0.0f, 0.395f, 0.882f, 1.0f, + -0.932f, 0.0f, 0.395f, 0.883f, 1.0f, -0.923f, 0.0f, 0.394f, 0.883f, 1.0f, -0.915f, 0.0f, 0.393f, 0.883f, 1.0f, -0.906f, 0.0f, 0.391f, 0.883f, 1.0f, + -0.896f, 0.0f, 0.389f, 0.881f, 1.0f, -0.887f, 0.0f, 0.386f, 0.876f, 1.0f, -0.877f, 0.0f, 0.382f, 0.866f, 1.0f, -0.867f, 0.0f, 0.378f, 0.85f, 1.0f, + -0.857f, 0.0f, 0.373f, 0.828f, 1.0f, -0.848f, 0.0f, 0.368f, 0.799f, 1.0f, -0.838f, 0.0f, 0.363f, 0.764f, 1.0f, -0.829f, 0.0f, 0.357f, 0.723f, 1.0f, + -0.819f, 0.0f, 0.352f, 0.679f, 1.0f, -0.811f, 0.0f, 0.347f, 0.631f, 1.0f, -0.802f, 0.0f, 0.342f, 0.579f, 1.0f, -0.794f, 0.0f, 0.338f, 0.525f, 1.0f, + -0.786f, 0.0f, 0.333f, 0.469f, 1.0f, -0.779f, 0.0f, 0.329f, 0.412f, 1.0f, -0.772f, 0.0f, 0.325f, 0.351f, 1.0f, -0.766f, 0.0f, 0.321f, 0.3f, 1.0f, + -0.757f, 0.0f, 0.317f, 0.219f, 1.0f, +}; + +static const float data29[205 * GP_PRIM_DATABUF_SIZE] = { + 0.816f, 0.0f, 0.326f, 0.285f, 1.0f, 0.819f, 0.0f, 0.328f, 0.287f, 1.0f, 0.821f, 0.0f, 0.33f, 0.29f, 1.0f, 0.823f, 0.0f, 0.331f, 0.295f, 1.0f, + 0.825f, 0.0f, 0.333f, 0.304f, 1.0f, 0.828f, 0.0f, 0.335f, 0.315f, 1.0f, 0.83f, 0.0f, 0.337f, 0.328f, 1.0f, 0.833f, 0.0f, 0.339f, 0.341f, 1.0f, + 0.836f, 0.0f, 0.341f, 0.355f, 1.0f, 0.839f, 0.0f, 0.343f, 0.368f, 1.0f, 0.842f, 0.0f, 0.345f, 0.38f, 1.0f, 0.845f, 0.0f, 0.347f, 0.392f, 1.0f, + 0.848f, 0.0f, 0.349f, 0.402f, 1.0f, 0.851f, 0.0f, 0.351f, 0.412f, 1.0f, 0.854f, 0.0f, 0.352f, 0.421f, 1.0f, 0.857f, 0.0f, 0.354f, 0.429f, 1.0f, + 0.861f, 0.0f, 0.356f, 0.437f, 1.0f, 0.865f, 0.0f, 0.357f, 0.444f, 1.0f, 0.869f, 0.0f, 0.359f, 0.452f, 1.0f, 0.872f, 0.0f, 0.36f, 0.46f, 1.0f, + 0.876f, 0.0f, 0.361f, 0.47f, 1.0f, 0.881f, 0.0f, 0.363f, 0.481f, 1.0f, 0.885f, 0.0f, 0.364f, 0.491f, 1.0f, 0.889f, 0.0f, 0.365f, 0.501f, 1.0f, + 0.893f, 0.0f, 0.366f, 0.511f, 1.0f, 0.898f, 0.0f, 0.367f, 0.52f, 1.0f, 0.902f, 0.0f, 0.368f, 0.528f, 1.0f, 0.906f, 0.0f, 0.37f, 0.535f, 1.0f, + 0.911f, 0.0f, 0.371f, 0.542f, 1.0f, 0.915f, 0.0f, 0.372f, 0.548f, 1.0f, 0.92f, 0.0f, 0.373f, 0.554f, 1.0f, 0.924f, 0.0f, 0.374f, 0.559f, 1.0f, + 0.929f, 0.0f, 0.375f, 0.564f, 1.0f, 0.933f, 0.0f, 0.376f, 0.567f, 1.0f, 0.938f, 0.0f, 0.377f, 0.57f, 1.0f, 0.943f, 0.0f, 0.378f, 0.572f, 1.0f, + 0.947f, 0.0f, 0.378f, 0.574f, 1.0f, 0.952f, 0.0f, 0.379f, 0.576f, 1.0f, 0.956f, 0.0f, 0.38f, 0.577f, 1.0f, 0.961f, 0.0f, 0.38f, 0.579f, 1.0f, + 0.966f, 0.0f, 0.381f, 0.581f, 1.0f, 0.971f, 0.0f, 0.381f, 0.585f, 1.0f, 0.975f, 0.0f, 0.382f, 0.588f, 1.0f, 0.98f, 0.0f, 0.382f, 0.591f, 1.0f, + 0.985f, 0.0f, 0.382f, 0.595f, 1.0f, 0.989f, 0.0f, 0.382f, 0.597f, 1.0f, 0.994f, 0.0f, 0.382f, 0.6f, 1.0f, 0.999f, 0.0f, 0.382f, 0.603f, 1.0f, + 1.003f, 0.0f, 0.382f, 0.605f, 1.0f, 1.008f, 0.0f, 0.381f, 0.607f, 1.0f, 1.013f, 0.0f, 0.381f, 0.61f, 1.0f, 1.017f, 0.0f, 0.381f, 0.611f, 1.0f, + 1.021f, 0.0f, 0.381f, 0.613f, 1.0f, 1.025f, 0.0f, 0.38f, 0.613f, 1.0f, 1.029f, 0.0f, 0.38f, 0.614f, 1.0f, 1.033f, 0.0f, 0.379f, 0.614f, 1.0f, + 1.037f, 0.0f, 0.379f, 0.614f, 1.0f, 1.041f, 0.0f, 0.378f, 0.614f, 1.0f, 1.044f, 0.0f, 0.378f, 0.614f, 1.0f, 1.048f, 0.0f, 0.377f, 0.614f, 1.0f, + 1.051f, 0.0f, 0.376f, 0.613f, 1.0f, 1.054f, 0.0f, 0.375f, 0.612f, 1.0f, 1.057f, 0.0f, 0.374f, 0.611f, 1.0f, 1.06f, 0.0f, 0.373f, 0.61f, 1.0f, + 1.063f, 0.0f, 0.372f, 0.609f, 1.0f, 1.066f, 0.0f, 0.371f, 0.609f, 1.0f, 1.068f, 0.0f, 0.37f, 0.608f, 1.0f, 1.071f, 0.0f, 0.368f, 0.608f, 1.0f, + 1.073f, 0.0f, 0.367f, 0.608f, 1.0f, 1.076f, 0.0f, 0.365f, 0.608f, 1.0f, 1.078f, 0.0f, 0.364f, 0.607f, 1.0f, 1.081f, 0.0f, 0.362f, 0.607f, 1.0f, + 1.083f, 0.0f, 0.36f, 0.607f, 1.0f, 1.085f, 0.0f, 0.358f, 0.606f, 1.0f, 1.087f, 0.0f, 0.356f, 0.606f, 1.0f, 1.09f, 0.0f, 0.354f, 0.606f, 1.0f, + 1.092f, 0.0f, 0.352f, 0.606f, 1.0f, 1.094f, 0.0f, 0.35f, 0.606f, 1.0f, 1.096f, 0.0f, 0.348f, 0.606f, 1.0f, 1.097f, 0.0f, 0.346f, 0.606f, 1.0f, + 1.099f, 0.0f, 0.344f, 0.606f, 1.0f, 1.101f, 0.0f, 0.341f, 0.606f, 1.0f, 1.103f, 0.0f, 0.339f, 0.606f, 1.0f, 1.104f, 0.0f, 0.337f, 0.607f, 1.0f, + 1.106f, 0.0f, 0.335f, 0.607f, 1.0f, 1.108f, 0.0f, 0.332f, 0.607f, 1.0f, 1.109f, 0.0f, 0.33f, 0.608f, 1.0f, 1.111f, 0.0f, 0.327f, 0.608f, 1.0f, + 1.113f, 0.0f, 0.324f, 0.608f, 1.0f, 1.114f, 0.0f, 0.322f, 0.609f, 1.0f, 1.116f, 0.0f, 0.319f, 0.609f, 1.0f, 1.117f, 0.0f, 0.316f, 0.609f, 1.0f, + 1.118f, 0.0f, 0.313f, 0.609f, 1.0f, 1.12f, 0.0f, 0.31f, 0.609f, 1.0f, 1.121f, 0.0f, 0.307f, 0.609f, 1.0f, 1.123f, 0.0f, 0.304f, 0.608f, 1.0f, + 1.124f, 0.0f, 0.301f, 0.608f, 1.0f, 1.125f, 0.0f, 0.297f, 0.607f, 1.0f, 1.126f, 0.0f, 0.294f, 0.606f, 1.0f, 1.127f, 0.0f, 0.29f, 0.605f, 1.0f, + 1.129f, 0.0f, 0.287f, 0.603f, 1.0f, 1.13f, 0.0f, 0.283f, 0.601f, 1.0f, 1.131f, 0.0f, 0.279f, 0.599f, 1.0f, 1.132f, 0.0f, 0.276f, 0.597f, 1.0f, + 1.132f, 0.0f, 0.272f, 0.595f, 1.0f, 1.133f, 0.0f, 0.268f, 0.593f, 1.0f, 1.134f, 0.0f, 0.264f, 0.592f, 1.0f, 1.135f, 0.0f, 0.26f, 0.591f, 1.0f, + 1.135f, 0.0f, 0.256f, 0.59f, 1.0f, 1.136f, 0.0f, 0.252f, 0.589f, 1.0f, 1.136f, 0.0f, 0.248f, 0.588f, 1.0f, 1.137f, 0.0f, 0.244f, 0.587f, 1.0f, + 1.137f, 0.0f, 0.24f, 0.586f, 1.0f, 1.138f, 0.0f, 0.236f, 0.585f, 1.0f, 1.138f, 0.0f, 0.232f, 0.584f, 1.0f, 1.138f, 0.0f, 0.228f, 0.582f, 1.0f, + 1.138f, 0.0f, 0.224f, 0.581f, 1.0f, 1.138f, 0.0f, 0.22f, 0.579f, 1.0f, 1.138f, 0.0f, 0.216f, 0.578f, 1.0f, 1.138f, 0.0f, 0.212f, 0.576f, 1.0f, + 1.138f, 0.0f, 0.208f, 0.575f, 1.0f, 1.138f, 0.0f, 0.204f, 0.573f, 1.0f, 1.137f, 0.0f, 0.2f, 0.572f, 1.0f, 1.137f, 0.0f, 0.196f, 0.571f, 1.0f, + 1.137f, 0.0f, 0.192f, 0.569f, 1.0f, 1.136f, 0.0f, 0.188f, 0.568f, 1.0f, 1.136f, 0.0f, 0.184f, 0.567f, 1.0f, 1.135f, 0.0f, 0.18f, 0.566f, 1.0f, + 1.134f, 0.0f, 0.176f, 0.565f, 1.0f, 1.133f, 0.0f, 0.172f, 0.563f, 1.0f, 1.132f, 0.0f, 0.168f, 0.561f, 1.0f, 1.131f, 0.0f, 0.164f, 0.559f, 1.0f, + 1.13f, 0.0f, 0.16f, 0.556f, 1.0f, 1.129f, 0.0f, 0.156f, 0.552f, 1.0f, 1.128f, 0.0f, 0.152f, 0.548f, 1.0f, 1.127f, 0.0f, 0.148f, 0.543f, 1.0f, + 1.126f, 0.0f, 0.144f, 0.537f, 1.0f, 1.124f, 0.0f, 0.14f, 0.53f, 1.0f, 1.123f, 0.0f, 0.136f, 0.522f, 1.0f, 1.122f, 0.0f, 0.132f, 0.514f, 1.0f, + 1.12f, 0.0f, 0.128f, 0.505f, 1.0f, 1.118f, 0.0f, 0.123f, 0.495f, 1.0f, 1.117f, 0.0f, 0.119f, 0.486f, 1.0f, 1.115f, 0.0f, 0.115f, 0.476f, 1.0f, + 1.113f, 0.0f, 0.111f, 0.466f, 1.0f, 1.111f, 0.0f, 0.107f, 0.456f, 1.0f, 1.11f, 0.0f, 0.102f, 0.446f, 1.0f, 1.108f, 0.0f, 0.098f, 0.436f, 1.0f, + 1.105f, 0.0f, 0.094f, 0.425f, 1.0f, 1.103f, 0.0f, 0.09f, 0.414f, 1.0f, 1.101f, 0.0f, 0.085f, 0.402f, 1.0f, 1.099f, 0.0f, 0.081f, 0.389f, 1.0f, + 1.096f, 0.0f, 0.077f, 0.377f, 1.0f, 1.094f, 0.0f, 0.072f, 0.364f, 1.0f, 1.091f, 0.0f, 0.068f, 0.351f, 1.0f, 1.088f, 0.0f, 0.063f, 0.338f, 1.0f, + 1.085f, 0.0f, 0.059f, 0.325f, 1.0f, 1.082f, 0.0f, 0.054f, 0.313f, 1.0f, 1.079f, 0.0f, 0.05f, 0.301f, 1.0f, 1.075f, 0.0f, 0.045f, 0.29f, 1.0f, + 1.071f, 0.0f, 0.04f, 0.281f, 1.0f, 1.067f, 0.0f, 0.035f, 0.272f, 1.0f, 1.063f, 0.0f, 0.031f, 0.266f, 1.0f, 1.059f, 0.0f, 0.026f, 0.261f, 1.0f, + 1.054f, 0.0f, 0.021f, 0.258f, 1.0f, 1.049f, 0.0f, 0.016f, 0.257f, 1.0f, 1.043f, 0.0f, 0.011f, 0.259f, 1.0f, 1.037f, 0.0f, 0.006f, 0.264f, 1.0f, + 1.031f, 0.0f, 0.0f, 0.272f, 1.0f, 1.025f, 0.0f, -0.005f, 0.283f, 1.0f, 1.018f, 0.0f, -0.01f, 0.296f, 1.0f, 1.011f, 0.0f, -0.015f, 0.313f, 1.0f, + 1.003f, 0.0f, -0.021f, 0.33f, 1.0f, 0.996f, 0.0f, -0.026f, 0.348f, 1.0f, 0.988f, 0.0f, -0.032f, 0.365f, 1.0f, 0.979f, 0.0f, -0.038f, 0.379f, 1.0f, + 0.971f, 0.0f, -0.044f, 0.389f, 1.0f, 0.962f, 0.0f, -0.05f, 0.394f, 1.0f, 0.953f, 0.0f, -0.057f, 0.392f, 1.0f, 0.944f, 0.0f, -0.063f, 0.384f, 1.0f, + 0.934f, 0.0f, -0.069f, 0.368f, 1.0f, 0.924f, 0.0f, -0.075f, 0.347f, 1.0f, 0.914f, 0.0f, -0.081f, 0.32f, 1.0f, 0.903f, 0.0f, -0.087f, 0.289f, 1.0f, + 0.893f, 0.0f, -0.092f, 0.256f, 1.0f, 0.882f, 0.0f, -0.098f, 0.223f, 1.0f, 0.871f, 0.0f, -0.103f, 0.191f, 1.0f, 0.86f, 0.0f, -0.108f, 0.162f, 1.0f, + 0.849f, 0.0f, -0.112f, 0.136f, 1.0f, 0.838f, 0.0f, -0.117f, 0.112f, 1.0f, 0.827f, 0.0f, -0.121f, 0.091f, 1.0f, 0.815f, 0.0f, -0.125f, 0.074f, 1.0f, + 0.804f, 0.0f, -0.128f, 0.059f, 1.0f, 0.793f, 0.0f, -0.132f, 0.046f, 1.0f, 0.782f, 0.0f, -0.135f, 0.036f, 1.0f, 0.771f, 0.0f, -0.138f, 0.028f, 1.0f, + 0.76f, 0.0f, -0.141f, 0.021f, 1.0f, 0.749f, 0.0f, -0.144f, 0.016f, 1.0f, 0.738f, 0.0f, -0.147f, 0.012f, 1.0f, 0.728f, 0.0f, -0.149f, 0.009f, 1.0f, + 0.718f, 0.0f, -0.152f, 0.006f, 1.0f, 0.708f, 0.0f, -0.154f, 0.004f, 1.0f, 0.699f, 0.0f, -0.157f, 0.003f, 1.0f, 0.691f, 0.0f, -0.159f, 0.002f, 1.0f, + 0.68f, 0.0f, -0.162f, 0.0f, 1.0f, +}; + +static const float data30[33 * GP_PRIM_DATABUF_SIZE] = { + -1.02f, 0.0f, 0.179f, 0.21f, 1.0f, -1.014f, 0.0f, 0.182f, 0.301f, 1.0f, -1.01f, 0.0f, 0.184f, 0.36f, 1.0f, -1.004f, 0.0f, 0.186f, 0.426f, 1.0f, + -0.999f, 0.0f, 0.188f, 0.479f, 1.0f, -0.993f, 0.0f, 0.19f, 0.519f, 1.0f, -0.987f, 0.0f, 0.191f, 0.545f, 1.0f, -0.981f, 0.0f, 0.192f, 0.562f, 1.0f, + -0.975f, 0.0f, 0.193f, 0.575f, 1.0f, -0.968f, 0.0f, 0.193f, 0.582f, 1.0f, -0.961f, 0.0f, 0.193f, 0.587f, 1.0f, -0.954f, 0.0f, 0.191f, 0.592f, 1.0f, + -0.946f, 0.0f, 0.19f, 0.597f, 1.0f, -0.938f, 0.0f, 0.187f, 0.6f, 1.0f, -0.93f, 0.0f, 0.183f, 0.603f, 1.0f, -0.922f, 0.0f, 0.178f, 0.606f, 1.0f, + -0.913f, 0.0f, 0.173f, 0.608f, 1.0f, -0.905f, 0.0f, 0.168f, 0.61f, 1.0f, -0.898f, 0.0f, 0.162f, 0.612f, 1.0f, -0.89f, 0.0f, 0.156f, 0.613f, 1.0f, + -0.883f, 0.0f, 0.15f, 0.612f, 1.0f, -0.877f, 0.0f, 0.143f, 0.608f, 1.0f, -0.871f, 0.0f, 0.137f, 0.602f, 1.0f, -0.865f, 0.0f, 0.131f, 0.593f, 1.0f, + -0.86f, 0.0f, 0.125f, 0.577f, 1.0f, -0.855f, 0.0f, 0.12f, 0.554f, 1.0f, -0.85f, 0.0f, 0.114f, 0.524f, 1.0f, -0.846f, 0.0f, 0.109f, 0.487f, 1.0f, + -0.842f, 0.0f, 0.104f, 0.443f, 1.0f, -0.838f, 0.0f, 0.1f, 0.394f, 1.0f, -0.835f, 0.0f, 0.095f, 0.339f, 1.0f, -0.832f, 0.0f, 0.091f, 0.295f, 1.0f, + -0.828f, 0.0f, 0.086f, 0.227f, 1.0f, +}; + +static const float data31[37 * GP_PRIM_DATABUF_SIZE] = { + 0.777f, 0.0f, 0.096f, 0.278f, 1.0f, 0.779f, 0.0f, 0.1f, 0.307f, 1.0f, 0.781f, 0.0f, 0.103f, 0.326f, 1.0f, 0.782f, 0.0f, 0.106f, 0.349f, 1.0f, + 0.784f, 0.0f, 0.109f, 0.372f, 1.0f, 0.786f, 0.0f, 0.112f, 0.395f, 1.0f, 0.789f, 0.0f, 0.116f, 0.418f, 1.0f, 0.791f, 0.0f, 0.119f, 0.44f, 1.0f, + 0.794f, 0.0f, 0.123f, 0.462f, 1.0f, 0.798f, 0.0f, 0.127f, 0.484f, 1.0f, 0.801f, 0.0f, 0.13f, 0.504f, 1.0f, 0.806f, 0.0f, 0.134f, 0.522f, 1.0f, + 0.81f, 0.0f, 0.138f, 0.54f, 1.0f, 0.815f, 0.0f, 0.142f, 0.556f, 1.0f, 0.82f, 0.0f, 0.146f, 0.571f, 1.0f, 0.826f, 0.0f, 0.15f, 0.584f, 1.0f, + 0.832f, 0.0f, 0.154f, 0.596f, 1.0f, 0.839f, 0.0f, 0.159f, 0.607f, 1.0f, 0.846f, 0.0f, 0.163f, 0.616f, 1.0f, 0.854f, 0.0f, 0.166f, 0.623f, 1.0f, + 0.862f, 0.0f, 0.17f, 0.628f, 1.0f, 0.87f, 0.0f, 0.174f, 0.632f, 1.0f, 0.878f, 0.0f, 0.177f, 0.632f, 1.0f, 0.887f, 0.0f, 0.18f, 0.63f, 1.0f, + 0.895f, 0.0f, 0.183f, 0.623f, 1.0f, 0.903f, 0.0f, 0.186f, 0.611f, 1.0f, 0.912f, 0.0f, 0.188f, 0.592f, 1.0f, 0.92f, 0.0f, 0.19f, 0.567f, 1.0f, + 0.928f, 0.0f, 0.192f, 0.533f, 1.0f, 0.935f, 0.0f, 0.193f, 0.492f, 1.0f, 0.943f, 0.0f, 0.194f, 0.442f, 1.0f, 0.95f, 0.0f, 0.196f, 0.385f, 1.0f, + 0.957f, 0.0f, 0.197f, 0.321f, 1.0f, 0.963f, 0.0f, 0.197f, 0.253f, 1.0f, 0.97f, 0.0f, 0.198f, 0.175f, 1.0f, 0.975f, 0.0f, 0.199f, 0.107f, 1.0f, + 0.983f, 0.0f, 0.199f, 0.0f, 1.0f, +}; + +static const float data32[201 * GP_PRIM_DATABUF_SIZE] = { + -0.437f, 0.0f, 0.508f, 0.0f, 1.0f, -0.435f, 0.0f, 0.51f, 0.0f, 1.0f, -0.434f, 0.0f, 0.511f, 0.0f, 1.0f, -0.432f, 0.0f, 0.512f, 0.0f, 1.0f, + -0.43f, 0.0f, 0.513f, 0.0f, 1.0f, -0.428f, 0.0f, 0.514f, 0.001f, 1.0f, -0.426f, 0.0f, 0.515f, 0.002f, 1.0f, -0.424f, 0.0f, 0.517f, 0.004f, 1.0f, + -0.422f, 0.0f, 0.518f, 0.007f, 1.0f, -0.42f, 0.0f, 0.519f, 0.012f, 1.0f, -0.418f, 0.0f, 0.521f, 0.018f, 1.0f, -0.416f, 0.0f, 0.522f, 0.025f, 1.0f, + -0.414f, 0.0f, 0.523f, 0.034f, 1.0f, -0.411f, 0.0f, 0.525f, 0.043f, 1.0f, -0.409f, 0.0f, 0.526f, 0.053f, 1.0f, -0.407f, 0.0f, 0.528f, 0.063f, 1.0f, + -0.404f, 0.0f, 0.529f, 0.073f, 1.0f, -0.402f, 0.0f, 0.531f, 0.083f, 1.0f, -0.399f, 0.0f, 0.532f, 0.092f, 1.0f, -0.396f, 0.0f, 0.534f, 0.101f, 1.0f, + -0.394f, 0.0f, 0.535f, 0.11f, 1.0f, -0.391f, 0.0f, 0.536f, 0.118f, 1.0f, -0.388f, 0.0f, 0.538f, 0.126f, 1.0f, -0.386f, 0.0f, 0.539f, 0.133f, 1.0f, + -0.383f, 0.0f, 0.54f, 0.14f, 1.0f, -0.38f, 0.0f, 0.542f, 0.147f, 1.0f, -0.377f, 0.0f, 0.543f, 0.153f, 1.0f, -0.374f, 0.0f, 0.544f, 0.159f, 1.0f, + -0.37f, 0.0f, 0.545f, 0.166f, 1.0f, -0.367f, 0.0f, 0.546f, 0.172f, 1.0f, -0.364f, 0.0f, 0.547f, 0.179f, 1.0f, -0.361f, 0.0f, 0.548f, 0.186f, 1.0f, + -0.357f, 0.0f, 0.549f, 0.193f, 1.0f, -0.354f, 0.0f, 0.55f, 0.202f, 1.0f, -0.35f, 0.0f, 0.551f, 0.211f, 1.0f, -0.347f, 0.0f, 0.552f, 0.221f, 1.0f, + -0.343f, 0.0f, 0.552f, 0.233f, 1.0f, -0.339f, 0.0f, 0.553f, 0.245f, 1.0f, -0.336f, 0.0f, 0.553f, 0.258f, 1.0f, -0.332f, 0.0f, 0.554f, 0.272f, 1.0f, + -0.328f, 0.0f, 0.554f, 0.286f, 1.0f, -0.324f, 0.0f, 0.554f, 0.301f, 1.0f, -0.321f, 0.0f, 0.555f, 0.317f, 1.0f, -0.317f, 0.0f, 0.555f, 0.332f, 1.0f, + -0.313f, 0.0f, 0.555f, 0.348f, 1.0f, -0.309f, 0.0f, 0.555f, 0.364f, 1.0f, -0.305f, 0.0f, 0.555f, 0.38f, 1.0f, -0.302f, 0.0f, 0.555f, 0.396f, 1.0f, + -0.298f, 0.0f, 0.555f, 0.411f, 1.0f, -0.294f, 0.0f, 0.555f, 0.426f, 1.0f, -0.29f, 0.0f, 0.554f, 0.44f, 1.0f, -0.287f, 0.0f, 0.554f, 0.454f, 1.0f, + -0.283f, 0.0f, 0.554f, 0.467f, 1.0f, -0.28f, 0.0f, 0.553f, 0.479f, 1.0f, -0.276f, 0.0f, 0.553f, 0.49f, 1.0f, -0.273f, 0.0f, 0.552f, 0.5f, 1.0f, + -0.269f, 0.0f, 0.552f, 0.51f, 1.0f, -0.266f, 0.0f, 0.551f, 0.519f, 1.0f, -0.263f, 0.0f, 0.55f, 0.527f, 1.0f, -0.26f, 0.0f, 0.549f, 0.534f, 1.0f, + -0.256f, 0.0f, 0.549f, 0.541f, 1.0f, -0.253f, 0.0f, 0.548f, 0.547f, 1.0f, -0.25f, 0.0f, 0.547f, 0.552f, 1.0f, -0.247f, 0.0f, 0.546f, 0.557f, 1.0f, + -0.244f, 0.0f, 0.545f, 0.561f, 1.0f, -0.241f, 0.0f, 0.544f, 0.564f, 1.0f, -0.238f, 0.0f, 0.543f, 0.567f, 1.0f, -0.235f, 0.0f, 0.542f, 0.57f, 1.0f, + -0.233f, 0.0f, 0.541f, 0.572f, 1.0f, -0.23f, 0.0f, 0.54f, 0.574f, 1.0f, -0.227f, 0.0f, 0.539f, 0.575f, 1.0f, -0.224f, 0.0f, 0.538f, 0.576f, 1.0f, + -0.221f, 0.0f, 0.537f, 0.577f, 1.0f, -0.219f, 0.0f, 0.535f, 0.578f, 1.0f, -0.216f, 0.0f, 0.534f, 0.578f, 1.0f, -0.213f, 0.0f, 0.533f, 0.579f, 1.0f, + -0.211f, 0.0f, 0.532f, 0.579f, 1.0f, -0.208f, 0.0f, 0.53f, 0.579f, 1.0f, -0.206f, 0.0f, 0.529f, 0.578f, 1.0f, -0.203f, 0.0f, 0.528f, 0.578f, 1.0f, + -0.2f, 0.0f, 0.526f, 0.577f, 1.0f, -0.198f, 0.0f, 0.525f, 0.576f, 1.0f, -0.195f, 0.0f, 0.523f, 0.575f, 1.0f, -0.193f, 0.0f, 0.522f, 0.574f, 1.0f, + -0.19f, 0.0f, 0.52f, 0.572f, 1.0f, -0.188f, 0.0f, 0.518f, 0.571f, 1.0f, -0.185f, 0.0f, 0.517f, 0.569f, 1.0f, -0.182f, 0.0f, 0.515f, 0.568f, 1.0f, + -0.18f, 0.0f, 0.513f, 0.567f, 1.0f, -0.177f, 0.0f, 0.512f, 0.565f, 1.0f, -0.174f, 0.0f, 0.51f, 0.564f, 1.0f, -0.172f, 0.0f, 0.508f, 0.562f, 1.0f, + -0.169f, 0.0f, 0.506f, 0.56f, 1.0f, -0.166f, 0.0f, 0.504f, 0.559f, 1.0f, -0.164f, 0.0f, 0.502f, 0.556f, 1.0f, -0.161f, 0.0f, 0.501f, 0.554f, 1.0f, + -0.158f, 0.0f, 0.499f, 0.552f, 1.0f, -0.155f, 0.0f, 0.497f, 0.55f, 1.0f, -0.153f, 0.0f, 0.495f, 0.547f, 1.0f, -0.15f, 0.0f, 0.493f, 0.545f, 1.0f, + -0.147f, 0.0f, 0.491f, 0.543f, 1.0f, -0.144f, 0.0f, 0.489f, 0.54f, 1.0f, -0.142f, 0.0f, 0.487f, 0.538f, 1.0f, -0.139f, 0.0f, 0.485f, 0.536f, 1.0f, + -0.136f, 0.0f, 0.483f, 0.533f, 1.0f, -0.133f, 0.0f, 0.481f, 0.53f, 1.0f, -0.13f, 0.0f, 0.479f, 0.527f, 1.0f, -0.127f, 0.0f, 0.477f, 0.524f, 1.0f, + -0.124f, 0.0f, 0.475f, 0.521f, 1.0f, -0.121f, 0.0f, 0.473f, 0.519f, 1.0f, -0.118f, 0.0f, 0.471f, 0.516f, 1.0f, -0.115f, 0.0f, 0.469f, 0.514f, 1.0f, + -0.112f, 0.0f, 0.467f, 0.511f, 1.0f, -0.109f, 0.0f, 0.465f, 0.509f, 1.0f, -0.106f, 0.0f, 0.463f, 0.506f, 1.0f, -0.103f, 0.0f, 0.461f, 0.503f, 1.0f, + -0.099f, 0.0f, 0.458f, 0.501f, 1.0f, -0.096f, 0.0f, 0.456f, 0.5f, 1.0f, -0.093f, 0.0f, 0.454f, 0.498f, 1.0f, -0.09f, 0.0f, 0.452f, 0.497f, 1.0f, + -0.086f, 0.0f, 0.45f, 0.496f, 1.0f, -0.083f, 0.0f, 0.448f, 0.496f, 1.0f, -0.079f, 0.0f, 0.446f, 0.495f, 1.0f, -0.076f, 0.0f, 0.444f, 0.495f, 1.0f, + -0.072f, 0.0f, 0.442f, 0.494f, 1.0f, -0.069f, 0.0f, 0.44f, 0.494f, 1.0f, -0.065f, 0.0f, 0.438f, 0.494f, 1.0f, -0.062f, 0.0f, 0.436f, 0.494f, 1.0f, + -0.058f, 0.0f, 0.435f, 0.494f, 1.0f, -0.054f, 0.0f, 0.433f, 0.494f, 1.0f, -0.05f, 0.0f, 0.431f, 0.494f, 1.0f, -0.046f, 0.0f, 0.43f, 0.494f, 1.0f, + -0.042f, 0.0f, 0.428f, 0.494f, 1.0f, -0.038f, 0.0f, 0.427f, 0.494f, 1.0f, -0.033f, 0.0f, 0.426f, 0.494f, 1.0f, -0.029f, 0.0f, 0.425f, 0.494f, 1.0f, + -0.025f, 0.0f, 0.424f, 0.494f, 1.0f, -0.02f, 0.0f, 0.423f, 0.494f, 1.0f, -0.015f, 0.0f, 0.422f, 0.494f, 1.0f, -0.011f, 0.0f, 0.422f, 0.494f, 1.0f, + -0.006f, 0.0f, 0.421f, 0.494f, 1.0f, -0.001f, 0.0f, 0.421f, 0.495f, 1.0f, 0.004f, 0.0f, 0.421f, 0.495f, 1.0f, 0.009f, 0.0f, 0.421f, 0.495f, 1.0f, + 0.014f, 0.0f, 0.422f, 0.495f, 1.0f, 0.019f, 0.0f, 0.422f, 0.495f, 1.0f, 0.024f, 0.0f, 0.423f, 0.495f, 1.0f, 0.029f, 0.0f, 0.424f, 0.495f, 1.0f, + 0.034f, 0.0f, 0.426f, 0.495f, 1.0f, 0.039f, 0.0f, 0.427f, 0.495f, 1.0f, 0.044f, 0.0f, 0.429f, 0.496f, 1.0f, 0.049f, 0.0f, 0.43f, 0.497f, 1.0f, + 0.054f, 0.0f, 0.432f, 0.498f, 1.0f, 0.059f, 0.0f, 0.435f, 0.5f, 1.0f, 0.064f, 0.0f, 0.438f, 0.502f, 1.0f, 0.069f, 0.0f, 0.44f, 0.506f, 1.0f, + 0.074f, 0.0f, 0.443f, 0.51f, 1.0f, 0.08f, 0.0f, 0.446f, 0.516f, 1.0f, 0.085f, 0.0f, 0.45f, 0.522f, 1.0f, 0.09f, 0.0f, 0.453f, 0.528f, 1.0f, + 0.095f, 0.0f, 0.456f, 0.533f, 1.0f, 0.101f, 0.0f, 0.46f, 0.537f, 1.0f, 0.107f, 0.0f, 0.463f, 0.539f, 1.0f, 0.112f, 0.0f, 0.467f, 0.542f, 1.0f, + 0.118f, 0.0f, 0.471f, 0.543f, 1.0f, 0.124f, 0.0f, 0.475f, 0.545f, 1.0f, 0.13f, 0.0f, 0.478f, 0.546f, 1.0f, 0.137f, 0.0f, 0.482f, 0.546f, 1.0f, + 0.143f, 0.0f, 0.486f, 0.547f, 1.0f, 0.149f, 0.0f, 0.49f, 0.546f, 1.0f, 0.156f, 0.0f, 0.493f, 0.544f, 1.0f, 0.163f, 0.0f, 0.497f, 0.54f, 1.0f, + 0.17f, 0.0f, 0.5f, 0.533f, 1.0f, 0.176f, 0.0f, 0.503f, 0.525f, 1.0f, 0.183f, 0.0f, 0.507f, 0.515f, 1.0f, 0.191f, 0.0f, 0.509f, 0.503f, 1.0f, + 0.198f, 0.0f, 0.512f, 0.491f, 1.0f, 0.205f, 0.0f, 0.515f, 0.477f, 1.0f, 0.214f, 0.0f, 0.518f, 0.462f, 1.0f, 0.222f, 0.0f, 0.521f, 0.445f, 1.0f, + 0.23f, 0.0f, 0.524f, 0.427f, 1.0f, 0.238f, 0.0f, 0.527f, 0.409f, 1.0f, 0.245f, 0.0f, 0.529f, 0.388f, 1.0f, 0.254f, 0.0f, 0.531f, 0.366f, 1.0f, + 0.262f, 0.0f, 0.532f, 0.343f, 1.0f, 0.272f, 0.0f, 0.533f, 0.317f, 1.0f, 0.282f, 0.0f, 0.534f, 0.289f, 1.0f, 0.292f, 0.0f, 0.535f, 0.258f, 1.0f, + 0.301f, 0.0f, 0.535f, 0.224f, 1.0f, 0.311f, 0.0f, 0.536f, 0.189f, 1.0f, 0.32f, 0.0f, 0.536f, 0.153f, 1.0f, 0.328f, 0.0f, 0.536f, 0.117f, 1.0f, + 0.338f, 0.0f, 0.537f, 0.084f, 1.0f, 0.346f, 0.0f, 0.537f, 0.057f, 1.0f, 0.353f, 0.0f, 0.536f, 0.037f, 1.0f, 0.361f, 0.0f, 0.536f, 0.022f, 1.0f, + 0.37f, 0.0f, 0.537f, 0.013f, 1.0f, 0.376f, 0.0f, 0.536f, 0.007f, 1.0f, 0.384f, 0.0f, 0.536f, 0.004f, 1.0f, 0.39f, 0.0f, 0.536f, 0.002f, 1.0f, + 0.399f, 0.0f, 0.535f, 0.0f, 1.0f, +}; + +static const float data33[69 * GP_PRIM_DATABUF_SIZE] = { + -0.308f, 0.0f, 0.151f, 0.363f, 1.0f, -0.31f, 0.0f, 0.15f, 0.377f, 1.0f, -0.311f, 0.0f, 0.149f, 0.386f, 1.0f, -0.313f, 0.0f, 0.149f, 0.397f, 1.0f, + -0.314f, 0.0f, 0.149f, 0.408f, 1.0f, -0.316f, 0.0f, 0.148f, 0.42f, 1.0f, -0.318f, 0.0f, 0.148f, 0.431f, 1.0f, -0.32f, 0.0f, 0.148f, 0.443f, 1.0f, + -0.322f, 0.0f, 0.148f, 0.455f, 1.0f, -0.325f, 0.0f, 0.149f, 0.467f, 1.0f, -0.327f, 0.0f, 0.149f, 0.478f, 1.0f, -0.33f, 0.0f, 0.151f, 0.49f, 1.0f, + -0.333f, 0.0f, 0.152f, 0.501f, 1.0f, -0.336f, 0.0f, 0.154f, 0.512f, 1.0f, -0.34f, 0.0f, 0.157f, 0.522f, 1.0f, -0.343f, 0.0f, 0.161f, 0.533f, 1.0f, + -0.346f, 0.0f, 0.166f, 0.543f, 1.0f, -0.349f, 0.0f, 0.171f, 0.553f, 1.0f, -0.351f, 0.0f, 0.178f, 0.563f, 1.0f, -0.352f, 0.0f, 0.186f, 0.572f, 1.0f, + -0.353f, 0.0f, 0.193f, 0.582f, 1.0f, -0.352f, 0.0f, 0.2f, 0.591f, 1.0f, -0.351f, 0.0f, 0.206f, 0.6f, 1.0f, -0.349f, 0.0f, 0.211f, 0.608f, 1.0f, + -0.347f, 0.0f, 0.215f, 0.616f, 1.0f, -0.345f, 0.0f, 0.219f, 0.623f, 1.0f, -0.343f, 0.0f, 0.222f, 0.63f, 1.0f, -0.341f, 0.0f, 0.224f, 0.637f, 1.0f, + -0.339f, 0.0f, 0.226f, 0.642f, 1.0f, -0.337f, 0.0f, 0.228f, 0.647f, 1.0f, -0.335f, 0.0f, 0.229f, 0.652f, 1.0f, -0.333f, 0.0f, 0.23f, 0.656f, 1.0f, + -0.332f, 0.0f, 0.231f, 0.66f, 1.0f, -0.33f, 0.0f, 0.232f, 0.663f, 1.0f, -0.328f, 0.0f, 0.232f, 0.666f, 1.0f, -0.327f, 0.0f, 0.233f, 0.669f, 1.0f, + -0.325f, 0.0f, 0.233f, 0.672f, 1.0f, -0.324f, 0.0f, 0.234f, 0.676f, 1.0f, -0.322f, 0.0f, 0.234f, 0.679f, 1.0f, -0.321f, 0.0f, 0.234f, 0.682f, 1.0f, + -0.319f, 0.0f, 0.234f, 0.686f, 1.0f, -0.317f, 0.0f, 0.234f, 0.689f, 1.0f, -0.316f, 0.0f, 0.234f, 0.693f, 1.0f, -0.314f, 0.0f, 0.234f, 0.697f, 1.0f, + -0.312f, 0.0f, 0.233f, 0.701f, 1.0f, -0.31f, 0.0f, 0.232f, 0.705f, 1.0f, -0.307f, 0.0f, 0.231f, 0.709f, 1.0f, -0.305f, 0.0f, 0.23f, 0.713f, 1.0f, + -0.302f, 0.0f, 0.228f, 0.716f, 1.0f, -0.299f, 0.0f, 0.225f, 0.719f, 1.0f, -0.295f, 0.0f, 0.222f, 0.722f, 1.0f, -0.292f, 0.0f, 0.217f, 0.725f, 1.0f, + -0.289f, 0.0f, 0.21f, 0.727f, 1.0f, -0.287f, 0.0f, 0.202f, 0.728f, 1.0f, -0.285f, 0.0f, 0.194f, 0.729f, 1.0f, -0.286f, 0.0f, 0.185f, 0.729f, 1.0f, + -0.287f, 0.0f, 0.178f, 0.728f, 1.0f, -0.289f, 0.0f, 0.171f, 0.726f, 1.0f, -0.292f, 0.0f, 0.166f, 0.723f, 1.0f, -0.294f, 0.0f, 0.162f, 0.717f, 1.0f, + -0.297f, 0.0f, 0.159f, 0.71f, 1.0f, -0.299f, 0.0f, 0.157f, 0.701f, 1.0f, -0.301f, 0.0f, 0.155f, 0.689f, 1.0f, -0.303f, 0.0f, 0.154f, 0.675f, 1.0f, + -0.305f, 0.0f, 0.152f, 0.659f, 1.0f, -0.306f, 0.0f, 0.151f, 0.641f, 1.0f, -0.308f, 0.0f, 0.151f, 0.62f, 1.0f, -0.309f, 0.0f, 0.15f, 0.602f, 1.0f, + -0.31f, 0.0f, 0.15f, 0.572f, 1.0f, +}; + +static const float data34[57 * GP_PRIM_DATABUF_SIZE] = { + 0.302f, 0.0f, 0.166f, 0.25f, 1.0f, 0.301f, 0.0f, 0.167f, 0.319f, 1.0f, 0.3f, 0.0f, 0.167f, 0.363f, 1.0f, 0.299f, 0.0f, 0.167f, 0.414f, 1.0f, + 0.298f, 0.0f, 0.167f, 0.459f, 1.0f, 0.296f, 0.0f, 0.168f, 0.501f, 1.0f, 0.295f, 0.0f, 0.168f, 0.539f, 1.0f, 0.293f, 0.0f, 0.169f, 0.573f, 1.0f, + 0.291f, 0.0f, 0.17f, 0.603f, 1.0f, 0.289f, 0.0f, 0.171f, 0.629f, 1.0f, 0.286f, 0.0f, 0.173f, 0.652f, 1.0f, 0.283f, 0.0f, 0.176f, 0.672f, 1.0f, + 0.279f, 0.0f, 0.18f, 0.69f, 1.0f, 0.276f, 0.0f, 0.186f, 0.705f, 1.0f, 0.272f, 0.0f, 0.195f, 0.719f, 1.0f, 0.271f, 0.0f, 0.205f, 0.73f, 1.0f, + 0.272f, 0.0f, 0.217f, 0.741f, 1.0f, 0.275f, 0.0f, 0.227f, 0.75f, 1.0f, 0.279f, 0.0f, 0.234f, 0.758f, 1.0f, 0.283f, 0.0f, 0.24f, 0.765f, 1.0f, + 0.287f, 0.0f, 0.243f, 0.771f, 1.0f, 0.291f, 0.0f, 0.245f, 0.776f, 1.0f, 0.294f, 0.0f, 0.247f, 0.781f, 1.0f, 0.296f, 0.0f, 0.248f, 0.785f, 1.0f, + 0.299f, 0.0f, 0.249f, 0.789f, 1.0f, 0.301f, 0.0f, 0.249f, 0.793f, 1.0f, 0.303f, 0.0f, 0.249f, 0.796f, 1.0f, 0.305f, 0.0f, 0.25f, 0.799f, 1.0f, + 0.306f, 0.0f, 0.25f, 0.802f, 1.0f, 0.308f, 0.0f, 0.249f, 0.805f, 1.0f, 0.31f, 0.0f, 0.249f, 0.808f, 1.0f, 0.311f, 0.0f, 0.249f, 0.81f, 1.0f, + 0.313f, 0.0f, 0.249f, 0.813f, 1.0f, 0.314f, 0.0f, 0.248f, 0.816f, 1.0f, 0.316f, 0.0f, 0.248f, 0.819f, 1.0f, 0.317f, 0.0f, 0.247f, 0.822f, 1.0f, + 0.319f, 0.0f, 0.246f, 0.825f, 1.0f, 0.321f, 0.0f, 0.245f, 0.828f, 1.0f, 0.323f, 0.0f, 0.244f, 0.832f, 1.0f, 0.325f, 0.0f, 0.243f, 0.835f, 1.0f, + 0.328f, 0.0f, 0.24f, 0.838f, 1.0f, 0.33f, 0.0f, 0.237f, 0.841f, 1.0f, 0.333f, 0.0f, 0.233f, 0.844f, 1.0f, 0.337f, 0.0f, 0.228f, 0.847f, 1.0f, + 0.339f, 0.0f, 0.219f, 0.849f, 1.0f, 0.341f, 0.0f, 0.209f, 0.852f, 1.0f, 0.34f, 0.0f, 0.197f, 0.854f, 1.0f, 0.336f, 0.0f, 0.186f, 0.856f, 1.0f, + 0.331f, 0.0f, 0.178f, 0.858f, 1.0f, 0.325f, 0.0f, 0.173f, 0.86f, 1.0f, 0.321f, 0.0f, 0.17f, 0.861f, 1.0f, 0.318f, 0.0f, 0.169f, 0.862f, 1.0f, + 0.315f, 0.0f, 0.168f, 0.864f, 1.0f, 0.312f, 0.0f, 0.167f, 0.865f, 1.0f, 0.311f, 0.0f, 0.167f, 0.866f, 1.0f, 0.309f, 0.0f, 0.166f, 0.867f, 1.0f, + 0.308f, 0.0f, 0.166f, 0.868f, 1.0f, +}; + +static const float data35[261 * GP_PRIM_DATABUF_SIZE] = { + -0.685f, 0.0f, 0.408f, 0.0f, 1.0f, -0.683f, 0.0f, 0.41f, 0.023f, 1.0f, -0.681f, 0.0f, 0.412f, 0.051f, 1.0f, -0.679f, 0.0f, 0.414f, 0.092f, 1.0f, + -0.678f, 0.0f, 0.415f, 0.125f, 1.0f, -0.676f, 0.0f, 0.417f, 0.149f, 1.0f, -0.674f, 0.0f, 0.419f, 0.167f, 1.0f, -0.672f, 0.0f, 0.42f, 0.183f, 1.0f, + -0.67f, 0.0f, 0.422f, 0.199f, 1.0f, -0.668f, 0.0f, 0.424f, 0.218f, 1.0f, -0.666f, 0.0f, 0.426f, 0.237f, 1.0f, -0.664f, 0.0f, 0.429f, 0.257f, 1.0f, + -0.661f, 0.0f, 0.431f, 0.275f, 1.0f, -0.659f, 0.0f, 0.434f, 0.291f, 1.0f, -0.657f, 0.0f, 0.436f, 0.305f, 1.0f, -0.655f, 0.0f, 0.439f, 0.315f, 1.0f, + -0.653f, 0.0f, 0.442f, 0.322f, 1.0f, -0.65f, 0.0f, 0.444f, 0.327f, 1.0f, -0.648f, 0.0f, 0.447f, 0.331f, 1.0f, -0.646f, 0.0f, 0.45f, 0.334f, 1.0f, + -0.643f, 0.0f, 0.453f, 0.334f, 1.0f, -0.641f, 0.0f, 0.456f, 0.334f, 1.0f, -0.639f, 0.0f, 0.459f, 0.334f, 1.0f, -0.636f, 0.0f, 0.462f, 0.333f, 1.0f, + -0.634f, 0.0f, 0.466f, 0.332f, 1.0f, -0.631f, 0.0f, 0.469f, 0.332f, 1.0f, -0.628f, 0.0f, 0.473f, 0.332f, 1.0f, -0.625f, 0.0f, 0.476f, 0.333f, 1.0f, + -0.622f, 0.0f, 0.48f, 0.335f, 1.0f, -0.618f, 0.0f, 0.483f, 0.338f, 1.0f, -0.615f, 0.0f, 0.488f, 0.342f, 1.0f, -0.611f, 0.0f, 0.492f, 0.347f, 1.0f, + -0.608f, 0.0f, 0.495f, 0.352f, 1.0f, -0.605f, 0.0f, 0.5f, 0.358f, 1.0f, -0.601f, 0.0f, 0.505f, 0.363f, 1.0f, -0.597f, 0.0f, 0.509f, 0.366f, 1.0f, + -0.593f, 0.0f, 0.514f, 0.367f, 1.0f, -0.589f, 0.0f, 0.518f, 0.367f, 1.0f, -0.585f, 0.0f, 0.522f, 0.369f, 1.0f, -0.582f, 0.0f, 0.526f, 0.372f, 1.0f, + -0.578f, 0.0f, 0.531f, 0.376f, 1.0f, -0.575f, 0.0f, 0.535f, 0.382f, 1.0f, -0.571f, 0.0f, 0.539f, 0.388f, 1.0f, -0.567f, 0.0f, 0.543f, 0.394f, 1.0f, + -0.563f, 0.0f, 0.547f, 0.4f, 1.0f, -0.56f, 0.0f, 0.551f, 0.406f, 1.0f, -0.556f, 0.0f, 0.555f, 0.411f, 1.0f, -0.552f, 0.0f, 0.559f, 0.415f, 1.0f, + -0.548f, 0.0f, 0.563f, 0.418f, 1.0f, -0.544f, 0.0f, 0.566f, 0.419f, 1.0f, -0.54f, 0.0f, 0.569f, 0.42f, 1.0f, -0.537f, 0.0f, 0.572f, 0.421f, 1.0f, + -0.533f, 0.0f, 0.576f, 0.421f, 1.0f, -0.529f, 0.0f, 0.579f, 0.421f, 1.0f, -0.526f, 0.0f, 0.582f, 0.422f, 1.0f, -0.523f, 0.0f, 0.585f, 0.422f, 1.0f, + -0.52f, 0.0f, 0.588f, 0.423f, 1.0f, -0.516f, 0.0f, 0.591f, 0.426f, 1.0f, -0.513f, 0.0f, 0.594f, 0.43f, 1.0f, -0.51f, 0.0f, 0.597f, 0.435f, 1.0f, + -0.507f, 0.0f, 0.6f, 0.441f, 1.0f, -0.504f, 0.0f, 0.603f, 0.447f, 1.0f, -0.501f, 0.0f, 0.606f, 0.453f, 1.0f, -0.498f, 0.0f, 0.609f, 0.458f, 1.0f, + -0.496f, 0.0f, 0.611f, 0.461f, 1.0f, -0.493f, 0.0f, 0.614f, 0.465f, 1.0f, -0.49f, 0.0f, 0.616f, 0.468f, 1.0f, -0.487f, 0.0f, 0.619f, 0.472f, 1.0f, + -0.484f, 0.0f, 0.621f, 0.476f, 1.0f, -0.482f, 0.0f, 0.624f, 0.48f, 1.0f, -0.479f, 0.0f, 0.627f, 0.484f, 1.0f, -0.476f, 0.0f, 0.629f, 0.487f, 1.0f, + -0.473f, 0.0f, 0.632f, 0.491f, 1.0f, -0.471f, 0.0f, 0.634f, 0.495f, 1.0f, -0.468f, 0.0f, 0.637f, 0.499f, 1.0f, -0.465f, 0.0f, 0.639f, 0.504f, 1.0f, + -0.462f, 0.0f, 0.641f, 0.508f, 1.0f, -0.459f, 0.0f, 0.643f, 0.513f, 1.0f, -0.456f, 0.0f, 0.646f, 0.519f, 1.0f, -0.453f, 0.0f, 0.648f, 0.525f, 1.0f, + -0.45f, 0.0f, 0.65f, 0.533f, 1.0f, -0.447f, 0.0f, 0.652f, 0.54f, 1.0f, -0.444f, 0.0f, 0.655f, 0.546f, 1.0f, -0.441f, 0.0f, 0.657f, 0.553f, 1.0f, + -0.438f, 0.0f, 0.659f, 0.56f, 1.0f, -0.435f, 0.0f, 0.662f, 0.567f, 1.0f, -0.432f, 0.0f, 0.664f, 0.574f, 1.0f, -0.429f, 0.0f, 0.666f, 0.58f, 1.0f, + -0.426f, 0.0f, 0.669f, 0.585f, 1.0f, -0.423f, 0.0f, 0.671f, 0.591f, 1.0f, -0.419f, 0.0f, 0.673f, 0.595f, 1.0f, -0.416f, 0.0f, 0.675f, 0.6f, 1.0f, + -0.412f, 0.0f, 0.678f, 0.604f, 1.0f, -0.409f, 0.0f, 0.68f, 0.609f, 1.0f, -0.405f, 0.0f, 0.682f, 0.613f, 1.0f, -0.401f, 0.0f, 0.684f, 0.618f, 1.0f, + -0.398f, 0.0f, 0.687f, 0.622f, 1.0f, -0.394f, 0.0f, 0.689f, 0.627f, 1.0f, -0.39f, 0.0f, 0.692f, 0.632f, 1.0f, -0.386f, 0.0f, 0.694f, 0.638f, 1.0f, + -0.381f, 0.0f, 0.697f, 0.643f, 1.0f, -0.377f, 0.0f, 0.7f, 0.649f, 1.0f, -0.373f, 0.0f, 0.702f, 0.654f, 1.0f, -0.368f, 0.0f, 0.705f, 0.659f, 1.0f, + -0.363f, 0.0f, 0.707f, 0.663f, 1.0f, -0.359f, 0.0f, 0.71f, 0.667f, 1.0f, -0.354f, 0.0f, 0.712f, 0.671f, 1.0f, -0.349f, 0.0f, 0.715f, 0.674f, 1.0f, + -0.345f, 0.0f, 0.717f, 0.677f, 1.0f, -0.34f, 0.0f, 0.72f, 0.68f, 1.0f, -0.335f, 0.0f, 0.722f, 0.683f, 1.0f, -0.33f, 0.0f, 0.725f, 0.685f, 1.0f, + -0.326f, 0.0f, 0.727f, 0.687f, 1.0f, -0.321f, 0.0f, 0.73f, 0.689f, 1.0f, -0.316f, 0.0f, 0.732f, 0.691f, 1.0f, -0.312f, 0.0f, 0.734f, 0.693f, 1.0f, + -0.307f, 0.0f, 0.736f, 0.694f, 1.0f, -0.302f, 0.0f, 0.738f, 0.696f, 1.0f, -0.298f, 0.0f, 0.74f, 0.697f, 1.0f, -0.293f, 0.0f, 0.741f, 0.698f, 1.0f, + -0.288f, 0.0f, 0.743f, 0.699f, 1.0f, -0.284f, 0.0f, 0.745f, 0.699f, 1.0f, -0.279f, 0.0f, 0.746f, 0.7f, 1.0f, -0.275f, 0.0f, 0.748f, 0.701f, 1.0f, + -0.27f, 0.0f, 0.749f, 0.702f, 1.0f, -0.265f, 0.0f, 0.751f, 0.702f, 1.0f, -0.261f, 0.0f, 0.752f, 0.704f, 1.0f, -0.256f, 0.0f, 0.753f, 0.705f, 1.0f, + -0.252f, 0.0f, 0.755f, 0.706f, 1.0f, -0.247f, 0.0f, 0.756f, 0.707f, 1.0f, -0.242f, 0.0f, 0.757f, 0.709f, 1.0f, -0.237f, 0.0f, 0.758f, 0.711f, 1.0f, + -0.233f, 0.0f, 0.759f, 0.713f, 1.0f, -0.228f, 0.0f, 0.761f, 0.715f, 1.0f, -0.223f, 0.0f, 0.762f, 0.717f, 1.0f, -0.218f, 0.0f, 0.763f, 0.719f, 1.0f, + -0.213f, 0.0f, 0.764f, 0.721f, 1.0f, -0.209f, 0.0f, 0.765f, 0.723f, 1.0f, -0.204f, 0.0f, 0.765f, 0.726f, 1.0f, -0.199f, 0.0f, 0.766f, 0.728f, 1.0f, + -0.194f, 0.0f, 0.767f, 0.73f, 1.0f, -0.189f, 0.0f, 0.768f, 0.731f, 1.0f, -0.183f, 0.0f, 0.769f, 0.733f, 1.0f, -0.178f, 0.0f, 0.77f, 0.735f, 1.0f, + -0.173f, 0.0f, 0.77f, 0.736f, 1.0f, -0.168f, 0.0f, 0.771f, 0.738f, 1.0f, -0.163f, 0.0f, 0.772f, 0.739f, 1.0f, -0.158f, 0.0f, 0.772f, 0.741f, 1.0f, + -0.152f, 0.0f, 0.773f, 0.742f, 1.0f, -0.147f, 0.0f, 0.774f, 0.744f, 1.0f, -0.142f, 0.0f, 0.774f, 0.746f, 1.0f, -0.137f, 0.0f, 0.775f, 0.748f, 1.0f, + -0.132f, 0.0f, 0.775f, 0.749f, 1.0f, -0.127f, 0.0f, 0.776f, 0.751f, 1.0f, -0.122f, 0.0f, 0.776f, 0.752f, 1.0f, -0.117f, 0.0f, 0.776f, 0.753f, 1.0f, + -0.112f, 0.0f, 0.777f, 0.754f, 1.0f, -0.108f, 0.0f, 0.777f, 0.755f, 1.0f, -0.103f, 0.0f, 0.777f, 0.755f, 1.0f, -0.099f, 0.0f, 0.777f, 0.756f, 1.0f, + -0.095f, 0.0f, 0.778f, 0.757f, 1.0f, -0.09f, 0.0f, 0.778f, 0.758f, 1.0f, -0.086f, 0.0f, 0.778f, 0.759f, 1.0f, -0.082f, 0.0f, 0.778f, 0.759f, 1.0f, + -0.077f, 0.0f, 0.778f, 0.76f, 1.0f, -0.073f, 0.0f, 0.779f, 0.76f, 1.0f, -0.069f, 0.0f, 0.779f, 0.761f, 1.0f, -0.064f, 0.0f, 0.779f, 0.761f, 1.0f, + -0.06f, 0.0f, 0.779f, 0.761f, 1.0f, -0.055f, 0.0f, 0.78f, 0.762f, 1.0f, -0.051f, 0.0f, 0.78f, 0.762f, 1.0f, -0.046f, 0.0f, 0.78f, 0.762f, 1.0f, + -0.041f, 0.0f, 0.78f, 0.762f, 1.0f, -0.037f, 0.0f, 0.781f, 0.762f, 1.0f, -0.032f, 0.0f, 0.781f, 0.763f, 1.0f, -0.027f, 0.0f, 0.781f, 0.763f, 1.0f, + -0.022f, 0.0f, 0.781f, 0.763f, 1.0f, -0.017f, 0.0f, 0.781f, 0.764f, 1.0f, -0.012f, 0.0f, 0.782f, 0.764f, 1.0f, -0.006f, 0.0f, 0.782f, 0.764f, 1.0f, + -0.001f, 0.0f, 0.782f, 0.765f, 1.0f, 0.004f, 0.0f, 0.782f, 0.766f, 1.0f, 0.009f, 0.0f, 0.782f, 0.766f, 1.0f, 0.015f, 0.0f, 0.782f, 0.767f, 1.0f, + 0.02f, 0.0f, 0.782f, 0.768f, 1.0f, 0.025f, 0.0f, 0.782f, 0.769f, 1.0f, 0.031f, 0.0f, 0.782f, 0.77f, 1.0f, 0.036f, 0.0f, 0.782f, 0.771f, 1.0f, + 0.042f, 0.0f, 0.782f, 0.772f, 1.0f, 0.048f, 0.0f, 0.782f, 0.773f, 1.0f, 0.053f, 0.0f, 0.782f, 0.774f, 1.0f, 0.059f, 0.0f, 0.782f, 0.775f, 1.0f, + 0.065f, 0.0f, 0.782f, 0.775f, 1.0f, 0.07f, 0.0f, 0.782f, 0.776f, 1.0f, 0.076f, 0.0f, 0.782f, 0.776f, 1.0f, 0.082f, 0.0f, 0.782f, 0.776f, 1.0f, + 0.088f, 0.0f, 0.782f, 0.776f, 1.0f, 0.094f, 0.0f, 0.782f, 0.777f, 1.0f, 0.1f, 0.0f, 0.781f, 0.777f, 1.0f, 0.106f, 0.0f, 0.781f, 0.778f, 1.0f, + 0.111f, 0.0f, 0.781f, 0.779f, 1.0f, 0.117f, 0.0f, 0.781f, 0.779f, 1.0f, 0.123f, 0.0f, 0.781f, 0.78f, 1.0f, 0.129f, 0.0f, 0.78f, 0.78f, 1.0f, + 0.135f, 0.0f, 0.78f, 0.781f, 1.0f, 0.141f, 0.0f, 0.779f, 0.781f, 1.0f, 0.147f, 0.0f, 0.779f, 0.782f, 1.0f, 0.153f, 0.0f, 0.778f, 0.783f, 1.0f, + 0.159f, 0.0f, 0.777f, 0.784f, 1.0f, 0.165f, 0.0f, 0.776f, 0.785f, 1.0f, 0.171f, 0.0f, 0.775f, 0.786f, 1.0f, 0.178f, 0.0f, 0.774f, 0.787f, 1.0f, + 0.185f, 0.0f, 0.773f, 0.788f, 1.0f, 0.192f, 0.0f, 0.772f, 0.789f, 1.0f, 0.2f, 0.0f, 0.771f, 0.79f, 1.0f, 0.208f, 0.0f, 0.77f, 0.791f, 1.0f, + 0.218f, 0.0f, 0.768f, 0.793f, 1.0f, 0.228f, 0.0f, 0.766f, 0.796f, 1.0f, 0.239f, 0.0f, 0.764f, 0.799f, 1.0f, 0.25f, 0.0f, 0.762f, 0.802f, 1.0f, + 0.261f, 0.0f, 0.759f, 0.806f, 1.0f, 0.271f, 0.0f, 0.755f, 0.81f, 1.0f, 0.282f, 0.0f, 0.752f, 0.815f, 1.0f, 0.293f, 0.0f, 0.748f, 0.819f, 1.0f, + 0.304f, 0.0f, 0.744f, 0.825f, 1.0f, 0.315f, 0.0f, 0.74f, 0.83f, 1.0f, 0.326f, 0.0f, 0.736f, 0.836f, 1.0f, 0.337f, 0.0f, 0.731f, 0.843f, 1.0f, + 0.349f, 0.0f, 0.727f, 0.85f, 1.0f, 0.361f, 0.0f, 0.722f, 0.858f, 1.0f, 0.372f, 0.0f, 0.718f, 0.866f, 1.0f, 0.384f, 0.0f, 0.712f, 0.874f, 1.0f, + 0.395f, 0.0f, 0.706f, 0.882f, 1.0f, 0.407f, 0.0f, 0.7f, 0.89f, 1.0f, 0.418f, 0.0f, 0.693f, 0.898f, 1.0f, 0.43f, 0.0f, 0.685f, 0.905f, 1.0f, + 0.442f, 0.0f, 0.677f, 0.912f, 1.0f, 0.458f, 0.0f, 0.666f, 0.918f, 1.0f, 0.473f, 0.0f, 0.654f, 0.924f, 1.0f, 0.49f, 0.0f, 0.64f, 0.93f, 1.0f, + 0.506f, 0.0f, 0.625f, 0.935f, 1.0f, 0.522f, 0.0f, 0.611f, 0.939f, 1.0f, 0.538f, 0.0f, 0.596f, 0.941f, 1.0f, 0.554f, 0.0f, 0.58f, 0.942f, 1.0f, + 0.569f, 0.0f, 0.564f, 0.941f, 1.0f, 0.584f, 0.0f, 0.548f, 0.935f, 1.0f, 0.598f, 0.0f, 0.533f, 0.925f, 1.0f, 0.612f, 0.0f, 0.517f, 0.91f, 1.0f, + 0.625f, 0.0f, 0.501f, 0.891f, 1.0f, 0.638f, 0.0f, 0.484f, 0.868f, 1.0f, 0.65f, 0.0f, 0.468f, 0.839f, 1.0f, 0.662f, 0.0f, 0.452f, 0.806f, 1.0f, + 0.671f, 0.0f, 0.437f, 0.766f, 1.0f, 0.679f, 0.0f, 0.423f, 0.718f, 1.0f, 0.685f, 0.0f, 0.412f, 0.661f, 1.0f, 0.691f, 0.0f, 0.403f, 0.595f, 1.0f, + 0.697f, 0.0f, 0.396f, 0.519f, 1.0f, 0.701f, 0.0f, 0.391f, 0.44f, 1.0f, 0.704f, 0.0f, 0.387f, 0.344f, 1.0f, 0.707f, 0.0f, 0.384f, 0.264f, 1.0f, + 0.711f, 0.0f, 0.38f, 0.133f, 1.0f, +}; + +/* ***************************************************************** */ +/* Monkey Color Data */ + +static const ColorTemplate gp_monkey_pct_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin = { + "Skin", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.733f, 0.567f, 0.359f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin_light = { + "Skin_Light", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.913f, 0.828f, 0.637f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin_shadow = { + "Skin_Shadow", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.32f, 0.29f, 0.223f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_eyes = { + "Eyes", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.773f, 0.762f, 0.73f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_pupils = { + "Pupils", + {0.107f, 0.075f, 0.051f, 0.0f}, + {0.153f, 0.057f, 0.063f, 1.0f}, +}; + +/* ***************************************************************** */ +/* Monkey API */ + +/* add a 2D Suzanne (original model created by Matias Mendiola) */ +void ED_gpencil_create_monkey(bContext *C, float mat[4][4]) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + bGPDstroke *gps; + + /* create colors */ + int color_Black = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_black); + int color_Skin = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin); + int color_Skin_Light = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_light); + int color_Skin_Shadow = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_shadow); + int color_Eyes = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_eyes); + int color_Pupils = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_pupils); + + /* layers */ + /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ + bGPDlayer *Colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + + /* frames */ + /* NOTE: No need to check for existing, as this will tkae care of it for us */ + bGPDframe *frameColor = BKE_gpencil_frame_addnew(Colors, cfra_eval); + bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, cfra_eval); + + /* generate strokes */ + gps = BKE_gpencil_add_stroke(frameColor, color_Skin, 538, 3); + BKE_gpencil_stroke_add_points(gps, data0, 538, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Eyes, 136, 3); + BKE_gpencil_stroke_add_points(gps, data1, 136, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin, 2, 3); + BKE_gpencil_stroke_add_points(gps, data2, 2, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 1, 3); + BKE_gpencil_stroke_add_points(gps, data3, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 1, 3); + BKE_gpencil_stroke_add_points(gps, data4, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 48, 3); + BKE_gpencil_stroke_add_points(gps, data5, 48, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 47, 3); + BKE_gpencil_stroke_add_points(gps, data6, 47, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 162, 3); + BKE_gpencil_stroke_add_points(gps, data7, 162, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 55, 3); + BKE_gpencil_stroke_add_points(gps, data8, 55, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 70, 3); + BKE_gpencil_stroke_add_points(gps, data9, 70, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 227, 3); + BKE_gpencil_stroke_add_points(gps, data10, 227, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 1, 3); + BKE_gpencil_stroke_add_points(gps, data11, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 123, 3); + BKE_gpencil_stroke_add_points(gps, data12, 123, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 125, 3); + BKE_gpencil_stroke_add_points(gps, data13, 125, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 45, 3); + BKE_gpencil_stroke_add_points(gps, data14, 45, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 44, 3); + BKE_gpencil_stroke_add_points(gps, data15, 44, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 84, 3); + BKE_gpencil_stroke_add_points(gps, data16, 84, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 56, 3); + BKE_gpencil_stroke_add_points(gps, data17, 56, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 59, 3); + BKE_gpencil_stroke_add_points(gps, data18, 59, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 100, 3); + BKE_gpencil_stroke_add_points(gps, data19, 100, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Eyes, 136, 3); + BKE_gpencil_stroke_add_points(gps, data20, 136, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 353, 3); + BKE_gpencil_stroke_add_points(gps, data21, 353, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 309, 3); + BKE_gpencil_stroke_add_points(gps, data22, 309, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 209, 3); + BKE_gpencil_stroke_add_points(gps, data23, 209, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 133, 3); + BKE_gpencil_stroke_add_points(gps, data24, 133, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 389, 3); + BKE_gpencil_stroke_add_points(gps, data25, 389, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 41, 3); + BKE_gpencil_stroke_add_points(gps, data26, 41, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 77, 3); + BKE_gpencil_stroke_add_points(gps, data27, 77, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 257, 3); + BKE_gpencil_stroke_add_points(gps, data28, 257, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 205, 3); + BKE_gpencil_stroke_add_points(gps, data29, 205, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 3); + BKE_gpencil_stroke_add_points(gps, data30, 33, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 37, 3); + BKE_gpencil_stroke_add_points(gps, data31, 37, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 201, 3); + BKE_gpencil_stroke_add_points(gps, data32, 201, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 69, 3); + BKE_gpencil_stroke_add_points(gps, data33, 69, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 57, 3); + BKE_gpencil_stroke_add_points(gps, data34, 57, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 261, 3); + BKE_gpencil_stroke_add_points(gps, data35, 261, mat); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} + + +/* ***************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index bd9bfcf7025..52642fb2570 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -48,6 +48,7 @@ #include "BLT_translation.h" +#include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -60,6 +61,9 @@ #include "BKE_library.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_object_deform.h" +#include "BKE_colortools.h" +#include "BKE_material.h" #include "UI_interface.h" @@ -80,6 +84,9 @@ #include "GPU_immediate_util.h" #include "GPU_state.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -89,7 +96,9 @@ typedef struct tGP_BrushEditData { /* Current editor/region/etc. */ /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ + Depsgraph *depsgraph; Scene *scene; + Object *object; ScrArea *sa; ARegion *ar; @@ -114,6 +123,10 @@ typedef struct tGP_BrushEditData { /* 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; @@ -128,6 +141,13 @@ typedef struct tGP_BrushEditData { /* - effect vector (e.g. 2D/3D translation for grab brush) */ float dvec[3]; + /* - multiframe falloff factor */ + float mf_falloff; + + /* active vertex group */ + int vrgroup; + + /* brush geometry (bounding box) */ rcti brush_rect; @@ -147,12 +167,34 @@ typedef struct tGP_BrushEditData { /* Callback for performing some brush operation on a single point */ -typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int i, +typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, 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; + } + + ToolSettings *ts = gso->scene->toolsettings; + int axis = ts->gp_sculpt.lock_axis; + + /* lock axis control */ + if (axis == 1) { + pt->x = save_pt[0]; + } + if (axis == 2) { + pt->y = save_pt[1]; + } + if (axis == 3) { + pt->z = save_pt[2]; + } +} + /* Context ---------------------------------------- */ /* Get the sculpting settings */ @@ -162,10 +204,18 @@ static GP_BrushEdit_Settings *gpsculpt_get_settings(Scene *scene) } /* Get the active brush */ -static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene) +static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) { GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; - return &gset->brush[gset->brushtype]; + GP_EditBrush_Data *brush = NULL; + if (is_weight_mode) { + brush = &gset->brush[gset->weighttype]; + } + else { + brush = &gset->brush[gset->brushtype]; + } + + return brush; } /* Brush Operations ------------------------------- */ @@ -181,6 +231,14 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso) invert ^= true; } + /* set temporary status */ + if (invert) { + gso->brush->flag |= GP_EDITBRUSH_FLAG_TMP_INVERT; + } + else { + gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; + } + return invert; } @@ -208,6 +266,9 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c influence *= fac; } + /* apply multiframe falloff */ + influence *= gso->mf_falloff; + /* return influence */ return influence; } @@ -222,29 +283,34 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c /* 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, int i, - const int radius, const int co[2]) +static bool gp_brush_smooth_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - GP_EditBrush_Data *brush = gso->brush; + // GP_EditBrush_Data *brush = gso->brush; float inf = gp_brush_influence_calc(gso, radius, co); - bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0; /* need one flag enabled by default */ - if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | - GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | - GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + if ((gso->settings->flag & + (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | + GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) { gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } /* perform smoothing */ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { - gp_smooth_stroke(gps, i, inf, affect_pressure); + BKE_gpencil_smooth_stroke(gps, pt_index, inf); } if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { - gp_smooth_stroke_strength(gps, i, inf); + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); } if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { - gp_smooth_stroke_thickness(gps, i, inf); + BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) { + BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); } return true; @@ -254,10 +320,11 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i /* Line Thickness Brush */ /* Make lines thicker or thinner by the specified amounts */ -static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_thickness_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; float inf; /* Compute strength of effect @@ -294,28 +361,29 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* Make color more or less transparent by the specified amounts */ static bool gp_brush_strength_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int i, + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + 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. + * - 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) / 10.0f; + inf = gp_brush_influence_calc(gso, radius, co) / 20.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 */ + /* make line more transparent - reduce alpha factor */ pt->strength -= inf; } else { - /* make line thicker - increase stroke pressure */ + /* make line more opaque - increase stroke strength */ pt->strength += inf; } + /* smooth the strength */ + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); /* Strength should stay within [0.0, 1.0] */ CLAMP(pt->strength, 0.0f, 1.0f); @@ -382,8 +450,9 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) } /* store references to stroke points in the initial stage */ -static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_grab_store_points( + tGP_BrushEditData *gso, bGPDstroke *gps, 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); @@ -392,7 +461,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, BLI_assert(data->size < data->capacity); /* insert this point into the set of affected points */ - data->points[data->size] = i; + data->points[data->size] = pt_index; data->weights[data->size] = inf; data->size++; @@ -431,7 +500,7 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) /* Apply grab transform to all relevant points of the affected strokes */ static void gp_brush_grab_apply_cached( - tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4]) + tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); int i; @@ -443,23 +512,21 @@ static void gp_brush_grab_apply_cached( /* adjust the amount of displacement to apply */ mul_v3_v3fl(delta, gso->dvec, data->weights[i]); - if (!parented) { - /* apply */ - add_v3_v3(&pt->x, delta); - } - else { - float fpt[3]; - /* apply transformation */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); - /* apply */ - add_v3_v3(fpt, delta); - copy_v3_v3(&pt->x, fpt); - /* 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); - } + 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); } } @@ -480,10 +547,14 @@ static void gp_brush_grab_stroke_free(void *ptr) /* Push Brush */ /* NOTE: Depends on gp_brush_grab_calc_dvec() */ -static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_push_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + 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}; @@ -493,6 +564,9 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* apply */ add_v3_v3(&pt->x, delta); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* done */ return true; } @@ -512,7 +586,8 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) float *rvec = ED_view3d_cursor3d_get(gso->scene, v3d)->location; float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - float mval_f[2] = {UNPACK2(gso->mval)}; + float mval_f[2]; + copy_v2fl_v2i(mval_f, gso->mval); float mval_prj[2]; float dvec[3]; @@ -536,12 +611,15 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) } /* Shrink distance between midpoint and this point... */ -static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_pinch_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + 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 @@ -571,6 +649,9 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* 3) Translate back to original space, with the shrinkage applied */ add_v3_v3v3(&pt->x, gso->dvec, vec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* done */ return true; } @@ -582,11 +663,14 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, * convert the rotated point and convert it into "data" space */ -static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_twist_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + 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); @@ -615,6 +699,9 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center (center is stored in dvec) */ mul_m3_v3(rmat, vec); add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); } else { const float axis[3] = {0.0f, 0.0f, 1.0f}; @@ -654,10 +741,13 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* Randomize Brush */ /* Apply some random jitter to the point */ -static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_randomize_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + 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 @@ -667,7 +757,8 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* need one flag enabled by default */ if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | - GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | + GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) { gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } @@ -710,6 +801,8 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in float dvec[3]; ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); add_v3_v3(&pt->x, dvec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); } } else { @@ -749,11 +842,76 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* only limit lower value */ CLAMP_MIN(pt->pressure, 0.0f); } + /* apply random to UV (use pressure) */ + if (gso->settings->flag & GP_BRUSHEDIT_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); + } /* done */ return true; } +/* Weight Paint Brush */ + +/* Change weight paint for vertex groups */ +static bool gp_brush_weight_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + 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); + gso->vrgroup = 0; + } + } + /* get current weight */ + float curweight = 0.0f; + for (int i = 0; i < dvert->totweight; i++) { + MDeformWeight *gpw = &dvert->dw[i]; + if (gpw->def_nr == gso->vrgroup) { + curweight = gpw->weight; + break; + } + } + + if (gp_brush_invert_check(gso)) { + /* reduce weight */ + curweight -= inf; + } + else { + /* increase weight */ + curweight += inf; + } + + CLAMP(curweight, 0.0f, 1.0f); + BKE_gpencil_vgroup_add_point_weight(dvert, gso->vrgroup, curweight); + + /* weight should stay within [0.0, 1.0] */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; +} + + + /* ************************************************ */ /* Non Callback-Based Brushes */ @@ -827,7 +985,7 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) /* Init colormap for mapping between the pasted stroke's source colour(names) * and the final colours that will be used here instead... */ - data->new_colors = gp_copybuf_validate_colormap(gso->gpd); + data->new_colors = gp_copybuf_validate_colormap(C); } /* Free custom data used for "clone" brush */ @@ -857,9 +1015,12 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) { tGPSB_CloneBrushData *data = gso->customdata; - Scene *scene = gso->scene; + Object *ob = CTX_data_active_object(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, true); bGPDstroke *gps; float delta[3]; @@ -882,17 +1043,22 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); + 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 */ - BLI_assert(new_stroke->colorname[0] != '\0'); - new_stroke->palcolor = BLI_ghash_lookup(data->new_colors, new_stroke->colorname); - - BLI_assert(new_stroke->palcolor != NULL); - BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); + Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); + if ((ma) && (BKE_object_material_slot_find_index(ob, ma) > 0)) { + gps->mat_nr = BKE_object_material_slot_find_index(ob, ma) - 1; + CLAMP_MIN(gps->mat_nr, 0); + } + else { + gps->mat_nr = 0; /* only if the color is not found */ + } /* Adjust all the stroke's points, so that the strokes * get pasted relative to where the cursor is now @@ -976,57 +1142,6 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) return true; } -/* ************************************************ */ -/* Cursor drawing */ - -/* Helper callback for drawing the cursor itself */ -static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)) -{ - GP_EditBrush_Data *brush = gpsculpt_get_brush(CTX_data_scene(C)); - - if (brush) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_smooth(true); - GPU_blend(true); - - /* Inner Ring: Light color for action of the brush */ - /* TODO: toggle between add and remove? */ - immUniformColor4ub(255, 255, 255, 200); - imm_draw_circle_wire_2d(pos, x, y, brush->size, 40); - - /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ - immUniformColor3ub(30, 30, 30); - imm_draw_circle_wire_2d(pos, x, y, brush->size + 1, 40); - - immUnbindProgram(); - - GPU_blend(false); - GPU_line_smooth(false); - } -} - -/* Turn brush cursor in on/off */ -static void gpencil_toggle_brush_cursor(bContext *C, bool enable) -{ - GP_BrushEdit_Settings *gset = gpsculpt_get_settings(CTX_data_scene(C)); - - if (gset->paintcursor && !enable) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); - gset->paintcursor = NULL; - } - else if (enable) { - /* enable cursor */ - gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), - NULL, - gp_brush_drawcursor, NULL); - } -} - - /* ************************************************ */ /* Header Info for GPencil Sculpt */ @@ -1054,17 +1169,40 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) 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_GPENCIL_WEIGHT; + /* set the brush using the tool */ + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + eGP_EditBrush_Types mode = RNA_enum_get(op->ptr, "mode"); + const bool keep_brush = RNA_boolean_get(op->ptr, "keep_brush"); + + if (!keep_brush) { + if (is_weight_mode) { + gset->weighttype = mode; + } + else { + gset->brushtype = mode; + } + } tGP_BrushEditData *gso; /* setup operator data */ gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); op->customdata = gso; + gso->depsgraph = CTX_data_depsgraph(C); /* store state */ gso->settings = gpsculpt_get_settings(scene); - gso->brush = gpsculpt_get_brush(scene); + gso->brush = gpsculpt_get_brush(scene, is_weight_mode); - gso->brush_type = gso->settings->brushtype; + 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); @@ -1078,10 +1216,31 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ 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->sa = CTX_wm_area(C); gso->ar = 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_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0; + + /* init multiedit falloff curve data before doing anything, + * so we won't have to do it again later + */ + if (gso->is_multiframe) { + curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + /* initialise custom data for brushes */ switch (gso->brush_type) { case GP_EDITBRUSH_TYPE_CLONE: @@ -1133,9 +1292,10 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gpsculpt_brush_header_set(C, gso); /* setup cursor drawing */ - WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); - gpencil_toggle_brush_cursor(C, true); - + //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } return true; } @@ -1179,7 +1339,12 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op) /* disable cursor and headerprints */ ED_workspace_status_text(C, NULL); WM_cursor_modal_restore(win); - gpencil_toggle_brush_cursor(C, false); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + + /* disable temp invert flag */ + gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; /* free operator data */ MEM_freeN(gso); @@ -1197,13 +1362,13 @@ static bool gpsculpt_brush_poll(bContext *C) static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) { - Scene *scene = gso->scene; bGPdata *gpd = gso->gpd; + bGPDlayer *gpl; - int cfra = CFRA; + int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); /* only try to add a new frame if this is the first stroke, or the frame has changed */ - if ((gpd == NULL) || (cfra == gso->cfra)) + if ((gpd == NULL) || (cfra_eval == gso->cfra)) return; /* go through each layer, and ensure that we've got a valid frame to use */ @@ -1217,21 +1382,21 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) * spent too much time editing the wrong frame... */ // XXX: should this be allowed when framelock is enabled? - if (gpf->framenum != cfra) { - BKE_gpencil_frame_addcopy(gpl, cfra); + if (gpf->framenum != cfra_eval) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); } } } /* save off new current frame, so that next update works fine */ - gso->cfra = cfra; + gso->cfra = cfra_eval; } /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ static bool gpsculpt_brush_do_stroke( - tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, + tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4], GP_BrushApplyCb apply) { GP_SpaceConversion *gsc = &gso->gsc; @@ -1246,14 +1411,9 @@ static bool gpsculpt_brush_do_stroke( bool changed = false; if (gps->totpoints == 1) { - if (!parented) { - gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); - } - else { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); - } + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(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])) { @@ -1280,19 +1440,12 @@ static bool gpsculpt_brush_do_stroke( continue; } } - if (!parented) { - gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); - } - else { - 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]); - } + 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])) || @@ -1342,9 +1495,114 @@ static bool gpsculpt_brush_do_stroke( 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, + float diff_mat[4][4]) +{ + bool changed = false; + Object *ob = CTX_data_active_object(C); + + 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_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); + break; + } + + case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); + break; + } + + case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply); + break; + } + + case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ + { + 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); + 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, diff_mat); + changed |= true; + } + break; + } + + case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); + break; + } + + case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); + break; + } + + case GP_EDITBRUSH_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 */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + } + } + + 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_depsgraph(C); \ + Object *obact = gso->object; + bGPdata *gpd = gso->gpd; bool changed = false; /* Calculate brush-specific data which applies equally to all points */ @@ -1378,104 +1636,53 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* Find visible strokes, and perform operations on those if hit */ - float diff_mat[4][4]; - bool parented = false; - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) + /* If no active frame, don't do anything... */ + if (gpl->actframe == NULL) { continue; - - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - parented = true; - } - else { - parented = false; } - 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(gpl, gps) == false) { - 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); } - switch (gso->brush_type) { - case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply); - break; - } - - case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply); - break; - } - - case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply); - break; - } - - case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ - { - 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); - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points); + 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 { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); - changed |= true; + /* No falloff */ + gso->mf_falloff = 1.0f; } - break; - } - case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); - break; + /* affect strokes in this frame */ + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); } - - case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply); - break; - } - - case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply); - break; - } - - case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply); - break; - } - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; } } + else { + /* Apply to active frame's strokes */ + gso->mf_falloff = 1.0f; + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } } CTX_DATA_END; @@ -1529,6 +1736,7 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt /* Updates */ if (changed) { + DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -1852,6 +2060,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even /* Redraw toolsettings (brush settings)? */ if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); } @@ -1860,6 +2069,19 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even /* Operator --------------------------------------------- */ +static const EnumPropertyItem prop_gpencil_sculpt_brush_items[] = { + {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points" }, + {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes" }, + {GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" }, + {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" }, + {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them" }, + {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush" }, + {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush" }, + {GP_EDITBRUSH_TYPE_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Introduce jitter/randomness into strokes" }, + {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard" }, + {GP_EDITBRUSH_TYPE_WEIGHT, "WEIGHT", 0, "Weight", "Weight Paint" }, + {0, NULL, 0, NULL, NULL } +}; void GPENCIL_OT_brush_paint(wmOperatorType *ot) { @@ -1879,13 +2101,20 @@ void GPENCIL_OT_brush_paint(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_sculpt_brush_items, 0, "Mode", "Brush mode"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + 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_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "keep_brush", false, "Keep Brush", + "Keep current brush activated"); + 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 79bfc5149ba..f662e9b42be 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -56,7 +56,6 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" -#include "DNA_workspace_types.h" #include "BKE_collection.h" #include "BKE_context.h" @@ -74,6 +73,7 @@ #include "BKE_tracking.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" @@ -149,28 +149,24 @@ static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), Poi * - assumes that the active space is the 3D-View */ static void gp_strokepoint_convertcoords( - bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, + bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, float p3d[3], const rctf *subrect) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); bGPDspoint mypt, *pt; float diff_mat[4][4]; pt = &mypt; - /* calculate difference matrix if parent object */ - if (gpl->parent == NULL) { - copy_v3_v3(&pt->x, &source_pt->x); - } - else { - /* apply parent transform */ - float fpt[3]; - ED_gpencil_parent_location(gpl, diff_mat); - mul_v3_m4v3(fpt, diff_mat, &source_pt->x); - copy_v3_v3(&pt->x, fpt); - } + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); if (gps->flag & GP_STROKE_3DSPACE) { @@ -591,7 +587,7 @@ static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const fl } } -static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, +static void gp_stroke_to_path(bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { @@ -655,7 +651,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv bp = &nu->bp[old_nbp - 1]; /* First point */ - gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { @@ -676,7 +672,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -697,9 +693,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv float p[3], next_p[3]; float dt = 0.0f; - gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p, p, next_p, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -728,10 +724,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv i++, pt++, bp++) { float p[3]; - float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, p, subrect); gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); @@ -801,7 +797,7 @@ static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt, } } -static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, +static void gp_stroke_to_bezier(bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { @@ -843,12 +839,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* get initial coordinates */ pt = gps->points; if (tot) { - gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { - gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); } } @@ -967,7 +963,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* add points */ for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); @@ -991,7 +987,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu copy_v3_v3(p3d_cur, p3d_next); if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; @@ -1130,10 +1126,12 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd) { struct Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Collection *collection = CTX_data_collection(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; @@ -1192,12 +1190,12 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG switch (mode) { case GP_STROKECONVERT_PATH: - gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + gp_stroke_to_path(C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; case GP_STROKECONVERT_CURVE: case GP_STROKECONVERT_POLY: /* convert after */ - gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + gp_stroke_to_bezier(C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; default: @@ -1238,7 +1236,9 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG */ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDframe *gpf = NULL; bGPDstroke *gps = NULL; bGPDspoint *pt; @@ -1246,7 +1246,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, 0)) || !(gps = gpf->strokes.first)) + if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0)) || !(gps = gpf->strokes.first)) return false; do { @@ -1294,10 +1294,12 @@ static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UN static bool gp_convert_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDlayer *gpl = NULL; bGPDframe *gpf = NULL; ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), @@ -1305,7 +1307,7 @@ static bool gp_convert_poll(bContext *C) */ return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_getactive(gpd)) && - (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) && + (gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0)) && (gpf->strokes.first) && (OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL)); } diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c28fea0fc41..dd1852ca8ce 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -46,18 +46,32 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" -#include "DNA_gpencil_types.h" -#include "BKE_colortools.h" +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_animsys.h" #include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_object.h" +#include "BKE_material.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -72,8 +86,13 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "ED_object.h" #include "ED_gpencil.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -84,9 +103,9 @@ /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - ToolSettings *ts = CTX_data_tool_settings(C); + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); @@ -94,19 +113,36 @@ static int gp_data_add_exec(bContext *C, wmOperator *op) } else { /* decrement user count and add new datablock */ - bGPdata *gpd = (*gpd_ptr); + /* TODO: if a datablock exists, we should make a copy of it instead of starting fresh (as in other areas) */ + Main *bmain = CTX_data_main(C); - id_us_min(&gpd->id); - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* if not exist brushes, create a new set */ - if (ts) { - if (BLI_listbase_is_empty(&ts->gp_brushes)) { - /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); - } + /* decrement user count of old GP datablock */ + if (*gpd_ptr) { + bGPdata *gpd = (*gpd_ptr); + id_us_min(&gpd->id); } + /* add new datablock, with a single layer ready to use (so users don't have to perform an extra step) */ + if (is_annotation) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + *gpd_ptr = gpd; + + /* tag for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; + + /* add new layer (i.e. a "note") */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + } + else { + /* GP Object Case - This shouldn't happen! */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + + /* add default sets of colors and brushes */ + ED_gpencil_add_defaults(C); + + /* add new layer */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } } /* notifiers */ @@ -185,28 +221,42 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - ToolSettings *ts = CTX_data_tool_settings(C); + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); /* if there's no existing Grease-Pencil data there, add some */ if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); return OPERATOR_CANCELLED; } - if (*gpd_ptr == NULL) - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - /* if not exist brushes, create a new set */ - if (ts) { - if (BLI_listbase_is_empty(&ts->gp_brushes)) { - /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); + if (*gpd_ptr == NULL) { + Main *bmain = CTX_data_main(C); + if (is_annotation) { + /* Annotations */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + + /* mark as annotation */ + (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; + } + else { + /* GP Object */ + /* NOTE: This shouldn't actually happen in practice */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + + /* add default sets of colors and brushes */ + ED_gpencil_add_defaults(C); } } /* add new layer now */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + if (is_annotation) { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + } + else { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -219,7 +269,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) /* identifiers */ ot->name = "Add New Layer"; ot->idname = "GPENCIL_OT_layer_add"; - ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block"; + ot->description = "Add new layer or note for the active data-block"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -257,6 +307,7 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_delete(gpd, gpl); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -296,6 +347,7 @@ 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)) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -346,6 +398,7 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) BKE_gpencil_layer_setactive(gpd, new_layer); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -366,6 +419,154 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************* Duplicate Frame ************************** */ +enum { + GP_FRAME_DUP_ACTIVE = 0, + GP_FRAME_DUP_ALL = 1 +}; + +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); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + int mode = RNA_enum_get(op->ptr, "mode"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; + + if (mode == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + else { + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if ((gpl->flag & GP_LAYER_LOCKED) == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + } + + } + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_duplicate(wmOperatorType *ot) +{ + static const EnumPropertyItem duplicate_mode[] = { + { GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only" }, + { GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers" }, + { 0, NULL, 0, NULL, NULL } + }; + + + /* identifiers */ + ot->name = "Duplicate Frame"; + ot->idname = "GPENCIL_OT_frame_duplicate"; + ot->description = "Make a copy of the active Grease Pencil Frame"; + + /* callbacks */ + ot->exec = gp_frame_duplicate_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); +} + +/* ********************* Clean Fill Boundaries on Frame ************************** */ +enum { + GP_FRAME_CLEAN_FILL_ACTIVE = 0, + GP_FRAME_CLEAN_FILL_ALL = 1 +}; + +static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) +{ + bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + int mode = RNA_enum_get(op->ptr, "mode"); + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (mode == GP_FRAME_CLEAN_FILL_ALL) { + init_gpf = gpl->frames.first; + } + + 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; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke */ + if (gps->flag & GP_STROKE_NOFILL) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot) +{ + static const EnumPropertyItem duplicate_mode[] = { + { GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only" }, + { GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Clean Fill Boundaries"; + ot->idname = "GPENCIL_OT_frame_clean_fill"; + ot->description = "Remove 'no fill' boundary strokes"; + + /* callbacks */ + ot->exec = gp_frame_clean_fill_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); +} + /* *********************** Hide Layers ******************************** */ static int gp_hide_exec(bContext *C, wmOperator *op) @@ -394,6 +595,7 @@ static int gp_hide_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -481,6 +683,7 @@ static int gp_reveal_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -521,6 +724,7 @@ static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -558,6 +762,7 @@ static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -630,6 +835,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -673,22 +879,28 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op) BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); } - /* read all frames from next layer */ + /* read all frames from next layer and add any missing in current layer */ for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { - /* try to find frame in active layer */ + /* try to find frame in current layer */ bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); if (!frame) { - /* nothing found, create new */ + bGPDframe *actframe = BKE_gpencil_layer_getframe(gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); + /* duplicate strokes of current active frame */ + if (actframe) { + BKE_gpencil_frame_copy_strokes(actframe, frame); + } } /* add to tail all strokes */ BLI_movelisttolist(&frame->strokes, &gpf->strokes); } + /* Now delete next layer */ BKE_gpencil_layer_delete(gpd, gpl_next); BLI_ghash_free(gh_frames_cur, NULL, NULL); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -750,6 +962,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_setactive(gpd, gpl); /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -788,6 +1001,7 @@ enum { 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 = BKE_gpencil_layer_getactive(gpd); bGPDstroke *gps; @@ -797,79 +1011,95 @@ static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bGPDframe *gpf = gpl->actframe; - /* temp listbase to store selected strokes */ - ListBase selected = {NULL}; const int direction = RNA_enum_get(op->ptr, "direction"); - /* verify if any selected stroke is in the extreme of the stack and select to move */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - /* only if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* 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(gpl, gps) == false) { - continue; - } - /* some stroke is already at front*/ - if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { - if (gps == gpf->strokes.last) { - return OPERATOR_CANCELLED; - } - } - /* some stroke is already at botom */ - if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { - if (gps == gpf->strokes.first) { - return OPERATOR_CANCELLED; - } - } - /* add to list */ - BLI_addtail(&selected, BLI_genericNodeN(gps)); + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* temp listbase to store selected strokes by layer */ + ListBase selected = { NULL }; + bGPDframe *gpf = gpl->actframe; + if (gpl->flag & GP_LAYER_LOCKED) { + continue; } - } - /* Now do the movement of the stroke */ - switch (direction) { - /* Bring to Front */ - case GP_STROKE_MOVE_TOP: - for (LinkData *link = selected.first; link; link = link->next) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&gpf->strokes, gps); + if (gpf == NULL) { + continue; + } + bool gpf_lock = false; + /* verify if any selected stroke is in the extreme of the stack and select to move */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* 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; + } + /* some stroke is already at front*/ + if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { + if (gps == gpf->strokes.last) { + gpf_lock = true; + continue; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + gpf_lock = true; + continue; + } + } + /* add to list (if not locked) */ + if (!gpf_lock) { + BLI_addtail(&selected, BLI_genericNodeN(gps)); + } } - break; - /* Bring Forward */ - case GP_STROKE_MOVE_UP: - for (LinkData *link = selected.last; link; link = link->prev) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, 1); + } + /* Now do the movement of the stroke */ + if (!gpf_lock) { + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_addtail(&gpf->strokes, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, 1); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, -1); + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_addhead(&gpf->strokes, gps); + } + break; + default: + BLI_assert(0); + break; } - break; - /* Send Backward */ - case GP_STROKE_MOVE_DOWN: - for (LinkData *link = selected.first; link; link = link->next) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, -1); - } - break; - /* Send to Back */ - case GP_STROKE_MOVE_BOTTOM: - for (LinkData *link = selected.last; link; link = link->prev) { - gps = link->data; - BLI_remlink(&gpf->strokes, gps); - BLI_addhead(&gpf->strokes, gps); - } - break; - default: - BLI_assert(0); - break; + } + BLI_freelistN(&selected); } - BLI_freelistN(&selected); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -890,58 +1120,70 @@ void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) ot->idname = "GPENCIL_OT_stroke_arrange"; ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; - /* api callbacks */ + /* callbacks */ ot->exec = gp_stroke_arrange_exec; ot->poll = gp_active_layer_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* properties */ ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); } + /* ******************* Move Stroke to new color ************************** */ static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette; - bGPDpalettecolor *color; + Object *ob = CTX_data_active_object(C); + Material *ma = give_current_material(ob, ob->actcol); + int idx = BKE_object_material_slot_find_index(ob, ma) - 1; /* sanity checks */ if (ELEM(NULL, gpd)) { return OPERATOR_CANCELLED; } - palette = BKE_gpencil_palette_getactive(gpd); - color = BKE_gpencil_palettecolor_getactive(palette); - if (ELEM(NULL, palette, color)) { + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + if (ELEM(NULL, ma)) { return OPERATOR_CANCELLED; } /* loop all strokes */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (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) { - /* 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(gpl, gps) == false) - continue; + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - /* asign new color (only if different) */ - if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { - BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); - gps->palcolor = color; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* 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; + + /* asign new color */ + gps->mat_nr = idx; } } } } } + CTX_DATA_END; + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -954,9 +1196,12 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) ot->idname = "GPENCIL_OT_stroke_change_color"; ot->description = "Move selected strokes to active color"; - /* api callbacks */ + /* callbacks */ ot->exec = gp_stroke_change_color_exec; ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************* Lock color of non selected Strokes colors ************************** */ @@ -964,19 +1209,21 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette; + + Object *ob = CTX_data_active_object(C); + + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); /* sanity checks */ if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = BKE_gpencil_palette_getactive(gpd); - if (ELEM(NULL, palette)) - return OPERATOR_CANCELLED; - /* first lock all colors */ - for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag |= PC_COLOR_LOCKED; + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = (*matar)[i]; + tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; + } /* loop all selected strokes and unlock any color */ @@ -991,14 +1238,14 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) continue; } /* unlock color */ - if (gps->palcolor != NULL) { - gps->palcolor->flag &= ~PC_COLOR_LOCKED; - } + Material *tmp_ma = (*matar)[gps->mat_nr]; + tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; } } } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1014,230 +1261,18 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) /* api callbacks */ ot->exec = gp_stroke_lock_color_exec; ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ************************************************ */ /* Drawing Brushes Operators */ -/* ******************* Add New Brush ************************ */ - -/* add new brush - wrapper around API */ -static int gp_brush_add_exec(bContext *C, wmOperator *op) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - - /* if there's no existing container */ - if (ts == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); - return OPERATOR_CANCELLED; - } - /* add new brush now */ - BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Brush"; - ot->idname = "GPENCIL_OT_brush_add"; - ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil data-block"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_brush_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Remove Active Brush ************************* */ - -static int gp_brush_remove_exec(bContext *C, wmOperator *op) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - /* sanity checks */ - if (ELEM(NULL, ts, brush)) - return OPERATOR_CANCELLED; - - if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one"); - return OPERATOR_CANCELLED; - } - - - /* make the brush before this the new active brush - * - use the one after if this is the first - * - if this is the only brush, this naturally becomes NULL - */ - if (brush->prev) - BKE_gpencil_brush_setactive(ts, brush->prev); - else - BKE_gpencil_brush_setactive(ts, brush->next); - - /* delete the brush now... */ - BKE_gpencil_brush_delete(ts, brush); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Brush"; - ot->idname = "GPENCIL_OT_brush_remove"; - ot->description = "Remove active Grease Pencil drawing brush"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_brush_remove_exec; - ot->poll = gp_active_brush_poll; -} - -/* ********************** Change Brush ***************************** */ - -static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) -{ - uiPopupMenu *pup; - uiLayout *layout; - - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -static int gp_brush_change_exec(bContext *C, wmOperator *op) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = NULL; - int brush_num = RNA_enum_get(op->ptr, "brush"); - - /* Get brush or create new one */ - if (brush_num == -1) { - /* Create brush */ - brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); - } - else { - /* Try to get brush */ - brush = BLI_findlink(&ts->gp_brushes, brush_num); - - if (brush == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); - return OPERATOR_CANCELLED; - } - } - - /* Set active brush */ - BKE_gpencil_brush_setactive(ts, brush); - - /* updates */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_change(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Brush"; - ot->idname = "GPENCIL_OT_brush_change"; - ot->description = "Change active Grease Pencil drawing brush"; - - /* callbacks */ - ot->invoke = gp_brush_change_invoke; - ot->exec = gp_brush_change_exec; - ot->poll = gp_active_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* gp brush to use (dynamic enum) */ - ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); - RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); -} - -/* ******************* Move Brush Up/Down ************************** */ - -enum { - GP_BRUSH_MOVE_UP = -1, - GP_BRUSH_MOVE_DOWN = 1 -}; - -static int gp_brush_move_exec(bContext *C, wmOperator *op) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - int direction = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, ts, brush)) { - return OPERATOR_CANCELLED; - } - - /* up or down? */ - if (direction == GP_BRUSH_MOVE_UP) { - /* up */ - BLI_remlink(&ts->gp_brushes, brush); - BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); - } - else if (direction == GP_BRUSH_MOVE_DOWN) { - /* down */ - BLI_remlink(&ts->gp_brushes, brush); - BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); - } - else { - BLI_assert(0); - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_move(wmOperatorType *ot) -{ - static const EnumPropertyItem slot_move[] = { - {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, - {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL } - }; - - /* identifiers */ - ot->name = "Move Brush"; - ot->idname = "GPENCIL_OT_brush_move"; - ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; - - /* api callbacks */ - ot->exec = gp_brush_move_exec; - ot->poll = gp_active_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); -} - /* ******************* Brush create presets ************************** */ - static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) { - ToolSettings *ts = CTX_data_tool_settings(C); - BKE_gpencil_brush_init_presets(ts); + BKE_brush_gpencil_presets(C); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1260,77 +1295,12 @@ void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) } -/* ***************** Copy Brush ************************ */ - -static int gp_brush_copy_exec(bContext *C, wmOperator *op) -{ - ToolSettings *ts = CTX_data_tool_settings(C); - - /* if there's no existing container */ - if (ts == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); - return OPERATOR_CANCELLED; - } - - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - bGPDbrush *newbrush; - - /* sanity checks */ - if (ELEM(NULL, brush)) - return OPERATOR_CANCELLED; - - /* create a brush and duplicate data */ - newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true); - newbrush->thickness = brush->thickness; - newbrush->draw_smoothfac = brush->draw_smoothfac; - newbrush->draw_smoothlvl = brush->draw_smoothlvl; - newbrush->sublevel = brush->sublevel; - newbrush->flag = brush->flag; - newbrush->draw_sensitivity = brush->draw_sensitivity; - newbrush->draw_strength = brush->draw_strength; - newbrush->draw_jitter = brush->draw_jitter; - newbrush->draw_angle = brush->draw_angle; - newbrush->draw_angle_factor = brush->draw_angle_factor; - newbrush->draw_random_press = brush->draw_random_press; - newbrush->draw_random_sub = brush->draw_random_sub; - - /* free automatic curves created by default (replaced by copy) */ - curvemapping_free(newbrush->cur_sensitivity); - curvemapping_free(newbrush->cur_strength); - curvemapping_free(newbrush->cur_jitter); - - /* make a copy of curves */ - newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); - newbrush->cur_strength = curvemapping_copy(brush->cur_strength); - newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); - - BKE_gpencil_brush_setactive(ts, newbrush); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_brush_copy(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Brush"; - ot->idname = "GPENCIL_OT_brush_copy"; - ot->description = "Copy current Grease Pencil drawing brush"; - - /* callbacks */ - ot->exec = gp_brush_copy_exec; - ot->poll = gp_active_brush_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* ***************** Select Brush ************************ */ static int gp_brush_select_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); + Main *bmain = CTX_data_main(C); /* if there's no existing container */ if (ts == NULL) { @@ -1339,18 +1309,23 @@ static int gp_brush_select_exec(bContext *C, wmOperator *op) } const int index = RNA_int_get(op->ptr, "index"); - bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); - /* sanity checks */ - if (ELEM(NULL, brush)) { - return OPERATOR_CANCELLED; + + Paint *paint = BKE_brush_get_gpencil_paint(ts); + int i = 0; + for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { + if (brush->ob_mode == OB_MODE_GPENCIL_PAINT) { + if (i == index) { + BKE_paint_brush_set(paint, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + i++; + } } - BKE_gpencil_brush_setactive(ts, brush); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + return OPERATOR_CANCELLED; } void GPENCIL_OT_brush_select(wmOperatorType *ot) @@ -1371,182 +1346,694 @@ void GPENCIL_OT_brush_select(wmOperatorType *ot) RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); } -/* ************************************************ */ -/* Palette Operators */ +/* ***************** Select Sculpt Brush ************************ */ -/* ******************* Add New Palette ************************ */ - -/* add new palette - wrapper around API */ -static int gp_palette_add_exec(bContext *C, wmOperator *op) +static int gp_sculpt_select_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + ToolSettings *ts = CTX_data_tool_settings(C); - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); return OPERATOR_CANCELLED; } - if (*gpd_ptr == NULL) - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* add new palette now */ - BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palette_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Palette"; - ot->idname = "GPENCIL_OT_palette_add"; - ot->description = "Add new Grease Pencil palette for the active Grease Pencil data-block"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_palette_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Remove Active Palette ************************* */ - -static int gp_palette_remove_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + const int index = RNA_int_get(op->ptr, "index"); + GP_BrushEdit_Settings *gp_sculpt = &ts->gp_sculpt; /* sanity checks */ - if (ELEM(NULL, gpd, palette)) - return OPERATOR_CANCELLED; - - if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one"); + if (ELEM(NULL, gp_sculpt)) { return OPERATOR_CANCELLED; } - - /* make the palette before this the new active palette - * - use the one after if this is the first - * - if this is the only palette, this naturally becomes NULL - */ - if (palette->prev) - BKE_gpencil_palette_setactive(gpd, palette->prev); - else - BKE_gpencil_palette_setactive(gpd, palette->next); - - /* delete the palette now... */ - BKE_gpencil_palette_delete(gpd, palette); - + if (index < TOT_GP_EDITBRUSH_TYPES - 1) { + gp_sculpt->brushtype = index; + } /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palette_remove(wmOperatorType *ot) +void GPENCIL_OT_sculpt_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove palette"; - ot->idname = "GPENCIL_OT_palette_remove"; - ot->description = "Remove active Grease Pencil palette"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->name = "Select Sculpt Brush"; + ot->idname = "GPENCIL_OT_sculpt_select"; + ot->description = "Select a Grease Pencil sculpt brush"; /* callbacks */ - ot->exec = gp_palette_remove_exec; - ot->poll = gp_active_palette_poll; -} - -/* ********************** Change Palette ***************************** */ - -static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) -{ - uiPopupMenu *pup; - uiLayout *layout; - - /* call the menu, which will call this operator again, hence the canceled */ - pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); - layout = UI_popup_menu_layout(pup); - uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; -} - -static int gp_palette_change_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDpalette *palette = NULL; - int palette_num = RNA_enum_get(op->ptr, "palette"); - - /* Get palette or create new one */ - if (palette_num == -1) { - /* Create palette */ - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); - } - else { - /* Try to get palette */ - palette = BLI_findlink(&gpd->palettes, palette_num); - - if (palette == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); - return OPERATOR_CANCELLED; - } - } - - /* Set active palette */ - BKE_gpencil_palette_setactive(gpd, palette); - - /* updates */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palette_change(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Change Palette"; - ot->idname = "GPENCIL_OT_palette_change"; - ot->description = "Change active Grease Pencil palette"; - - /* callbacks */ - ot->invoke = gp_palette_change_invoke; - ot->exec = gp_palette_change_exec; - ot->poll = gp_active_palette_poll; + ot->exec = gp_sculpt_select_exec; + ot->poll = gp_add_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* gp palette to use (dynamic enum) */ - ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); - RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Sculpt Brush", 0, INT_MAX); } -/* ******************* Lock and hide any color non used in current layer ************************** */ +/*********************** Vertex Groups ***********************************/ -static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) +static bool gpencil_vertex_group_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ELEM(ob->mode, + OB_MODE_GPENCIL_EDIT, + OB_MODE_GPENCIL_SCULPT)) + { + return true; + } + } + } + + return false; +} + +static bool gpencil_vertex_group_weight_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ob->mode == OB_MODE_GPENCIL_WEIGHT) + { + return true; + } + } + } + + return false; +} + +static int gpencil_vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) + return OPERATOR_CANCELLED; + + ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_assign(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Assign to Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_assign"; + ot->description = "Assign the selected vertices to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_assign_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* remove point from vertex group */ +static int gpencil_vertex_group_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; + + ED_gpencil_vgroup_remove(C, ob); + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_remove_from(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove from Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_remove_from"; + ot->description = "Remove the selected vertices from active or all vertex group(s)"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_remove_from_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} + +static int gpencil_vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; + + ED_gpencil_vgroup_select(C, ob); + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_select"; + ot->description = "Select all the vertices assigned to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_select_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int gpencil_vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; + + ED_gpencil_vgroup_deselect(C, ob); + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_deselect(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Deselect Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_deselect"; + ot->description = "Deselect all selected vertices assigned to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_deselect_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* invert */ +static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) + return OPERATOR_CANCELLED; + + MDeformVert *dvert; + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return OPERATOR_CANCELLED; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + if (dvert->dw == NULL) { + BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, 1.0f); + } + else if (dvert->dw->weight == 1.0f) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + else { + dvert->dw->weight = 1.0f - dvert->dw->weight; + } + } + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_invert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Invert Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_invert"; + ot->description = "Invert weights to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_invert_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* smooth */ +static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) +{ + const float fac = RNA_float_get(op->ptr, "factor"); + const int repeat = RNA_int_get(op->ptr, "repeat"); + + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) + return OPERATOR_CANCELLED; + + bGPDspoint *pta, *ptb, *ptc; + MDeformVert *dverta, *dvertb; + + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return OPERATOR_CANCELLED; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int s = 0; s < repeat; s++) { + for (int i = 0; i < gps->totpoints; i++) { + /* previous point */ + if (i > 0) { + pta = &gps->points[i - 1]; + dverta = &gps->dvert[i - 1]; + } + else { + pta = &gps->points[i]; + dverta = &gps->dvert[i]; + } + /* current */ + ptb = &gps->points[i]; + dvertb = &gps->dvert[i]; + /* next point */ + if (i + 1 < gps->totpoints) { + ptc = &gps->points[i + 1]; + } + else { + ptc = &gps->points[i]; + } + + float wa = BKE_gpencil_vgroup_use_index(dverta, def_nr); + float wb = BKE_gpencil_vgroup_use_index(dvertb, def_nr); + CLAMP_MIN(wa, 0.0f); + CLAMP_MIN(wb, 0.0f); + + /* the optimal value is the corresponding to the interpolation of the weight + * at the distance of point b + */ + const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = interpf(wa, wb, opfac); + /* Based on influence factor, blend between original and optimal */ + wb = interpf(wb, optimal, fac); + BKE_gpencil_vgroup_add_point_weight(dvertb, def_nr, wb); + } + } + } + CTX_DATA_END; + + /* notifiers */ + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_vertex_group_smooth(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Smooth Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_smooth"; + ot->description = "Smooth weights to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_smooth_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); +} + +/****************************** Join ***********************************/ + +/* userdata for joined_gpencil_fix_animdata_cb() */ +typedef struct tJoinGPencil_AdtFixData { + bGPdata *src_gpd; + bGPdata *tar_gpd; + + GHash *names_map; +} tJoinGPencil_AdtFixData; + +/* Callback to pass to BKE_fcurves_main_cb() for RNA Paths attached to each F-Curve used in the AnimData */ +static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) +{ + tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; + ID *src_id = &afd->src_gpd->id; + ID *dst_id = &afd->tar_gpd->id; + + GHashIterator gh_iter; + + /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ + if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed; this still means there will be some waste if there aren't many drivers/keys */ + if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { + fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "layers", + old_name, new_name, 0, 0, false); + + /* we don't want to apply a second remapping on this F-Curve now, + * so stop trying to fix names names + */ + break; + } + } + } + + /* Fix driver targets */ + if (fcu->driver) { + /* Fix driver references to invalid ID's */ + for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { + /* only change the used targets, since the others will need fixing manually anyway */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + /* change the ID's used... */ + if (dtar->id == src_id) { + dtar->id = dst_id; + + /* also check on the subtarget... + * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data + */ + if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed */ + if (!STREQ(old_name, new_name)) { + if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { + /* Fix up path */ + dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "layers", + old_name, new_name, 0, 0, false); + break; /* no need to try any more names for layer path */ + } + } + } + } + } + } + DRIVER_TARGETS_LOOPER_END + } + } +} + +/* join objects called from OBJECT_OT_join */ +int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + bGPdata *gpd_dst = NULL; + bool ok = false; + + /* Ensure we're in right mode and that the active object is correct */ + if (!obact || obact->type != OB_GPENCIL) + return OPERATOR_CANCELLED; + + bGPdata *gpd = (bGPdata *)obact->data; + if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* Ensure all rotations are applied before */ + // XXX: Why don't we apply them here instead of warning? + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if (base->object->type == OB_GPENCIL) { + if ((base->object->rot[0] != 0) || + (base->object->rot[1] != 0) || + (base->object->rot[2] != 0)) + { + BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); + return OPERATOR_CANCELLED; + } + } + } + CTX_DATA_END; + + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if (base->object == obact) { + ok = true; + break; + } + } + CTX_DATA_END; + + /* that way the active object is always selected */ + if (ok == false) { + BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); + return OPERATOR_CANCELLED; + } + + gpd_dst = obact->data; + Object *ob_dst = obact; + + /* loop and join all data */ + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if ((base->object->type == OB_GPENCIL) && (base->object != obact)) { + /* we assume that each datablock is not already used in active object */ + if (obact->data != base->object->data) { + Object *ob_src = base->object; + bGPdata *gpd_src = base->object->data; + + /* Apply all GP modifiers before */ + for (GpencilModifierData *md = base->object->greasepencil_modifiers.first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + if (mti->bakeModifier) { + mti->bakeModifier(bmain, depsgraph, md, base->object); + } + } + + /* copy vertex groups to the base one's */ + int old_idx = 0; + for (bDeformGroup *dg = base->object->defbase.first; dg; dg = dg->next) { + bDeformGroup *vgroup = MEM_dupallocN(dg); + int idx = BLI_listbase_count(&obact->defbase); + defgroup_unique_name(vgroup, obact); + BLI_addtail(&obact->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) { + MDeformVert *dvert; + int i; + for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { + if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { + dvert->dw->def_nr = idx; + } + } + } + } + } + old_idx++; + } + if (obact->defbase.first && obact->actdef == 0) + obact->actdef = 1; + + /* add missing materials reading source materials and checking in destination object */ + Material ***matar = give_matarar(ob_src); + short *totcol = give_totcolp(ob_src); + + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = (*matar)[i]; + if (BKE_object_material_slot_find_index(ob_dst, tmp_ma) == 0) { + BKE_object_material_slot_add(bmain, ob_dst); + assign_material(bmain, ob_dst, tmp_ma, ob_dst->totcol, BKE_MAT_ASSIGN_EXISTING); + } + } + + /* duplicate bGPDlayers */ + tJoinGPencil_AdtFixData afd = {0}; + afd.src_gpd = gpd_src; + afd.tar_gpd = gpd_dst; + afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); + + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + + sub_v3_v3v3(offset_global, obact->loc, base->object->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + + for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { + bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all stroke points */ + ED_gpencil_parent_location(depsgraph, base->object, gpd_src, gpl_src, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + Material *ma_src = NULL; + int idx; + for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + + /* reasign material. Look old material and try to find in dst */ + ma_src = give_current_material(ob_src, gps->mat_nr + 1); + if (ma_src != NULL) { + idx = BKE_object_material_slot_find_index(ob_dst, ma_src); + if (idx > 0) { + gps->mat_nr = idx - 1; + } + else { + gps->mat_nr = 0; + } + } + else { + gps->mat_nr = 0; + } + + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); + } + } + } + + /* be sure name is unique in new object */ + BLI_uniquename(&gpd_dst->layers, gpl_new, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_new->info)); + BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); + + /* add to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl_new); + } + + /* Fix all the animation data */ + BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); + BLI_ghash_free(afd.names_map, MEM_freeN, NULL); + + /* Only copy over animdata now, after all the remapping has been done, + * so that we don't have to worry about ambiguities re which datablock + * a layer came from! + */ + if (base->object->adt) { + if (obact->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + obact->adt = BKE_animdata_copy(bmain, base->object->adt, false, true); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(bmain, &obact->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (gpd_src->adt) { + if (gpd_dst->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, false, true); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(bmain, &gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + } + + /* Free the old object */ + ED_object_base_free_and_unlink(bmain, scene, base->object); + } + } + CTX_DATA_END; + + DEG_relations_tag_update(bmain); /* because we removed object(s) */ + + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; +} + +/* Color Handle operator */ +static bool gpencil_active_color_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + short *totcolp = give_totcolp(ob); + return *totcolp > 0; + } + return false; +} + + +/* ******************* 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); - bGPDpalette *palette; + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = NULL; /* sanity checks */ if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = BKE_gpencil_palette_getactive(gpd); - if (ELEM(NULL, palette)) + /* first lock and hide all colors */ + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; - /* first lock and hide all colors */ - for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag |= PC_COLOR_LOCKED; - palcolor->flag |= PC_COLOR_HIDE; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; + gp_style->flag |= GP_STYLE_COLOR_HIDE; } /* loop all selected strokes and unlock any color used in active layer */ @@ -1558,140 +2045,49 @@ static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; + gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); /* unlock/unhide color if not unlocked before */ - if (gps->palcolor != NULL) { - gps->palcolor->flag &= ~PC_COLOR_LOCKED; - gps->palcolor->flag &= ~PC_COLOR_HIDE; + if (gp_style != NULL) { + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; } } } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) +void GPENCIL_OT_lock_layer(wmOperatorType *ot) { /* identifiers */ ot->name = "Disable Unused Layer Colors"; - ot->idname = "GPENCIL_OT_palette_lock_layer"; + ot->idname = "GPENCIL_OT_lock_layer"; ot->description = "Lock and hide any color not used in any layer"; /* api callbacks */ - ot->exec = gp_palette_lock_layer_exec; + ot->exec = gpencil_lock_layer_exec; ot->poll = gp_active_layer_poll; } -/* ************************************************ */ -/* Palette Colors Operators */ +/* ********************** Isolate gpencil_ color **************************** */ -/* ******************* Add New Palette ************************ */ - -/* add new palette - wrapper around API */ -static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - if (*gpd_ptr == NULL) - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* verify palette */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); - if (palette == NULL) - palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); - - /* add new palette color now */ - BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Palette Color"; - ot->idname = "GPENCIL_OT_palettecolor_add"; - ot->description = "Add new Grease Pencil palette color for the active Grease Pencil data-block"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_palettecolor_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Remove Active Palette color ************************* */ - -static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); + Material *active_ma = give_current_material(ob, ob->actcol); + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); + MaterialGPencilStyle *gp_style; - /* sanity checks */ - if (ELEM(NULL, gpd, palette, color)) - return OPERATOR_CANCELLED; - - /* make the palette color before this the new active color - * - use the one after if this is the first - * - if this is the only color, this naturally becomes NULL - */ - if (color->prev) - BKE_gpencil_palettecolor_setactive(palette, color->prev); - else - BKE_gpencil_palettecolor_setactive(palette, color->next); - - /* delete the strokes */ - BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); - - /* delete the palette color now... */ - BKE_gpencil_palettecolor_delete(palette, color); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove palette color"; - ot->idname = "GPENCIL_OT_palettecolor_remove"; - ot->description = "Remove active Grease Pencil palette color"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_palettecolor_remove_exec; - ot->poll = gp_active_palettecolor_poll; -} - -/* ********************** Isolate palette color **************************** */ - -static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); - bGPDpalettecolor *palcolor; - - int flags = PC_COLOR_LOCKED; + int flags = GP_STYLE_COLOR_LOCKED; bool isolate = false; if (RNA_boolean_get(op->ptr, "affect_visibility")) - flags |= PC_COLOR_HIDE; + flags |= GP_STYLE_COLOR_HIDE; if (ELEM(NULL, gpd, active_color)) { BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); @@ -1699,15 +2095,20 @@ static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) } /* Test whether to isolate or clear all flags */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; /* Skip if this is the active one */ - if (palcolor == active_color) + if (ma == active_ma) continue; /* If the flags aren't set, that means that the color is - * not alone, so we have some colors to isolate still - */ - if ((palcolor->flag & flags) == 0) { + * not alone, so we have some colors to isolate still + */ + gp_style = ma->gp_style; + if ((gp_style->flag & flags) == 0) { isolate = true; break; } @@ -1716,72 +2117,79 @@ static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) /* Set/Clear flags as appropriate */ if (isolate) { /* Set flags on all "other" colors */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - if (palcolor == active_color) + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + if (gp_style == active_color) continue; else - palcolor->flag |= flags; + gp_style->flag |= flags; } } else { /* Clear flags - Restore everything else */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~flags; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~flags; } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) +void GPENCIL_OT_color_isolate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Isolate Palette Color"; - ot->idname = "GPENCIL_OT_palettecolor_isolate"; + ot->name = "Isolate Color"; + ot->idname = "GPENCIL_OT_color_isolate"; ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; /* callbacks */ - ot->exec = gp_isolate_palettecolor_exec; - ot->poll = gp_active_palettecolor_poll; + ot->exec = gpencil_color_isolate_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " - "the editability, also affect the visibility"); + "the editability, also affect the visibility"); } -/* *********************** Hide Palette colors ******************************** */ +/* *********************** Hide colors ******************************** */ -static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) +static int gpencil_color_hide_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); bool unselected = RNA_boolean_get(op->ptr, "unselected"); - /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; if (unselected) { - bGPDpalettecolor *color; - /* hide unselected */ - for (color = palette->colors.first; color; color = color->next) { - if (color != palcolor) { - color->flag |= PC_COLOR_HIDE; + MaterialGPencilStyle *color = NULL; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + color = ma->gp_style; + if (active_color != color) { + color->flag |= GP_STYLE_COLOR_HIDE; } } } else { /* hide selected/active */ - palcolor->flag |= PC_COLOR_HIDE; + active_color->flag |= GP_STYLE_COLOR_HIDE; } /* notifiers */ @@ -1790,18 +2198,18 @@ static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) +void GPENCIL_OT_color_hide(wmOperatorType *ot) { /* identifiers */ ot->name = "Hide Color(s)"; - ot->idname = "GPENCIL_OT_palettecolor_hide"; + ot->idname = "GPENCIL_OT_color_hide"; ot->description = "Hide selected/unselected Grease Pencil colors"; /* callbacks */ - ot->exec = gp_palettecolor_hide_exec; - ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ + ot->exec = gpencil_color_hide_exec; + ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ - /* flags */ + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ @@ -1810,25 +2218,23 @@ void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) /* ********************** Show All Colors ***************************** */ -/* poll callback for showing colors */ -static bool gp_palettecolor_reveal_poll(bContext *C) +static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) { - return ED_gpencil_data_get_active(C) != NULL; -} + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); -static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; - - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all colors visible */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_HIDE; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; } /* notifiers */ @@ -1837,36 +2243,41 @@ static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) +void GPENCIL_OT_color_reveal(wmOperatorType *ot) { /* identifiers */ ot->name = "Show All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_reveal"; - ot->description = "Unhide all hidden Grease Pencil palette colors"; + ot->idname = "GPENCIL_OT_color_reveal"; + ot->description = "Unhide all hidden Grease Pencil gpencil_ colors"; /* callbacks */ - ot->exec = gp_palettecolor_reveal_exec; - ot->poll = gp_palettecolor_reveal_poll; + ot->exec = gpencil_color_reveal_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Lock/Unlock All Palette colors ************************ */ +/* ***************** Lock/Unlock All colors ************************ */ -static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all layers non-editable */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag |= PC_COLOR_LOCKED; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; } /* notifiers */ @@ -1875,16 +2286,16 @@ static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) +void GPENCIL_OT_color_lock_all(wmOperatorType *ot) { /* identifiers */ ot->name = "Lock All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_lock_all"; + ot->idname = "GPENCIL_OT_color_lock_all"; ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; /* callbacks */ - ot->exec = gp_palettecolor_lock_all_exec; - ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + ot->exec = gpencil_color_lock_all_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1892,19 +2303,23 @@ void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) /* -------------------------- */ -static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all layers editable again*/ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_LOCKED; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; } /* notifiers */ @@ -1913,94 +2328,32 @@ static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) +void GPENCIL_OT_color_unlock_all(wmOperatorType *ot) { /* identifiers */ ot->name = "Unlock All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; + ot->idname = "GPENCIL_OT_color_unlock_all"; ot->description = "Unlock all Grease Pencil colors so that they can be edited"; /* callbacks */ - ot->exec = gp_palettecolor_unlock_all_exec; - ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + ot->exec = gpencil_color_unlock_all_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* Move Color Up/Down ************************** */ -enum { - GP_COLOR_MOVE_UP = -1, - GP_COLOR_MOVE_DOWN = 1 -}; +/* ***************** Select all strokes using color ************************ */ -static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) +static int gpencil_color_select_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - - int direction = RNA_enum_get(op->ptr, "direction"); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol); /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) - return OPERATOR_CANCELLED; - - /* up or down? */ - if (direction == GP_COLOR_MOVE_UP) { - /* up */ - BLI_remlink(&palette->colors, palcolor); - BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); - } - else if (direction == GP_COLOR_MOVE_DOWN) { - /* down */ - BLI_remlink(&palette->colors, palcolor); - BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); - } - else { - BLI_assert(0); - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) -{ - static const EnumPropertyItem slot_move[] = { - {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, - {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Move Palette color"; - ot->idname = "GPENCIL_OT_palettecolor_move"; - ot->description = "Move the active Grease Pencil palette color up/down in the list"; - - /* api callbacks */ - ot->exec = gp_palettecolor_move_exec; - ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); -} - -/* ***************** Select all strokes using Palette color ************************ */ - -static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - - /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) + if (ELEM(NULL, gpd, gp_style)) return OPERATOR_CANCELLED; /* read all strokes and select*/ @@ -2013,11 +2366,11 @@ static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) continue; /* select */ - if (strcmp(palcolor->info, gps->colorname) == 0) { + if (ob->actcol == gps->mat_nr) { bGPDspoint *pt; int i; @@ -2035,56 +2388,16 @@ static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) +void GPENCIL_OT_color_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Color"; - ot->idname = "GPENCIL_OT_palettecolor_select"; + ot->idname = "GPENCIL_OT_color_select"; ot->description = "Select all Grease Pencil strokes using current color"; /* callbacks */ - ot->exec = gp_palettecolor_select_exec; - ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ***************** Copy Palette color ************************ */ - -static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - bGPDpalettecolor *newcolor; - - /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) - return OPERATOR_CANCELLED; - - /* create a new color and duplicate data */ - newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true); - copy_v4_v4(newcolor->color, palcolor->color); - copy_v4_v4(newcolor->fill, palcolor->fill); - newcolor->flag = palcolor->flag; - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Color"; - ot->idname = "GPENCIL_OT_palettecolor_copy"; - ot->description = "Copy current Grease Pencil palette color"; - - /* callbacks */ - ot->exec = gp_palettecolor_copy_exec; - ot->poll = gp_active_palettecolor_poll; + ot->exec = gpencil_color_select_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index ec67b2da161..4264645b52e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -47,6 +47,7 @@ #include "BLT_translation.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -54,12 +55,19 @@ #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" +#include "BKE_main.h" #include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_brush.h" #include "BKE_gpencil.h" +#include "BKE_paint.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "UI_interface.h" #include "UI_resources.h" @@ -80,30 +88,69 @@ #include "ED_space_api.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" /* ************************************************ */ /* Stroke Edit Mode Management */ - static bool gpencil_editmode_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_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op) { + const int back = RNA_boolean_get(op->ptr, "back"); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ bGPdata *gpd = ED_gpencil_data_get_active(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) + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active GP data"); return OPERATOR_CANCELLED; + } /* Just toggle editmode flag... */ gpd->flag ^= GP_DATA_STROKE_EDITMODE; /* recalculate parent matrix */ if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - ED_gpencil_reset_layers_parent(gpd); + ED_gpencil_reset_layers_parent(depsgraph, ob, gpd); } + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + mode = OB_MODE_GPENCIL_EDIT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); @@ -114,6 +161,8 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Strokes Edit Mode Toggle"; ot->idname = "GPENCIL_OT_editmode_toggle"; @@ -125,6 +174,259 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) /* 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 Paint Mode Management */ + +static bool gpencil_paintmode_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_paintmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + 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_PAINTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + mode = OB_MODE_GPENCIL_PAINT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* be sure we have brushes */ + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + BKE_brush_gpencil_presets(C); + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Paint Mode Toggle"; + ot->idname = "GPENCIL_OT_paintmode_toggle"; + ot->description = "Enter/Exit paint mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_paintmode_toggle_exec; + ot->poll = gpencil_paintmode_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 Sculpt Mode Management */ + +static bool gpencil_sculptmode_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_sculptmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + bGPdata *gpd = ED_gpencil_data_get_active(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 sculptmode flag... */ + gpd->flag ^= GP_DATA_STROKE_SCULPTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) { + mode = OB_MODE_GPENCIL_SCULPT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Sculpt Mode Toggle"; + ot->idname = "GPENCIL_OT_sculptmode_toggle"; + ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_sculptmode_toggle_exec; + ot->poll = gpencil_sculptmode_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 Weight Paint Mode Management */ + +static bool gpencil_weightmode_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_weightmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + bGPdata *gpd = ED_gpencil_data_get_active(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 weightmode flag... */ + gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) { + mode = OB_MODE_GPENCIL_WEIGHT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Weight Mode Toggle"; + ot->idname = "GPENCIL_OT_weightmode_toggle"; + ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_weightmode_toggle_exec; + ot->poll = gpencil_weightmode_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); } /* ************************************************ */ @@ -137,21 +439,30 @@ static bool gp_stroke_edit_poll(bContext *C) return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } +/* poll callback to verify edit mode in 3D view only */ +static bool gp_strokes_edit3d_poll(bContext *C) +{ + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); +} + /* ************ Stroke Hide selection Toggle ************** */ static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { - ToolSettings *ts = CTX_data_tool_settings(C); - - if (ts == NULL) + View3D *v3d = CTX_wm_view3d(C); + if (v3d == NULL) return OPERATOR_CANCELLED; /* Just toggle alpha... */ - if (ts->gp_sculpt.alpha > 0.0f) { - ts->gp_sculpt.alpha = 0.0f; + if (v3d->vertex_opacity > 0.0f) { + v3d->vertex_opacity = 0.0f; } else { - ts->gp_sculpt.alpha = 1.0f; + v3d->vertex_opacity = 1.0f; } WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); @@ -176,6 +487,44 @@ void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } +/* toggle multi edit strokes support */ +static int gpencil_multiedit_toggle_exec(bContext *C, wmOperator *op) +{ + View3D *v3d = CTX_wm_view3d(C); + const bool lines = RNA_boolean_get(op->ptr, "lines"); + + /* Just toggle value */ + if (lines == 0) { + v3d->flag3 ^= V3D_GP_SHOW_EDIT_LINES; + } + else { + v3d->flag3 ^= V3D_GP_SHOW_MULTIEDIT_LINES; + } + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_multiedit_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Lines Toggle"; + ot->idname = "GPENCIL_OT_multiedit_toggle"; + ot->description = "Enable/disable edit lines support"; + + /* callbacks */ + ot->exec = gpencil_multiedit_toggle_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_boolean(ot->srna, "toggle_visibility", 0, "Toggle Visibility Only", "Toggle visibility of edit lines only"); +} + /* ************** Duplicate Selected Strokes **************** */ /* Make copies of selected point segments in a selected stroke */ @@ -220,7 +569,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, co /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, layername, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -232,6 +581,20 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, co memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); gpsd->totpoints = len; + if (gps->dvert != NULL) { + gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy"); + memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len); + + /* Copy weights */ + int e = start_idx; + for (int j = 0; j < gpsd->totpoints; j++) { + MDeformVert *dvert_dst = &gps->dvert[e]; + MDeformVert *dvert_src = &gps->dvert[j]; + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + e++; + } + } + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(new_strokes, gpsd); @@ -252,6 +615,11 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* for each visible (and editable) layer's selected strokes, * copy the strokes into a temporary buffer, then append * once all done @@ -279,8 +647,12 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); + 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_CACHES; @@ -309,6 +681,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -342,7 +715,7 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot) /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ ListBase gp_strokes_copypastebuf = {NULL, NULL}; -/* Hash for hanging on to all the palette colors used by strokes in the buffer +/* Hash for hanging on to all the colors used by strokes in the buffer * * This is needed to prevent dangling and unsafe pointers when pasting across datablocks, * or after a color used by a stroke in the buffer gets deleted (via user action or undo). @@ -354,11 +727,11 @@ void ED_gpencil_strokes_copybuf_free(void) { bGPDstroke *gps, *gpsn; - /* Free the palettes buffer - * NOTE: This is done before the strokes so that the name ptrs (keys) are still safe + /* Free the colors buffer + * NOTE: This is done before the strokes so that the ptrs are still safe */ if (gp_strokes_copypastebuf_colors) { - BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); + BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, NULL); gp_strokes_copypastebuf_colors = NULL; } @@ -366,8 +739,15 @@ void ED_gpencil_strokes_copybuf_free(void) for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { gpsn = gps->next; - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + MEM_SAFE_FREE(gps->triangles); BLI_freelinkN(&gp_strokes_copypastebuf, gps); } @@ -378,38 +758,26 @@ void ED_gpencil_strokes_copybuf_free(void) /* Ensure that destination datablock has all the colours the pasted strokes need * Helper function for copy-pasting strokes */ -GHash *gp_copybuf_validate_colormap(bGPdata *gpd) +GHash *gp_copybuf_validate_colormap(bContext *C) { + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); GHash *new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); GHashIterator gh_iter; - /* If there's no active palette yet (i.e. new datablock), add one */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); - } - - /* For each color, figure out what to map to... */ + /* For each color, check if exist and add if not */ GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { - bGPDpalettecolor *palcolor; - char *name = BLI_ghashIterator_getKey(&gh_iter); - /* Look for existing color to map to */ - /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); - if (palcolor == NULL) { - /* Doesn't Exist - Create new matching color for this palette */ - /* XXX: This still doesn't fix the pasting across file boundaries problem... */ - bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); + int *key = BLI_ghashIterator_getKey(&gh_iter); + Material *ma = BLI_ghashIterator_getValue(&gh_iter); - palcolor = MEM_dupallocN(src_color); - BLI_addtail(&palette->colors, palcolor); - - BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + if (BKE_object_material_slot_find_index(ob, ma) == 0) { + BKE_object_material_slot_add(bmain, ob); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); } /* Store this mapping (for use later when pasting) */ - BLI_ghash_insert(new_colors, name, palcolor); + BLI_ghash_insert(new_colors, key, ma); } return new_colors; @@ -420,6 +788,7 @@ GHash *gp_copybuf_validate_colormap(bGPdata *gpd) static int gp_strokes_copy_exec(bContext *C, wmOperator *op) { + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); if (gpd == NULL) { @@ -427,6 +796,11 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* clear the buffer first */ ED_gpencil_strokes_copybuf_free(); @@ -455,8 +829,12 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */ gpsd->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsd->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsd); + } /* triangles cache - will be recalculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; @@ -476,17 +854,16 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* Build up hash of colors used in these strokes, making copies of these to protect against dangling pointers */ + /* Build up hash of material colors used in these strokes */ if (gp_strokes_copypastebuf.first) { gp_strokes_copypastebuf_colors = BLI_ghash_str_new("GPencil CopyBuf Colors"); - + Material *ma = NULL; for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { - if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, gps->colorname) == false) { - bGPDpalettecolor *color = MEM_dupallocN(gps->palcolor); - - BLI_ghash_insert(gp_strokes_copypastebuf_colors, gps->colorname, color); - gps->palcolor = color; + ma = give_current_material(ob, gps->mat_nr + 1); + if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, &gps->mat_nr) == false) + { + BLI_ghash_insert(gp_strokes_copypastebuf_colors, &gps->mat_nr, ma); } } } @@ -534,9 +911,11 @@ typedef enum eGP_PasteMode { static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + 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 */ + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDframe *gpf; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); @@ -547,6 +926,10 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); return OPERATOR_CANCELLED; } + else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) { BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again"); return OPERATOR_CANCELLED; @@ -599,14 +982,14 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* Ensure that all the necessary colors exist */ - new_colors = gp_copybuf_validate_colormap(gpd); + 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) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ if (type != GP_COPY_MERGE) { - gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); + 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); @@ -618,28 +1001,33 @@ 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, true); + gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, true); if (gpf) { /* Create new stroke */ bGPDstroke *new_stroke = MEM_dupallocN(gps); - new_stroke->tmp_layerinfo[0] = '\0'; + new_stroke->runtime.tmp_layerinfo[0] = '\0'; 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_CACHES; new_stroke->triangles = NULL; new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); - /* Fix color references */ - BLI_assert(new_stroke->colorname[0] != '\0'); - new_stroke->palcolor = BLI_ghash_lookup(new_colors, new_stroke->colorname); + /* Remap material */ + Material *ma = BLI_ghash_lookup(new_colors, &new_stroke->mat_nr); + if ((ma) && (BKE_object_material_slot_find_index(ob, ma) > 0)) { + gps->mat_nr = BKE_object_material_slot_find_index(ob, ma) - 1; + CLAMP_MIN(gps->mat_nr, 0); + } + else { + gps->mat_nr = 0; /* only if the color is not found */ + } - BLI_assert(new_stroke->palcolor != NULL); - BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); - - /*new_stroke->flag |= GP_STROKE_RECALC_COLOR; */ } } } @@ -648,6 +1036,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) BLI_ghash_free(new_colors, NULL, NULL); /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -697,10 +1086,17 @@ static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *U static int gp_move_to_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = CTX_data_gpencil_data(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_enum_get(op->ptr, "layer"); + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ @@ -748,14 +1144,14 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Paste them all in one go */ if (strokes.first) { - Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, true); BLI_movelisttolist(&gpf->strokes, &strokes); BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -804,8 +1200,10 @@ static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); @@ -826,7 +1224,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_find_frame(gpl, cfra_eval); if (gpf) { /* Shunt all frames after (and including) the existing one later by 1-frame */ for (; gpf; gpf = gpf->next) { @@ -835,11 +1233,12 @@ 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_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); } CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -876,10 +1275,13 @@ static bool gp_actframe_delete_poll(bContext *C) /* delete active frame - wrapper around API calls */ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -895,6 +1297,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_delframe(gpl, gpf); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -928,13 +1331,16 @@ static bool gp_actframe_delete_all_poll(bContext *C) static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bool success = false; 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, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); if (gpf == NULL) continue; @@ -949,6 +1355,7 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) /* updates */ if (success) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -983,43 +1390,69 @@ typedef enum eGP_DeleteMode { GP_DELETEOP_FRAME = 2, } eGP_DeleteMode; +typedef enum eGP_DissolveMode { + /* dissolve all selected points */ + GP_DISSOLVE_POINTS = 0, + /* dissolve between selected points */ + GP_DISSOLVE_BETWEEN = 1, + /* dissolve unselected points */ + GP_DISSOLVE_UNSELECT = 2, +} eGP_DissolveMode; + /* ----------------------------------- */ /* Delete selected strokes */ static int gp_delete_selected_strokes(bContext *C) { bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + if (gpf == NULL) + continue; - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; - /* free stroke if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* free stroke memory arrays, then stroke itself */ - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; - changed = true; + /* free stroke if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1031,88 +1464,238 @@ static int gp_delete_selected_strokes(bContext *C) /* ----------------------------------- */ /* Delete selected points but keep the stroke */ -static int gp_dissolve_selected_points(bContext *C) +static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) { + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bool changed = false; + int first = 0; + int last = 0; CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - /* simply delete points from selected strokes - * NOTE: we may still have to remove the stroke if it ends up having no points! - */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + bGPDstroke *gps, *gpsn; - /* 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(gpl, gps) == false) - continue; + if (gpf == NULL) + continue; - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; + /* simply delete points from selected strokes + * NOTE: we may still have to remove the stroke if it ends up having no points! + */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; - int tot = gps->totpoints; /* number of points in new buffer */ + /* 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; - /* First Pass: Count how many points are selected (i.e. how many to remove) */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* selected point - one of the points to remove */ - tot--; - } - } + /* the stroke must have at least one point selected for any operator */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + MDeformVert *dvert = NULL; + int i; - /* if no points are left, we simply delete the entire stroke */ - if (tot <= 0) { - /* remove the entire stroke */ - MEM_freeN(gps->points); - if (gps->triangles) { - MEM_freeN(gps->triangles); - } - BLI_freelinkN(&gpf->strokes, gps); - } - else { - /* just copy all unselected into a smaller buffer */ - bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); - bGPDspoint *npt = new_points; + int tot = gps->totpoints; /* number of points in new buffer */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - *npt = *pt; - npt++; + /* first pass: count points to remove */ + switch (mode) { + case GP_DISSOLVE_POINTS: + /* Count how many points are selected (i.e. how many to remove) */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* selected point - one of the points to remove */ + tot--; + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* need to find first and last point selected */ + first = -1; + last = 0; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (first < 0) { + first = i; + } + last = i; + } + } + /* count unselected points in the range */ + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + case GP_DISSOLVE_UNSELECT: + /* count number of unselected points */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + default: + return false; + break; } + + /* if no points are left, we simply delete the entire stroke */ + if (tot <= 0) { + /* remove the entire stroke */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) { + MEM_freeN(gps->triangles); + } + BLI_freelinkN(&gpf->strokes, gps); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + else { + /* just copy all points to keep into a smaller buffer */ + bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); + bGPDspoint *npt = new_points; + + MDeformVert *new_dvert = NULL; + MDeformVert *ndvert = NULL; + + if (gps->dvert != NULL) { + new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); + ndvert = new_dvert; + } + + switch (mode) { + case GP_DISSOLVE_POINTS: + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* copy first segment */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < first; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + /* copy segment (selected points) */ + (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL; + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + /* copy last segment */ + (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL; + for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + + break; + case GP_DISSOLVE_UNSELECT: + /* copy any selected point */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + } + + /* free the old buffer */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + /* save the new buffer */ + gps->points = new_points; + gps->dvert = new_dvert; + gps->totpoints = tot; + + /* triangles cache needs to be recalculated */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + + /* deselect the stroke, since none of its selected points will still be selected */ + gps->flag &= ~GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } + + changed = true; } - - /* free the old buffer */ - MEM_freeN(gps->points); - - /* save the new buffer */ - gps->points = new_points; - gps->totpoints = tot; - - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; - - /* deselect the stroke, since none of its selected points will still be selected */ - gps->flag &= ~GP_STROKE_SELECT; } - - changed = true; } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1146,17 +1729,17 @@ typedef struct tGPDeleteIsland { * becomes much less * 2) Each island gets converted to a new stroke */ -void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags) +void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, + int tag_flags, bool select) { tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); bool in_island = false; int num_islands = 0; - bGPDspoint *pt; - int i; /* First Pass: Identify start/end of islands */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { if (pt->flag & tag_flags) { /* selected - stop accumulating to island */ in_island = false; @@ -1198,11 +1781,26 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ new_stroke->totpoints = island->end_idx - island->start_idx + 1; + + /* Copy over the relevant point data */ new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); - - /* Copy over the relevant points */ memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); - + + /* Copy over vertex weight data (if available) */ + if (new_stroke->dvert != NULL) { + /* Copy over the relevant vertex-weight points */ + new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight"); + memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints); + + /* Copy weights */ + int e = island->start_idx; + for (int i = 0; i < new_stroke->totpoints; i++) { + MDeformVert *dvert_dst = &gps->dvert[e]; + MDeformVert *dvert_src = &new_stroke->dvert[i]; + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + e++; + } + } /* Each island corresponds to a new stroke. We must adjust the * timings of these new strokes: @@ -1222,6 +1820,11 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke pts = new_stroke->points; for (j = 0; j < new_stroke->totpoints; j++, pts++) { pts->time -= delta; + /* set flag for select again later */ + if (select == true) { + pts->flag &= ~GP_SPOINT_SELECT; + pts->flag |= GP_SPOINT_TAG; + } } } @@ -1239,53 +1842,70 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke MEM_freeN(islands); /* Delete the old stroke */ - MEM_freeN(gps->points); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } if (gps->triangles) { MEM_freeN(gps->triangles); } BLI_freelinkN(&gpf->strokes, gps); } - /* Split selected strokes into segments, splitting on selected points */ static int gp_delete_selected_points(bContext *C) { + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bool changed = false; CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + if (gpf == NULL) + continue; - /* 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(gpl, gps) == false) - continue; + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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; - if (gps->flag & GP_STROKE_SELECT) { - /* deselect old stroke, since it will be used as template for the new strokes */ - gps->flag &= ~GP_STROKE_SELECT; + if (gps->flag & GP_STROKE_SELECT) { + /* deselect old stroke, since it will be used as template for the new strokes */ + 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); + /* delete unwanted points by splitting stroke into several smaller ones */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); - changed = true; + changed = true; + } + } } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1294,6 +1914,12 @@ static int gp_delete_selected_points(bContext *C) } } +/* simple wrapper to external call */ +int gp_delete_selected_point_wrap(bContext *C) +{ + return gp_delete_selected_points(C); +} + /* ----------------------------------- */ static int gp_delete_exec(bContext *C, wmOperator *op) @@ -1344,24 +1970,37 @@ void GPENCIL_OT_delete(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data"); } -static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) +static int gp_dissolve_exec(bContext *C, wmOperator *op) { - return gp_dissolve_selected_points(C); + eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); + + return gp_dissolve_selected_points(C, mode); } void GPENCIL_OT_dissolve(wmOperatorType *ot) { + static EnumPropertyItem prop_gpencil_dissolve_types[] = { + { GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points" }, + { GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points" }, + { GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points" }, + { 0, NULL, 0, NULL, NULL } + }; + /* identifiers */ ot->name = "Dissolve"; ot->idname = "GPENCIL_OT_dissolve"; ot->description = "Delete selected points without splitting strokes"; /* callbacks */ + ot->invoke = WM_menu_invoke; ot->exec = gp_dissolve_exec; ot->poll = gp_stroke_edit_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0, "Type", "Method used for disolving Stroke points"); } /* ****************** Snapping - Strokes <-> Cursor ************************ */ @@ -1384,6 +2023,8 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); RegionView3D *rv3d = CTX_wm_region_data(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); const float gridf = rv3d->gridview; for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { @@ -1392,10 +2033,8 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix object */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1405,37 +2044,32 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; // TODO: if entire stroke is selected, offset entire stroke by same amount? for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { /* only if point is selected */ if (pt->flag & GP_SPOINT_SELECT) { - if (gpl->parent == NULL) { - pt->x = gridf * floorf(0.5f + pt->x / gridf); - pt->y = gridf * floorf(0.5f + pt->y / gridf); - pt->z = gridf * floorf(0.5f + pt->z / gridf); - } - else { - /* apply parent transformations */ - float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); - fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); - fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); - fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); + fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); + fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); + fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); - /* return data */ - copy_v3_v3(&pt->x, fpt); - gp_apply_parent_point(gpl, pt); - } + /* return data */ + copy_v3_v3(&pt->x, fpt); + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); } } } } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&obact->id, DEG_TAG_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1463,6 +2097,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); \ const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d)->location; @@ -1473,10 +2109,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1486,7 +2120,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) @@ -1509,9 +2143,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); - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pt); - } + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); } } } @@ -1520,6 +2152,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&obact->id, DEG_TAG_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1551,6 +2185,8 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); \ float *cursor = ED_view3d_cursor3d_get(scene, v3d)->location; float centroid[3] = {0.0f}; @@ -1566,10 +2202,8 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1579,7 +2213,7 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) @@ -1587,18 +2221,13 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { - if (gpl->parent == NULL) { - add_v3_v3(centroid, &pt->x); - minmax_v3v3_v3(min, max, &pt->x); - } - else { - /* apply parent transformations */ - float fpt[3]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); add_v3_v3(centroid, fpt); - minmax_v3v3_v3(min, max, fpt); - } + minmax_v3v3_v3(min, max, fpt); + count++; } } @@ -1616,7 +2245,9 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) } - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + return OPERATOR_FINISHED; } @@ -1650,13 +2281,20 @@ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { /* Apply thickness */ - gps->thickness = gps->thickness + gpl->thickness; + if ((gps->thickness == 0) && (gpl->line_change == 0)) { + gps->thickness = gpl->thickness; + } + else { + gps->thickness = gps->thickness + gpl->line_change; + } } } /* clear value */ gpl->thickness = 0.0f; + gpl->line_change = 0; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1685,6 +2323,8 @@ enum { static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + const int type = RNA_enum_get(op->ptr, "type"); /* sanity checks */ @@ -1698,13 +2338,13 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) continue; for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - bGPDpalettecolor *palcolor = gps->palcolor; + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); /* skip strokes that are not selected or invalid for current view */ if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) continue; /* skip hidden or locked colors */ - if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) + if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) continue; switch (type) { @@ -1729,6 +2369,7 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1809,15 +2450,24 @@ static void gpencil_flip_stroke(bGPDstroke *gps) } /* Helper: copy point between strokes */ -static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], +static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, int idx, float delta[3], float pressure, float strength, float deltatime) { bGPDspoint *newpoint; + MDeformVert *dvert, *newdvert; 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++; - newpoint = &gps->points[gps->totpoints - 1]; + + if (gps->dvert != NULL) { + dvert = &gps->dvert[idx]; + newdvert = &gps->dvert[gps->totpoints - 1]; + } + newpoint->x = point->x * delta[0]; newpoint->y = point->y * delta[1]; newpoint->z = point->z * delta[2]; @@ -1825,6 +2475,9 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float newpoint->pressure = pressure; newpoint->strength = strength; newpoint->time = point->time + deltatime; + + newdvert->totweight = dvert->totweight; + newdvert->dw = MEM_dupallocN(dvert->dw); } /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ @@ -1870,18 +2523,18 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, co /* 1st: add one tail point to start invisible area */ point = gps_a->points[gps_a->totpoints - 1]; deltatime = point.time; - gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f); + gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, delta, 0.0f, 0.0f, 0.0f); /* 2nd: add one head point to finish invisible area */ point = gps_b->points[0]; - gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime); + gpencil_stroke_copy_point(gps_a, &point, 0, delta, 0.0f, 0.0f, deltatime); } /* 3rd: add all points */ for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { /* check if still room in buffer */ if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { - gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime); + gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime); } } } @@ -1891,8 +2544,7 @@ 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; - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); bGPDframe *gpf_a = NULL; bGPDstroke *stroke_a = NULL; @@ -1928,7 +2580,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; } @@ -1948,15 +2600,17 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) 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_CACHES; /* if new, set current color */ if (type == GP_STROKE_JOINCOPY) { - new_stroke->palcolor = palcolor; - BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname)); - new_stroke->flag |= GP_STROKE_RECALC_COLOR; + new_stroke->mat_nr = stroke_a->mat_nr; } } @@ -1995,6 +2649,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2030,6 +2685,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot) static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); /* sanity checks */ if (ELEM(NULL, gpd)) @@ -2049,7 +2705,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; } @@ -2061,6 +2717,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2084,33 +2741,42 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /* ***************** Reproject Strokes ********************** */ typedef enum eGP_ReprojectModes { + /* Axis (equal to lock axis) */ + GP_REPROJECT_AXIS = 0, /* On same plane, parallel to viewplane */ - GP_REPROJECT_PLANAR = 0, + GP_REPROJECT_PLANAR, /* Reprojected on to the scene geometry */ GP_REPROJECT_SURFACE, } eGP_ReprojectModes; -static bool gp_strokes_reproject_poll(bContext *C) -{ - /* 2 Requirements: - * - 1) Editable GP data - * - 2) 3D View only (2D editors don't have projection issues) - */ - return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); -} - static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + View3D *v3d = sa->spacedata.first; + GP_SpaceConversion gsc = {NULL}; - eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); + eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); + + int lock_axis = ts->gp_sculpt.lock_axis; + float origin[3]; + + if ((mode == GP_REPROJECT_AXIS) && (lock_axis == GP_LOCKAXIS_NONE)) { + BKE_report(op->reports, RPT_ERROR, "To reproject by axis, a lock axis must be set before"); + return OPERATOR_CANCELLED; + } /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); /* init autodist for geometry projection */ if (mode == GP_REPROJECT_SURFACE) { - struct Depsgraph *depsgraph = CTX_data_depsgraph(C); view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0); } @@ -2127,9 +2793,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ /* TODO: add this bit to the iteration macro? */ - if (gpl->parent) { - invert_m4_m4(inverse_diff_mat, diff_mat); - } + invert_m4_m4(inverse_diff_mat, diff_mat); /* Adjust each point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { @@ -2140,19 +2804,28 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) * coordinates, resulting in lost precision, which in turn causes stairstepping * artifacts in the final points. */ - if (gpl->parent == NULL) { - gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); - } - else { - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); - } + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + /* Project stroke in the axis locked */ + if (mode == GP_REPROJECT_AXIS) { + if (lock_axis > GP_LOCKAXIS_NONE) { + ED_gp_get_drawing_reference(v3d, scene, ob, gpl, + ts->gpencil_v3d_align, origin); + ED_gp_project_point_to_plane(ob, rv3d, origin, + lock_axis - 1, &pt2); + + copy_v3_v3(&pt->x, &pt2.x); + + /* apply parent again */ + gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); + } + } /* Project screenspace back to 3D space (from current perspective) * so that all points have been treated the same way */ - if (mode == GP_REPROJECT_PLANAR) { + else if (mode == GP_REPROJECT_PLANAR) { /* Planar - All on same plane parallel to the viewplane */ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); } @@ -2175,7 +2848,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) } /* Unapply parent corrections */ - if (gpl->parent) { + if (mode != GP_REPROJECT_AXIS) { mul_m4_v3(inverse_diff_mat, &pt->x); } } @@ -2183,6 +2856,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) } GP_EDITABLE_STROKES_END; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -2190,6 +2864,9 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) void GPENCIL_OT_reproject(wmOperatorType *ot) { static const EnumPropertyItem reproject_type[] = { + { GP_REPROJECT_AXIS, "AXIS", 0, "Axis", + "Reproject the strokes using the current lock axis configuration. This is the same projection using while" + "drawing new strokes" }, {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " "using 'Cursor' Stroke Placement"}, @@ -2208,7 +2885,7 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = gp_strokes_reproject_exec; - ot->poll = gp_strokes_reproject_poll; + ot->poll = gp_strokes_edit3d_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2229,7 +2906,7 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps) if (pt->flag & GP_SPOINT_SELECT) { if (i + 1 < gps->totpoints) { if (gps->points[i + 1].flag & GP_SPOINT_SELECT) { - ++totnewpoints; + totnewpoints++; } } } @@ -2237,6 +2914,7 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps) return totnewpoints; } + static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -2267,6 +2945,9 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) /* resize the points arrys */ gps->totpoints += totnewpoints; gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } gps->flag |= GP_STROKE_RECALC_CACHES; /* loop and interpolate */ @@ -2275,19 +2956,27 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) bGPDspoint *pt = &temp_points[i]; bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + /* copy current point */ copy_v3_v3(&pt_final->x, &pt->x); pt_final->pressure = pt->pressure; pt_final->strength = pt->strength; pt_final->time = pt->time; pt_final->flag = pt->flag; - ++i2; + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + i2++; /* if next point is selected add a half way point */ if (pt->flag & GP_SPOINT_SELECT) { if (i + 1 < oldtotpoints) { if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { pt_final = &gps->points[i2]; + if (gps->dvert != NULL) { + dvert_final = &gps->dvert[i2]; + } /* Interpolate all values */ bGPDspoint *next = &temp_points[i + 1]; interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); @@ -2296,7 +2985,11 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->flag |= GP_SPOINT_SELECT; - ++i2; + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + + i2++; } } } @@ -2309,6 +3002,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2328,7 +3022,7 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) ot->poll = gp_active_layer_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); @@ -2336,3 +3030,414 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + +/* ** simplify stroke *** */ +static int gp_stroke_simplify_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + float factor = RNA_float_get(op->ptr, "factor"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + /* simplify stroke using Ramer-Douglas-Peucker algorithm */ + BKE_gpencil_simplify_stroke(gps, factor); + } + } + GP_EDITABLE_STROKES_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_simplify(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Simplify Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify"; + ot->description = "Simplify selected stroked reducing number of points"; + + /* api callbacks */ + ot->exec = gp_stroke_simplify_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ** simplify stroke using fixed algorith *** */ +static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + int steps = RNA_int_get(op->ptr, "step"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < steps; i++) { + BKE_gpencil_simplify_fixed(gps); + } + } + } + GP_EDITABLE_STROKES_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Simplify Fixed Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify_fixed"; + ot->description = "Simplify selected stroked reducing number of points using fixed algorithm"; + + /* api callbacks */ + ot->exec = gp_stroke_simplify_fixed_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10); + + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + +} + +/* ***************** Separate Strokes ********************** */ +typedef enum eGP_SeparateModes { + /* Points */ + GP_SEPARATE_POINT = 0, + /* Selected Strokes */ + GP_SEPARATE_STROKE, + /* Current Layer */ + GP_SEPARATE_LAYER, +} eGP_SeparateModes; + +static int gp_stroke_separate_exec(bContext *C, wmOperator *op) +{ + Base *base_new; + 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); + bGPdata *gpd_src = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + Object *ob_dst = NULL; + bGPdata *gpd_dst = NULL; + bGPDlayer *gpl_dst = NULL; + bGPDframe *gpf_dst = NULL; + bGPDspoint *pt; + Material *ma = NULL; + int i, idx; + + eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); + + /* sanity checks */ + if (ELEM(NULL, gpd_src)) { + return OPERATOR_CANCELLED; + } + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); + + /* create a new object */ + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0); + ob_dst = base_new->object; + + /* create new grease pencil datablock */ + // XXX: check usercounts + gpd_dst = BKE_gpencil_data_addnew(bmain, "GPencil"); + ob_dst->data = (bGPdata *)gpd_dst; + + int totslots = ob_dst->totcol; + int totadd = 0; + + /* loop old datablock and separate parts */ + if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + gpl_dst = NULL; + 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))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + gpf_dst = NULL; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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; + } + /* separate selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* add layer if not created before */ + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + } + + /* add frame if not created before */ + if (gpf_dst == NULL) { + gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); + } + + /* add duplicate materials */ + ma = give_current_material(ob, gps->mat_nr + 1); + idx = BKE_object_material_slot_find_index(ob_dst, ma); + if (idx == 0) { + + totadd++; + ob_dst->actcol = totadd; + ob_dst->totcol = totadd; + + if (totadd > totslots) { + BKE_object_material_slot_add(bmain, ob_dst); + } + + assign_material(bmain, ob_dst, ma, ob_dst->totcol, BKE_MAT_ASSIGN_EXISTING); + idx = totadd; + } + + /* selected points mode */ + if (mode == GP_SEPARATE_POINT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* reasign material */ + gps_dst->mat_nr = idx - 1; + + /* link to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + + /* Invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); + } + /* selected strokes mode */ + else if (mode == GP_SEPARATE_STROKE) { + /* deselect old stroke */ + gps->flag &= ~GP_STROKE_SELECT; + /* unlink from source frame */ + BLI_remlink(&gpf->strokes, gps); + gps->prev = gps->next = NULL; + /* relink to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps); + /* reasign material */ + gps->mat_nr = idx - 1; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + } + else if (mode == GP_SEPARATE_LAYER) { + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if (gpl) { + /* try to set a new active layer in source datablock */ + if (gpl->prev) { + BKE_gpencil_layer_setactive(gpd_src, gpl->prev); + } + else if (gpl->next) { + BKE_gpencil_layer_setactive(gpd_src, gpl->next); + } + /* unlink from source datablock */ + BLI_remlink(&gpd_src->layers, gpl); + gpl->prev = gpl->next = NULL; + /* relink to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl); + } + } + DEG_id_tag_update(&gpd_src->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&gpd_dst->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_separate(wmOperatorType *ot) +{ + static const EnumPropertyItem separate_type[] = { + {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points" }, + {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"}, + {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Separate Strokes"; + ot->idname = "GPENCIL_OT_stroke_separate"; + ot->description = "Separate the selected strokes or layer in a new grease pencil object"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_stroke_separate_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", ""); +} + +/* ***************** Split Strokes ********************** */ +static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDspoint *pt; + int i; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + /* loop strokes and split parts */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + 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))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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; + } + /* split selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* link to same frame */ + BLI_addtail(&gpf->strokes, gps_dst); + + /* invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); + } + } + /* select again tagged points */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *ptn = gps->points; + for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) { + if (ptn->flag & GP_SPOINT_TAG) { + ptn->flag |= GP_SPOINT_SELECT; + ptn->flag &= ~GP_SPOINT_TAG; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_split(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split Strokes"; + ot->idname = "GPENCIL_OT_stroke_split"; + ot->description = "Split selected points as new stroke on same frame"; + + /* callbacks */ + ot->exec = gp_stroke_split_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c new file mode 100644 index 00000000000..b768ac2c44f --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -0,0 +1,1246 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation, Joshua Leung + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/gpencil_fill.c + * \ingroup edgpencil + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_stack.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_image.h" +#include "BKE_gpencil.h" +#include "BKE_material.h" +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_paint.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "GPU_immediate.h" +#include "GPU_draw.h" +#include "GPU_matrix.h" +#include "GPU_framebuffer.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +#define LEAK_HORZ 0 +#define LEAK_VERT 1 + + +/* Temporary fill operation data (op->customdata) */ +typedef struct tGPDfill { + struct Main *bmain; + struct Depsgraph *depsgraph; + struct wmWindow *win; /* window where painting originated */ + struct Scene *scene; /* current scene from context */ + struct Object *ob; /* current active gp object */ + struct ScrArea *sa; /* area where painting originated */ + struct RegionView3D *rv3d; /* region where painting originated */ + struct View3D *v3d; /* view3 where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + + short flag; /* flags */ + short oldkey; /* avoid too fast events */ + bool on_back; /* send to back stroke */ + + int center[2]; /* mouse fill center position */ + int sizex; /* windows width */ + int sizey; /* window height */ + int lock_axis; /* lock to viewport axis */ + + short fill_leak; /* number of pixel to consider the leak is too small (x 2) */ + float fill_threshold; /* factor for transparency */ + int fill_simplylvl; /* number of simplify steps */ + int fill_draw_mode; /* boundary limits drawing mode */ + + short sbuffer_size; /* number of elements currently in cache */ + void *sbuffer; /* temporary points */ + float *depth_arr; /* depth array for reproject */ + + Image *ima; /* temp image */ + BLI_Stack *stack; /* temp points data */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ +} tGPDfill; + + + /* draw a given stroke using same thickness and color for all points */ +static void gp_draw_basic_stroke(tGPDfill *tgpf, bGPDstroke *gps, const float diff_mat[4][4], + bool cyclic, float ink[4], int flag, float thershold) +{ + bGPDspoint *points = gps->points; + + Material *ma = tgpf->mat; + MaterialGPencilStyle *gp_style = ma->gp_style; + + int totpoints = gps->totpoints; + float fpt[3]; + float col[4]; + + copy_v4_v4(col, ink); + + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); + + /* draw stroke curve */ + glLineWidth(1.0f); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + + for (int i = 0; i < totpoints; i++, pt++) { + + if (flag & GP_BRUSH_FILL_HIDE) { + float alpha = gp_style->stroke_rgba[3] * pt->strength; + CLAMP(alpha, 0.0f, 1.0f); + col[3] = alpha <= thershold ? 0.0f : 1.0f; + } + else { + col[3] = 1.0f; + } + /* set point */ + immAttrib4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + immVertex3fv(pos, fpt); + } + + if (cyclic && totpoints > 2) { + /* draw line to first point to complete the cycle */ + immAttrib4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &points->x); + immVertex3fv(pos, fpt); + } + + immEnd(); + immUnbindProgram(); +} + +/* loop all layers */ +static void gp_draw_datablock(tGPDfill *tgpf, float ink[4]) +{ + /* duplicated: etempFlags */ + enum { + GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */ + GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */ + }; + + Object *ob = tgpf->ob; + bGPdata *gpd = tgpf->gpd; + int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + tGPDdraw tgpw; + tgpw.rv3d = tgpf->rv3d; + tgpw.depsgraph = tgpf->depsgraph; + tgpw.ob = ob; + tgpw.gpd = gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpf->ar->winx; + tgpw.winy = tgpf->ar->winy; + tgpw.dflag = 0; + tgpw.disable_fill = 1; + tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS); + + glEnable(GL_BLEND); + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* calculate parent position */ + ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat); + + /* do not draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if ((gps->points == NULL) || (gps->totpoints < 2)) { + continue; + } + /* check if the color is visible */ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) + { + continue; + } + + tgpw.gps = gps; + tgpw.gpl = gpl; + tgpw.gpf = gpf; + tgpw.t_gpf = gpf; + + /* reduce thickness to avoid gaps */ + tgpw.lthick = gpl->line_change - 4; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, ink); + tgpw.onion = true; + tgpw.custonion = true; + + /* normal strokes */ + if ((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) + { + ED_gp_draw_fill(&tgpw); + + } + + /* 3D Lines with basic shapes and invisible lines */ + if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) + { + gp_draw_basic_stroke(tgpf, gps, tgpw.diff_mat, gps->flag & GP_STROKE_CYCLIC, ink, + tgpf->flag, tgpf->fill_threshold); + } + } + } + + glDisable(GL_BLEND); +} + + /* draw strokes in offscreen buffer */ +static void gp_render_offscreen(tGPDfill *tgpf) +{ + bool is_ortho = false; + float winmat[4][4]; + + if (!tgpf->gpd) { + return; + } + + char err_out[256] = "unknown"; + GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, 0, true, false, err_out); + GPU_offscreen_bind(offscreen, true); + uint flag = IB_rect | IB_rectfloat; + ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag); + + rctf viewplane; + float clipsta, clipend; + + is_ortho = ED_view3d_viewplane_get(tgpf->depsgraph, tgpf->v3d, tgpf->rv3d, tgpf->sizex, tgpf->sizey, &viewplane, &clipsta, &clipend, NULL); + if (is_ortho) { + orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clipend, clipend); + } + else { + perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend); + } + + /* set temporary new size */ + int bwinx = tgpf->ar->winx; + int bwiny = tgpf->ar->winy; + rcti brect = tgpf->ar->winrct; + + tgpf->ar->winx = (short) tgpf->sizex; + tgpf->ar->winy = (short) tgpf->sizey; + tgpf->ar->winrct.xmin = 0; + tgpf->ar->winrct.ymin = 0; + tgpf->ar->winrct.xmax = tgpf->sizex; + tgpf->ar->winrct.ymax = tgpf->sizey; + + GPU_matrix_push_projection(); + GPU_matrix_identity_set(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ED_view3d_update_viewmat(tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->ar, + NULL, winmat, NULL); + /* set for opengl */ + GPU_matrix_projection_set(tgpf->rv3d->winmat); + GPU_matrix_set(tgpf->rv3d->viewmat); + + /* draw strokes */ + float ink[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + gp_draw_datablock(tgpf, ink); + + /* restore size */ + tgpf->ar->winx = (short)bwinx; + tgpf->ar->winy = (short)bwiny; + tgpf->ar->winrct = brect; + + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + /* create a image to see result of template */ + if (ibuf->rect_float) { + GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + } + else if (ibuf->rect) { + GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + } + if (ibuf->rect_float && ibuf->rect) { + IMB_rect_from_float(ibuf); + } + + tgpf->ima = BKE_image_add_from_imbuf(tgpf->bmain, ibuf, "GP_fill"); + tgpf->ima->id.tag |= LIB_TAG_DOIT; + + BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); + + /* switch back to window-system-provided framebuffer */ + GPU_offscreen_unbind(offscreen, true); + GPU_offscreen_free(offscreen); +} + +/* return pixel data (rgba) at index */ +static void get_pixel(ImBuf *ibuf, int idx, float r_col[4]) +{ + if (ibuf->rect_float) { + float *frgba = &ibuf->rect_float[idx * 4]; + copy_v4_v4(r_col, frgba); + } + else { + /* XXX: This case probably doesn't happen, as we only write to the float buffer, + * but we get compiler warnings about uninitialised vars otherwise + */ + BLI_assert(!"gpencil_fill.c - get_pixel() non-float case is used!"); + zero_v4(r_col); + } +} + +/* set pixel data (rgba) at index */ +static void set_pixel(ImBuf *ibuf, int idx, const float col[4]) +{ + if (ibuf->rect) { + uint *rrect = &ibuf->rect[idx]; + uchar ccol[4]; + + rgba_float_to_uchar(ccol, col); + *rrect = *((uint *)ccol); + } + + if (ibuf->rect_float) { + float *rrectf = &ibuf->rect_float[idx * 4]; + copy_v4_v4(rrectf, col); + } +} + +/* check if the size of the leak is narrow to determine if the stroke is closed + * this is used for strokes with small gaps between them to get a full fill + * and do not get a full screen fill. + * + * \param ibuf Image pixel data + * \param maxpixel Maximum index + * \param limit Limit of pixels to analize + * \param index Index of current pixel + * \param type 0-Horizontal 1-Vertical + */ +static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type) +{ + float rgba[4]; + int i; + int pt; + bool t_a = false; + bool t_b = false; + + /* Horizontal leak (check vertical pixels) + * X + * X + * xB7 + * X + * X + */ + if (type == LEAK_HORZ) { + /* pixels on top */ + for (i = 1; i <= limit; i++) { + pt = index + (ibuf->x * i); + if (pt <= maxpixel) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + t_a = true; /* edge of image*/ + break; + } + } + /* pixels on bottom */ + for (i = 1; i <= limit; i++) { + pt = index - (ibuf->x * i); + if (pt >= 0) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + t_b = true; /* edge of image*/ + break; + } + } + } + + /* Vertical leak (check horizontal pixels) + * + * XXXxB7XX + * + */ + if (type == LEAK_VERT) { + /* get pixel range of the row */ + int row = index / ibuf->x; + int lowpix = row * ibuf->x; + int higpix = lowpix + ibuf->x - 1; + + /* pixels to right */ + for (i = 0; i < limit; i++) { + pt = index - (limit - i); + if (pt >= lowpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + t_a = true; /* edge of image*/ + break; + } + } + /* pixels to left */ + for (i = 0; i < limit; i++) { + pt = index + (limit - i); + if (pt <= higpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + t_b = true; /* edge of image */ + break; + } + } + } + return (bool)(t_a && t_b); +} + +/* Boundary fill inside strokes + * Fills the space created by a set of strokes using the stroke color as the boundary + * of the shape to fill. + * + * \param tgpf Temporary fill data + */ +static void gpencil_boundaryfill_area(tGPDfill *tgpf) +{ + ImBuf *ibuf; + float rgba[4]; + void *lock; + const float fill_col[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + const int maxpixel = (ibuf->x * ibuf->y) - 1; + + BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); + + /* calculate index of the seed point using the position of the mouse */ + int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0]; + if ((index >= 0) && (index < maxpixel)) { + BLI_stack_push(stack, &index); + } + + /* the fill use a stack to save the pixel list instead of the common recursive + * 4-contact point method. + * The problem with recursive calls is that for big fill areas, we can get max limit + * of recursive calls and STACK_OVERFLOW error. + * + * The 4-contact point analyze the pixels to the left, right, bottom and top + * ----------- + * | X | + * | XoX | + * | X | + * ----------- + */ + while (!BLI_stack_is_empty(stack)) { + int v; + BLI_stack_pop(stack, &v); + + get_pixel(ibuf, v, rgba); + + if (true) { /* Was: 'rgba' */ + /* check if no border(red) or already filled color(green) */ + if ((rgba[0] != 1.0f) && (rgba[1] != 1.0f)) + { + /* fill current pixel */ + set_pixel(ibuf, v, fill_col); + + /* add contact pixels */ + /* pixel left */ + if (v - 1 >= 0) { + index = v - 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel right */ + if (v + 1 < maxpixel) { + index = v + 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel top */ + if (v + tgpf->sizex < maxpixel) { + index = v + tgpf->sizex; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + /* pixel bottom */ + if (v - tgpf->sizex >= 0) { + index = v - tgpf->sizex; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + } + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; + /* free temp stack data */ + BLI_stack_free(stack); +} + +/* clean external border of image to avoid infinite loops */ +static void gpencil_clean_borders(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + const float fill_col[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int idx; + + /* horizontal lines */ + for (idx = 0; idx < ibuf->x; idx++) { + /* bottom line */ + set_pixel(ibuf, idx, fill_col); + /* top line */ + set_pixel(ibuf, idx + (ibuf->x * (ibuf->y - 1)), fill_col); + } + /* vertical lines */ + for (idx = 0; idx < ibuf->y; idx++) { + /* left line */ + set_pixel(ibuf, ibuf->x * idx, fill_col); + /* right line */ + set_pixel(ibuf, ibuf->x * idx + (ibuf->x - 1), fill_col); + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; +} + +/* Get the outline points of a shape using Moore Neighborhood algorithm + * + * This is a Blender customized version of the general algorithm described + * in https://en.wikipedia.org/wiki/Moore_neighborhood + */ +static void gpencil_get_outline_points(tGPDfill *tgpf) +{ + ImBuf *ibuf; + float rgba[4]; + void *lock; + int v[2]; + int boundary_co[2]; + int start_co[2]; + int backtracked_co[2]; + int current_check_co[2]; + int prev_check_co[2]; + int backtracked_offset[1][2] = { { 0,0 } }; + // bool boundary_found = false; + bool start_found = false; + const int NEIGHBOR_COUNT = 8; + + int offset[8][2] = { + { -1, -1 }, + { 0, -1 }, + { 1, -1 }, + { 1, 0 }, + { 1, 1 }, + { 0, 1 }, + { -1, 1 }, + { -1, 0 } + }; + + tgpf->stack = BLI_stack_new(sizeof(int[2]), __func__); + + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int imagesize = ibuf->x * ibuf->y; + + /* find the initial point to start outline analysis */ + for (int idx = imagesize; idx >= 0; idx--) { + get_pixel(ibuf, idx, rgba); + if (rgba[1] == 1.0f) { + boundary_co[0] = idx % ibuf->x; + boundary_co[1] = idx / ibuf->x; + copy_v2_v2_int(start_co, boundary_co); + backtracked_co[0] = (idx - 1) % ibuf->x; + backtracked_co[1] = (idx - 1) / ibuf->x; + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + copy_v2_v2_int(prev_check_co, start_co); + + BLI_stack_push(tgpf->stack, &boundary_co); + start_found = true; + break; + } + } + + while (true && start_found) + { + int cur_back_offset = -1; + for (int i = 0; i < NEIGHBOR_COUNT; i++) { + if (backtracked_offset[0][0] == offset[i][0] && + backtracked_offset[0][1] == offset[i][1]) + { + /* Finding the bracktracked pixel offset index */ + cur_back_offset = i; + break; + } + } + + int loop = 0; + while (loop < (NEIGHBOR_COUNT - 1) && cur_back_offset != -1) { + int offset_idx = (cur_back_offset + 1) % NEIGHBOR_COUNT; + current_check_co[0] = boundary_co[0] + offset[offset_idx][0]; + current_check_co[1] = boundary_co[1] + offset[offset_idx][1]; + + int image_idx = ibuf->x * current_check_co[1] + current_check_co[0]; + get_pixel(ibuf, image_idx, rgba); + + /* find next boundary pixel */ + if (rgba[1] == 1.0f) { + copy_v2_v2_int(boundary_co, current_check_co); + copy_v2_v2_int(backtracked_co, prev_check_co); + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + + BLI_stack_push(tgpf->stack, &boundary_co); + + break; + } + copy_v2_v2_int(prev_check_co, current_check_co); + cur_back_offset++; + loop++; + } + /* current pixel is equal to starting pixel */ + if (boundary_co[0] == start_co[0] && + boundary_co[1] == start_co[1]) + { + BLI_stack_pop(tgpf->stack, &v); + // boundary_found = true; + break; + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } +} + +/* get z-depth array to reproject on surface */ +static void gpencil_get_depth_array(tGPDfill *tgpf) +{ + tGPspoint *ptc; + ToolSettings *ts = tgpf->scene->toolsettings; + int totpoints = tgpf->sbuffer_size; + int i = 0; + + if (totpoints == 0) { + return; + } + + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) { + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(tgpf->win, tgpf->ar); + ED_view3d_autodist_init(tgpf->depsgraph, tgpf->ar, tgpf->v3d, 0); + + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = 0; + + /* get an array of depths, far depths are blended */ + int mval[2], mval_prev[2] = { 0 }; + int interp_depth = 0; + int found_depth = 0; + + tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); + + for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { + copy_v2_v2_int(mval, &ptc->x); + + if ((ED_view3d_autodist_depth(tgpf->ar, mval, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg(tgpf->ar, mval, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) + { + interp_depth = true; + } + else { + found_depth = true; + } + + copy_v2_v2_int(mval_prev, mval); + } + + if (found_depth == false) { + /* eeh... not much we can do.. :/, ignore depth in this case */ + for (i = totpoints - 1; i >= 0; i--) + tgpf->depth_arr[i] = 0.9999f; + } + else { + if (interp_depth) { + interp_sparse_array(tgpf->depth_arr, totpoints, FLT_MAX); + } + } + } +} + +/* create array of points using stack as source */ +static void gpencil_points_from_stack(tGPDfill *tgpf) +{ + tGPspoint *point2D; + int totpoints = BLI_stack_count(tgpf->stack); + if (totpoints == 0) { + return; + } + + tgpf->sbuffer_size = (short)totpoints; + tgpf->sbuffer = MEM_callocN(sizeof(tGPspoint) * totpoints, __func__); + + point2D = tgpf->sbuffer; + while (!BLI_stack_is_empty(tgpf->stack)) { + int v[2]; + BLI_stack_pop(tgpf->stack, &v); + point2D->x = v[0]; + point2D->y = v[1]; + + point2D->pressure = 1.0f; + point2D->strength = 1.0f;; + point2D->time = 0.0f; + point2D++; + } +} + +/* create a grease pencil stroke using points in buffer */ +static void gpencil_stroke_from_buffer(tGPDfill *tgpf) +{ + ToolSettings *ts = tgpf->scene->toolsettings; + int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + Brush *brush; + brush = BKE_brush_getactive_gpencil(ts); + if (brush == NULL) { + return; + } + + bGPDspoint *pt; + MDeformVert *dvert; + tGPspoint *point2D; + + if (tgpf->sbuffer_size == 0) { + return; + } + + /* get frame or create a new one */ + tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + + /* create new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); + gps->thickness = brush->size; + gps->inittime = 0.0f; + + /* the polygon must be closed, so enabled cyclic */ + gps->flag |= GP_STROKE_CYCLIC; + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_object_material_slot_find_index(tgpf->ob, tgpf->mat) - 1; + + /* allocate memory for storage points */ + gps->totpoints = tgpf->sbuffer_size; + gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_size, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * tgpf->sbuffer_size, "gp_stroke_weights"); + + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* add stroke to frame */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)){ + BLI_addhead(&tgpf->gpf->strokes, gps); + } + else { + BLI_addtail(&tgpf->gpf->strokes, gps); + } + + /* add points */ + pt = gps->points; + dvert = gps->dvert; + point2D = (tGPspoint *)tgpf->sbuffer; + for (int i = 0; i < tgpf->sbuffer_size && point2D; i++, point2D++, pt++, dvert++) { + /* convert screen-coordinates to 3D coordinates */ + gp_stroke_convertcoords_tpoint(tgpf->scene, tgpf->ar, tgpf->v3d, tgpf->ob, + tgpf->gpl, point2D, + tgpf->depth_arr ? tgpf->depth_arr + i : NULL, + &pt->x); + + pt->pressure = 1.0f; + pt->strength = 1.0f;; + pt->time = 0.0f; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* smooth stroke */ + float reduce = 0.0f; + 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); + } + reduce += 0.25f; // reduce the factor + } + + /* if axis locked, reproject to plane locked */ + if ((tgpf->lock_axis > GP_LOCKAXIS_NONE) && ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) { + float origin[3]; + ED_gp_get_drawing_reference(tgpf->v3d, tgpf->scene, tgpf->ob, tgpf->gpl, + ts->gpencil_v3d_align, origin); + ED_gp_project_stroke_to_plane(tgpf->ob, tgpf->rv3d, gps, origin, + tgpf->lock_axis - 1); + } + + /* if parented change position relative to parent object */ + for (int a = 0; a < tgpf->sbuffer_size; a++) { + pt = &gps->points[a]; + gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt); + } + + /* simplify stroke */ + for (int b = 0; b < tgpf->fill_simplylvl; b++) { + BKE_gpencil_simplify_fixed(gps); + } +} + +/* ----------------------- */ +/* Drawing */ +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_fill_status_indicators(bContext *C, tGPDfill *UNUSED(tgpf)) +{ + char status_str[UI_MAX_DRAW_STR]; + + BLI_snprintf(status_str, sizeof(status_str), IFACE_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back")); + ED_workspace_status_text(C, status_str); +} + +/* draw boundary lines to see fill limits */ +static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgpf) +{ + if (!tgpf->gpd) { + return; + } + float ink[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + gp_draw_datablock(tgpf, ink); +} + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_fill_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) +{ + tGPDfill *tgpf = (tGPDfill *)arg; + /* draw only in the region that originated operator. This is required for multiwindow */ + ARegion *ar = CTX_wm_region(C); + if (ar != tgpf->ar) { + return; + } + + gpencil_draw_boundary_lines(C, tgpf); +} + +/* check if context is suitable for filling */ +static bool gpencil_fill_poll(bContext *C) +{ + if (ED_operator_regionactive(C)) { + ScrArea *sa = CTX_wm_area(C); + if (sa->spacetype == SPACE_VIEW3D) { + return 1; + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator"); + return 0; + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return 0; + } +} + +/* Allocate memory and initialize values */ +static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op)) +{ + tGPDfill *tgpf = MEM_callocN(sizeof(tGPDfill), "GPencil Fill Data"); + + /* define initial values */ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + + /* set current scene and window info */ + tgpf->bmain = CTX_data_main(C); + tgpf->scene = CTX_data_scene(C); + tgpf->ob = CTX_data_active_object(C); + tgpf->sa = CTX_wm_area(C); + tgpf->ar = CTX_wm_region(C); + tgpf->rv3d = tgpf->ar->regiondata; + tgpf->v3d = tgpf->sa->spacedata.first; + tgpf->depsgraph = CTX_data_depsgraph(C); + tgpf->win = CTX_wm_window(C); + + /* set GP datablock */ + tgpf->gpd = gpd; + tgpf->gpl = BKE_gpencil_layer_getactive(gpd); + + tgpf->lock_axis = ts->gp_sculpt.lock_axis; + + tgpf->oldkey = -1; + tgpf->sbuffer_size = 0; + tgpf->sbuffer = NULL; + tgpf->depth_arr = NULL; + + /* save filling parameters */ + Brush *brush = BKE_brush_getactive_gpencil(ts); + tgpf->flag = brush->gpencil_settings->flag; + tgpf->fill_leak = brush->gpencil_settings->fill_leak; + tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; + tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; + tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; + + /* get color info */ + Material *ma = BKE_gpencil_get_material_from_brush(brush); + /* if no brush defaults, get material and color info */ + if ((ma == NULL) || (ma->gp_style == NULL)) { + ma = BKE_gpencil_material_ensure(bmain, tgpf->ob); + /* assign always the first material to the brush */ + brush->gpencil_settings->material = give_current_material(tgpf->ob, 1); + } + + tgpf->mat = ma; + + /* init undo */ + gpencil_undo_init(tgpf->gpd); + + /* return context data for running operator */ + return tgpf; +} + +/* end operator */ +static void gpencil_fill_exit(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + + /* clear undo stack */ + gpencil_undo_finish(); + + /* restore cursor to indicate end of fill */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + tGPDfill *tgpf = op->customdata; + + /* don't assume that operator data exists at all */ + if (tgpf) { + /* clear status message area */ + ED_workspace_status_text(C, NULL); + + MEM_SAFE_FREE(tgpf->sbuffer); + MEM_SAFE_FREE(tgpf->depth_arr); + + /* remove drawing handler */ + if (tgpf->draw_handle_3d) { + ED_region_draw_cb_exit(tgpf->ar->type, tgpf->draw_handle_3d); + } + + /* delete temp image */ + if (tgpf->ima) { + for (Image *ima = bmain->image.first; ima; ima = ima->id.next) { + if (ima == tgpf->ima) { + BLI_remlink(&bmain->image, ima); + BKE_image_free(tgpf->ima); + MEM_SAFE_FREE(tgpf->ima); + break; + } + } + } + + /* finally, free memory used by temp data */ + MEM_freeN(tgpf); + } + + /* clear pointer */ + op->customdata = NULL; + + /* drawing batch cache is dirty now */ + if ((ob) && (ob->type == OB_GPENCIL) && (ob->data)) { + bGPdata *gpd2 = ob->data; + DEG_id_tag_update(&gpd2->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd2->flag |= GP_DATA_CACHE_IS_DIRTY; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +static void gpencil_fill_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_fill_exit(C, op); +} + +/* Init: Allocate memory and set init values */ +static int gpencil_fill_init(bContext *C, wmOperator *op) +{ + tGPDfill *tgpf; + + /* check context */ + tgpf = op->customdata = gp_session_init_fill(C, op); + if (tgpf == NULL) { + /* something wasn't set correctly in context */ + gpencil_fill_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; +} + +/* start of interactive part of operator */ +static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + tGPDfill *tgpf = NULL; + + /* try to initialize context data needed */ + if (!gpencil_fill_init(C, op)) { + gpencil_fill_exit(C, op); + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpf = op->customdata; + } + + /* Enable custom drawing handlers to show help lines */ + if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) { + tgpf->draw_handle_3d = ED_region_draw_cb_activate(tgpf->ar->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); + } + + WM_cursor_modal_set(CTX_wm_window(C), BC_PAINTBRUSHCURSOR); + + gpencil_fill_status_indicators(C, tgpf); + + DEG_id_tag_update(&tgpf->gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator*/ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* events handling during interactive part of operator */ +static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDfill *tgpf = op->customdata; + + int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ + + switch (event->type) { + case ESCKEY: + case RIGHTMOUSE: + estate = OPERATOR_CANCELLED; + break; + case LEFTMOUSE: + tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); + /* first time the event is not enabled to show help lines */ + if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) { + ARegion *ar = BKE_area_find_region_xy(CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); + if (ar) { + bool in_bounds = false; + + /* Perform bounds check */ + in_bounds = BLI_rcti_isect_pt(&ar->winrct, event->x, event->y); + + if ((in_bounds) && (ar->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]; + + /* save size */ + tgpf->sizex = ar->winx; + tgpf->sizey = ar->winy; + + /* render screen to temp image */ + gp_render_offscreen(tgpf); + + /* apply boundary fill */ + gpencil_boundaryfill_area(tgpf); + + /* clean borders to avoid infinite loops */ + gpencil_clean_borders(tgpf); + + /* analyze outline */ + gpencil_get_outline_points(tgpf); + + /* create array of points from stack */ + gpencil_points_from_stack(tgpf); + + /* create z-depth array for reproject */ + gpencil_get_depth_array(tgpf); + + /* create stroke and reproject */ + gpencil_stroke_from_buffer(tgpf); + + /* free temp stack data */ + if (tgpf->stack) { + BLI_stack_free(tgpf->stack); + } + + /* push undo data */ + gpencil_undo_push(tgpf->gpd); + + estate = OPERATOR_FINISHED; + } + else { + estate = OPERATOR_CANCELLED; + } + } + else { + estate = OPERATOR_CANCELLED; + } + } + tgpf->oldkey = event->type; + break; + } + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + gpencil_fill_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_fill_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + break; + } + + /* return status code */ + return estate; +} + +void GPENCIL_OT_fill(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Grease Pencil Fill"; + ot->idname = "GPENCIL_OT_fill"; + ot->description = "Fill with color the shape formed by strokes"; + + /* api callbacks */ + ot->invoke = gpencil_fill_invoke; + ot->modal = gpencil_fill_modal; + ot->poll = gpencil_fill_poll; + ot->cancel = gpencil_fill_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + prop = RNA_def_boolean(ot->srna, "on_back", false, "Draw On Back", "Send new stroke to Back"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 90ff1e0bb25..0218530be4e 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -34,24 +34,144 @@ #include "DNA_vec_types.h" +#include "ED_numinput.h" + /* internal exports only */ struct bGPdata; struct bGPDstroke; struct bGPDspoint; +struct tGPspoint; +struct Material; struct GHash; struct RNG; +struct Brush; +struct Scene; struct ARegion; +struct View3D; struct View2D; struct wmOperatorType; +struct Depsgraph; + struct PointerRNA; struct PropertyRNA; struct EnumPropertyItem; +/* ***************************************************** */ +/* Modal Operator Geometry Preview + * + * Several modal operators (Fill, Interpolate, Primitive) + * need to run some drawing code to display previews, or + * to perform screen-space/image-based analysis routines. + * The following structs + function prototypes are used + * by these operators so that the operator code + * (in gpencil_.c) can communicate with the drawing + * code (in drawgpencil.c). + * + * NOTE: All this is within the gpencil module, so nothing needs + * to be exported to other modules. + */ + +/* Internal Operator-State Data ------------------------ */ + +/* Temporary draw data (no draw manager mode) */ +typedef struct tGPDdraw { + struct RegionView3D *rv3d; /* region to draw */ + struct Depsgraph *depsgraph; /* depsgraph */ + struct Object *ob; /* GP object */ + struct bGPdata *gpd; /* current GP datablock */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + struct bGPDframe *t_gpf; /* temporal frame */ + struct bGPDstroke *gps; /* stroke */ + int disable_fill; /* disable fill */ + int offsx; /* windows offset x */ + int offsy; /* windows offset y */ + int winx; /* windows width */ + int winy; /* windows height */ + int dflag; /* flags datablock */ + short lthick; /* layer thickness */ + float opacity; /* opacity */ + float tintcolor[4]; /* tint color */ + bool onion; /* onion flag */ + bool custonion; /* use custom onion colors */ + float diff_mat[4][4]; /* matrix */ +} tGPDdraw; + + +/* Temporary interpolate operation data */ +typedef struct tGPDinterpolate_layer { + struct tGPDinterpolate_layer *next, *prev; + + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */ + struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */ + struct bGPDframe *interFrame; /* interpolated frame */ + float factor; /* interpolate factor */ + +} tGPDinterpolate_layer; + +typedef struct tGPDinterpolate { + struct Scene *scene; /* current scene from context */ + struct ScrArea *sa; /* area where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + + int cframe; /* current frame number */ + ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */ + float shift; /* value for determining the displacement influence */ + float init_factor; /* initial interpolation factor for active layer */ + float low_limit; /* shift low limit (-100%) */ + float high_limit; /* shift upper limit (200%) */ + int flag; /* flag from toolsettings */ + + NumInput num; /* numeric input */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ + void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */ +} tGPDinterpolate; + + +/* Temporary primitive operation data */ +typedef struct tGPDprimitive { + struct Depsgraph *depsgraph; + struct wmWindow *win; /* window where painting originated */ + struct Scene *scene; /* current scene from context */ + struct Object *ob; /* current active gp object */ + struct ScrArea *sa; /* area where painting originated */ + struct RegionView3D *rv3d; /* region where painting originated */ + struct View3D *v3d; /* view3d where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + struct Brush *brush; /* current brush */ + + int cframe; /* current frame number */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + int type; /* type of primitive */ + int tot_edges; /* number of polygon edges */ + int top[2]; /* first box corner */ + int bottom[2]; /* last box corner */ + int flag; /* flag to determine operations in progress */ + + int lock_axis; /* lock to viewport axis */ + + NumInput num; /* numeric input */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ +} tGPDprimitive; + + +/* Modal Operator Drawing Callbacks ------------------------ */ + +void ED_gp_draw_interpolation(const struct bContext *C, struct tGPDinterpolate *tgpi, const int type); +void ED_gp_draw_primitives(const struct bContext *C, struct tGPDprimitive *tgpi, const int type); +void ED_gp_draw_fill(struct tGPDdraw *tgpw); + /* ***************************************************** */ /* Internal API */ @@ -84,21 +204,29 @@ void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, float *r_x, float *r_y); void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt); - -void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps); - -void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt); +/** + * Change points position relative to parent object + */ +void gp_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps); +/** + * Change point position relative to parent object + */ +void gp_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt); bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]); +/* helper to convert 2d to 3d */ +void gp_stroke_convertcoords_tpoint(struct Scene *scene, struct ARegion *ar, + struct View3D *v3d, struct Object *ob, + bGPDlayer *gpl, const struct tGPspoint *point2D, + float *depth, float out[3]); + /* Poll Callbacks ------------------------------------ */ /* gpencil_utils.c */ bool gp_add_poll(struct bContext *C); bool gp_active_layer_poll(struct bContext *C); bool gp_active_brush_poll(struct bContext *C); -bool gp_active_palette_poll(struct bContext *C); -bool gp_active_palettecolor_poll(struct bContext *C); bool gp_brush_crt_presets_poll(bContext *C); /* Copy/Paste Buffer --------------------------------- */ @@ -107,18 +235,19 @@ bool gp_brush_crt_presets_poll(bContext *C); extern ListBase gp_strokes_copypastebuf; /* Build a map for converting between old colornames and destination-color-refs */ -struct GHash *gp_copybuf_validate_colormap(bGPdata *gpd); +struct GHash *gp_copybuf_validate_colormap(struct bContext *C); /* Stroke Editing ------------------------------------ */ -void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags); - +void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, + int tag_flags, bool select); +int gp_delete_selected_point_wrap(bContext *C); bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure); bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf); bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf); -void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); -void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, struct RNG *rng); +void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide); +void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, struct RNG *rng); /* Layers Enums -------------------------------------- */ @@ -129,22 +258,18 @@ const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); -/* Enums of GP Brushes */ -const EnumPropertyItem *ED_gpencil_brushes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free); - -/* Enums of GP palettes */ -const EnumPropertyItem *ED_gpencil_palettes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free); - /* ***************************************************** */ /* Operator Defines */ +/* annotations ------ */ + +void GPENCIL_OT_annotate(struct wmOperatorType *ot); + + /* drawing ---------- */ void GPENCIL_OT_draw(struct wmOperatorType *ot); +void GPENCIL_OT_fill(struct wmOperatorType *ot); /* Paint Modes for operator */ typedef enum eGPencil_PaintModes { @@ -160,7 +285,11 @@ typedef enum eGPencil_PaintModes { /* stroke editing ----- */ void GPENCIL_OT_editmode_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_selection_opacity_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_multiedit_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); void GPENCIL_OT_select_all(struct wmOperatorType *ot); @@ -174,6 +303,7 @@ void GPENCIL_OT_select_more(struct wmOperatorType *ot); 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_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_delete(struct wmOperatorType *ot); @@ -218,6 +348,8 @@ void GPENCIL_OT_blank_frame_add(struct wmOperatorType *ot); void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); +void GPENCIL_OT_frame_duplicate(struct wmOperatorType *ot); +void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); @@ -226,6 +358,13 @@ enum { GP_STROKE_JOINCOPY = 1 }; +enum { + GP_STROKE_BOX = -1, + GP_STROKE_LINE = 1, + GP_STROKE_CIRCLE = 2 +}; + + void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot); void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot); @@ -234,30 +373,15 @@ void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); void GPENCIL_OT_stroke_join(struct wmOperatorType *ot); void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot); void GPENCIL_OT_stroke_subdivide(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_simplify(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_simplify_fixed(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_separate(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_split(struct wmOperatorType *ot); -void GPENCIL_OT_brush_add(struct wmOperatorType *ot); -void GPENCIL_OT_brush_remove(struct wmOperatorType *ot); -void GPENCIL_OT_brush_change(struct wmOperatorType *ot); -void GPENCIL_OT_brush_move(struct wmOperatorType *ot); void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); -void GPENCIL_OT_brush_copy(struct wmOperatorType *ot); void GPENCIL_OT_brush_select(struct wmOperatorType *ot); -void GPENCIL_OT_palette_add(struct wmOperatorType *ot); -void GPENCIL_OT_palette_remove(struct wmOperatorType *ot); -void GPENCIL_OT_palette_change(struct wmOperatorType *ot); -void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot); - -void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot); -void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot); +void GPENCIL_OT_sculpt_select(struct wmOperatorType *ot); /* undo stack ---------- */ @@ -271,6 +395,30 @@ void GPENCIL_OT_interpolate(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_reverse(struct wmOperatorType *ot); +/* primitives ---------- */ + +void GPENCIL_OT_primitive(struct wmOperatorType *ot); + +/* vertex groups ------------ */ +void GPENCIL_OT_vertex_group_assign(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_remove_from(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_select(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_deselect(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_invert(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_smooth(struct wmOperatorType *ot); + +/* color handle */ +void GPENCIL_OT_lock_layer(struct wmOperatorType *ot); +void GPENCIL_OT_color_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_color_hide(struct wmOperatorType *ot); +void GPENCIL_OT_color_reveal(struct wmOperatorType *ot); +void GPENCIL_OT_color_lock_all(struct wmOperatorType *ot); +void GPENCIL_OT_color_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_color_select(struct wmOperatorType *ot); + +/* convert old 2.7 files to 2.8 */ +void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot); + /* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ @@ -331,24 +479,37 @@ typedef enum ACTCONT_TYPES { */ #define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \ { \ + Depsgraph *depsgraph_ = CTX_data_depsgraph(C); \ + Object *obact_ = CTX_data_active_object(C); \ + bGPdata *gpd_ = CTX_data_gpencil_data(C); \ + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \ { \ - if (gpl->actframe == NULL) \ - continue; \ - /* calculate difference matrix if parent object */ \ - float diff_mat[4][4]; \ - ED_gpencil_parent_location(gpl, diff_mat); \ - /* loop over strokes */ \ - for (bGPDstroke *gps = gpl->actframe->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(gpl, gps) == false) \ - continue; \ - /* ... Do Stuff With Strokes ... */ + 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))) { \ + /* calculate difference matrix */ \ + float diff_mat[4][4]; \ + ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, diff_mat); \ + /* loop over strokes */ \ + 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(obact_, gpl, gps) == false) \ + continue; \ + /* ... Do Stuff With Strokes ... */ #define GP_EDITABLE_STROKES_END \ + } \ + } \ + if (!is_multiedit) { \ + break; \ + } \ } \ } \ CTX_DATA_END; \ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index cc30d7ec266..6541e9f012a 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -47,6 +47,7 @@ #include "DNA_color_types.h" #include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -60,6 +61,7 @@ #include "BKE_library.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_deform.h" #include "UI_interface.h" #include "UI_resources.h" @@ -79,6 +81,9 @@ #include "ED_view3d.h" #include "ED_space_api.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -108,11 +113,13 @@ static bool gpencil_view3d_poll(bContext *C) static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) { bGPDspoint *prev, *pt, *next; + MDeformVert *dvert; /* update points */ for (int i = 0; i < new_stroke->totpoints; i++) { prev = &gps_from->points[i]; pt = &new_stroke->points[i]; + dvert = &new_stroke->dvert[i]; next = &gps_to->points[i]; /* Interpolate all values */ @@ -120,6 +127,9 @@ static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_t 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); + + dvert->totweight = 0; + dvert->dw = NULL; } } @@ -128,6 +138,7 @@ static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_t /* 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; @@ -156,12 +167,14 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } /* Helper: Verify valid strokes for interpolation */ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) { + Object *ob = CTX_data_active_object(C); ToolSettings *ts = CTX_data_tool_settings(C); eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; @@ -190,7 +203,7 @@ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { continue; } @@ -213,6 +226,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) bGPdata *gpd = tgpi->gpd; bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); bGPDframe *actframe = active_gpl->actframe; + Object *ob = CTX_data_active_object(C); /* save initial factor for active layer to define shift limits */ tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / (actframe->next->framenum - actframe->framenum + 1); @@ -255,7 +269,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) bGPDstroke *gps_to; int fFrame; - bGPDstroke *new_stroke; + bGPDstroke *new_stroke = NULL; bool valid = true; @@ -269,7 +283,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { valid = false; } @@ -281,16 +295,13 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); if (valid) { /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + 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_CACHES; @@ -302,6 +313,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ new_stroke->totpoints = 0; new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); + 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_CACHES; @@ -317,17 +329,17 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) /* Drawing Callbacks */ /* Drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_screen(const struct bContext *C, ARegion *UNUSED(ar), void *arg) { tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); + 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 *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) { tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); } /* ----------------------- */ @@ -392,6 +404,7 @@ 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) { @@ -416,6 +429,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) BLI_freelistN(&tgpi->ilayers); MEM_freeN(tgpi); } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* clear pointer */ @@ -483,9 +497,10 @@ static int gpencil_interpolate_init(bContext *C, wmOperator *op) static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); bGPdata *gpd = CTX_data_gpencil_data(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDframe *actframe = gpl->actframe; tGPDinterpolate *tgpi = NULL; @@ -496,7 +511,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent } /* cannot interpolate in extremes */ - if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); return OPERATOR_CANCELLED; } @@ -529,6 +544,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent /* update shift indicator in header */ gpencil_interpolate_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* add a modal handler for this operator */ @@ -571,6 +587,8 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent /* make copy of source stroke, then adjust pointer to points too */ gps_dst = MEM_dupallocN(gps_src); gps_dst->points = MEM_dupallocN(gps_src->points); + gps_dst->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_CACHES; BLI_addtail(&gpf_dst->strokes, gps_dst); @@ -902,8 +920,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); bGPDframe *actframe = active_gpl->actframe; - Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; @@ -913,7 +934,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* cannot interpolate in extremes */ - if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); return OPERATOR_CANCELLED; } @@ -961,7 +982,7 @@ 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; + bGPDstroke *new_stroke = NULL; /* only selected */ if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { @@ -972,7 +993,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { continue; } @@ -990,15 +1011,15 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { + /* free weights of removed points */ + BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, gps_from->totpoints - gps_to->totpoints); + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + 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_CACHES; @@ -1014,6 +1035,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1055,6 +1077,8 @@ static bool gpencil_interpolate_reverse_poll(bContext *C) static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* Go through each layer, deleting the breakdowns around the current frame, * but only if there is a keyframe nearby to stop at */ @@ -1123,6 +1147,7 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/gpencil/gpencil_old.c b/source/blender/editors/gpencil/gpencil_old.c new file mode 100644 index 00000000000..b7af1c80d4c --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_old.c @@ -0,0 +1,219 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation, + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + * Use deprecated data to convert old 2.7x files + */ + +/** \file blender/editors/gpencil/gpencil_old.c + * \ingroup edgpencil + */ + + /* allow to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_object.h" +#include "BKE_material.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_gpencil.h" + +#include "gpencil_intern.h" + + /* Free all of a gp-colors */ +static void free_gpencil_colors(bGPDpalette *palette) +{ + /* error checking */ + if (palette == NULL) { + return; + } + + /* free colors */ + BLI_freelistN(&palette->colors); +} + +/* Free all of the gp-palettes and colors */ +static void free_palettes(ListBase *list) +{ + bGPDpalette *palette_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete palettes */ + for (bGPDpalette *palette = list->first; palette; palette = palette_next) { + palette_next = palette->next; + /* free palette colors */ + free_gpencil_colors(palette); + + MEM_freeN(palette); + } + BLI_listbase_clear(list); +} + +/* ***************** Convert old 2.7 files to 2.8 ************************ */ +static bool gpencil_convert_old_files_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + return (int) (scene->gpd != NULL); +} + +static int gpencil_convert_old_files_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* Convert grease pencil scene datablock to GP object */ + if ((scene->gpd) && (view_layer != NULL)) { + Object *ob; + ob = BKE_object_add_for_data(bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); + zero_v3(ob->loc); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + + /* convert grease pencil palettes (version >= 2.78) to materials and weights */ + bGPdata *gpd = scene->gpd; + for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + + /* create material slot */ + BKE_object_material_slot_add(bmain, ob); + Material *ma = BKE_material_add_gpencil(bmain, palcolor->info); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + /* copy color settings */ + MaterialGPencilStyle *gp_style = ma->gp_style; + copy_v4_v4(gp_style->stroke_rgba, palcolor->color); + copy_v4_v4(gp_style->fill_rgba, palcolor->fill); + gp_style->flag = palcolor->flag; + + /* 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) { + if ((gps->colorname[0] != '\0') && + (STREQ(gps->colorname, palcolor->info))) + { + gps->mat_nr = ob->totcol - 1; + gps->colorname[0] = '\0'; + /* create weights array */ + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + } + } + } + } + } + } + + /* free palettes */ + free_palettes(&gpd->palettes); + + /* disable all GP modes */ + ED_gpencil_setup_modes(C, gpd, 0); + + /* set cache as dirty */ + BKE_gpencil_batch_cache_dirty(ob->data); + + scene->gpd = NULL; + } + +#if 0 /* GPXX */ + /* Handle object-linked grease pencil datablocks */ + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->gpd) { + if (ob->type == OB_GPENCIL) { + /* GP Object - remap the links */ + ob->data = ob->gpd; + ob->gpd = NULL; + } + else if (ob->type == OB_EMPTY) { + /* Empty with GP data - This should be able to be converted + * to a GP object with little data loss + */ + ob->data = ob->gpd; + ob->gpd = NULL; + ob->type = OB_GPENCIL; + } + else { + /* FIXME: What to do in this case? + * + * We cannot create new objects for these, as we don't have a scene & scene layer + * to put them into from here... + */ + printf("WARNING: Old Grease Pencil data ('%s') still exists on Object '%s'\n", + ob->gpd->id.name + 2, ob->id.name + 2); + } + } + } +#endif + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_convert_old_files(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Convert 2.7 Grease Pencil File"; + ot->idname = "GPENCIL_OT_convert_old_files"; + ot->description = "Convert 2.7x grease pencil files to 2.8"; + + /* callbacks */ + ot->exec = gpencil_convert_old_files_exec; + ot->poll = gpencil_convert_old_files_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 3f114a4dd4a..991bfb622b7 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -35,8 +35,14 @@ #include "BLI_sys_types.h" #include "BKE_context.h" +#include "BKE_brush.h" +#include "BKE_gpencil.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "WM_api.h" #include "WM_types.h" @@ -52,7 +58,7 @@ /* ****************************************** */ /* Grease Pencil Keymaps */ -/* Generic Drawing Keymap */ +/* Generic Drawing Keymap - Annotations */ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) { wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil", 0, 0); @@ -60,36 +66,22 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) /* Draw --------------------------------------- */ /* draw */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, 0, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* draw - straight lines */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, KM_ALT, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* draw - poly lines */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, KM_CTRL, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* erase */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, 0, DKEY); - RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - - /* Tablet Mappings for Drawing ------------------ */ - /* For now, only support direct drawing using the eraser, as most users using a tablet - * may still want to use that as their primary pointing device! - */ -#if 0 - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0); - RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); -#endif - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", RIGHTMOUSE, KM_PRESS, 0, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); RNA_boolean_set(kmi->ptr, "wait_for_input", false); @@ -121,67 +113,85 @@ static bool gp_stroke_editmode_poll(bContext *C) return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)); } -/* Stroke Editing Keymap - Only when editmode is enabled */ -static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) +/* Poll callback for stroke painting mode */ +static bool gp_stroke_paintmode_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE)); +} + +/* Poll callback for stroke painting (draw brush) */ +static bool gp_stroke_paintmode_draw_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) + && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)); +} + +/* Poll callback for stroke painting (erase brush) */ +static bool gp_stroke_paintmode_erase_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) + && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)); +} + +/* Poll callback for stroke painting (fill) */ +static bool gp_stroke_paintmode_fill_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) + && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_FILL)); +} + +/* Poll callback for stroke sculpting mode */ +static bool gp_stroke_sculptmode_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + + /* if not gpencil object and not view3d, need sculpt keys if edit mode */ + if (sa->spacetype != SPACE_VIEW3D) { + 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 0; +} + +/* Poll callback for stroke weight paint mode */ +static bool gp_stroke_weightmode_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_WEIGHTMODE)); + } + + return 0; +} + +static void ed_keymap_gpencil_selection(wmKeyMap *keymap) { - wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); wmKeyMapItem *kmi; - /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ - keymap->poll = gp_stroke_editmode_poll; - - /* ----------------------------------------------- */ - - /* Exit EditMode */ - WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0); - - /* Pie Menu - For settings/tools easy access */ - WM_keymap_add_menu_pie(keymap, "GPENCIL_MT_pie_sculpt", EKEY, KM_PRESS, 0, DKEY); - - /* Brush Settings */ - /* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys - * in other modes. However, when we are dealing with Stroke Edit Mode, we know for certain - * that the only data being edited is that of the Grease Pencil strokes - */ - - /* CTRL + FKEY = Eraser Radius */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); - RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); - - /* Interpolation */ - WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); - WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); - - /* Sculpting ------------------------------------- */ - - /* Brush-Based Editing: - * EKEY + LMB = Single stroke, draw immediately - * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. - * - * For the modal version, use D+E -> Sculpt - */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ - - - /* Shift-FKEY = Sculpt Strength */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); - RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); - - /* FKEY = Sculpt Brush Size */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); - - - /* Selection ------------------------------------- */ /* select all */ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", AKEY, KM_PRESS, 0, 0); RNA_enum_set(kmi->ptr, "action", SEL_SELECT); @@ -204,17 +214,14 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "deselect", true); /* In the Node Editor, lasso select needs ALT modifier too (as somehow CTRL+LMB drag gets taken for "cut" quite early) - * There probably isn't too much harm adding this for other editors too as part of standard GP editing keymap. This hotkey - * combo doesn't seem to see much use under standard scenarios? - */ + * There probably isn't too much harm adding this for other editors too as part of standard GP editing keymap. This hotkey + * combo doesn't seem to see much use under standard scenarios? + */ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); RNA_boolean_set(kmi->ptr, "deselect", false); kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL | KM_ALT, 0); RNA_boolean_set(kmi->ptr, "deselect", true); - /* normal select */ - WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "toggle", true); @@ -223,11 +230,18 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "entire_strokes", true); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "entire_strokes", true); + RNA_boolean_set(kmi->ptr, "extend", true); + /* select linked */ /* NOTE: While LKEY is redundant, not having it breaks the mode illusion too much */ WM_keymap_add_item(keymap, "GPENCIL_OT_select_linked", LKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_select_linked", LKEY, KM_PRESS, KM_CTRL, 0); + /* select alternate */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_alternate", LKEY, KM_PRESS, KM_SHIFT, 0); + /* select grouped */ WM_keymap_add_item(keymap, "GPENCIL_OT_select_grouped", GKEY, KM_PRESS, KM_SHIFT, 0); @@ -235,6 +249,102 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0); +} + +static void ed_keymap_gpencil_sculpt(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + /* Pie Menu - For settings/tools easy access */ + WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_sculpt", EKEY, KM_PRESS, 0, DKEY); + + /* Sculpting ------------------------------------- */ + + /* Brush-Based Editing: + * EKEY + LMB = Single stroke, draw immediately + * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. + * + * For the modal version, use D+E -> Sculpt + */ + /* GPXX: disabled to make toolsystem works */ + //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0); + //RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ + + /* Shift-FKEY = Sculpt Strength */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); + + /* FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); + + /* menu sculpt specials */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_sculpt_specials", WKEY, KM_PRESS, 0, 0); +} + +static void ed_keymap_gpencil_weight(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + + /* Brush-Based Editing: + * EKEY + LMB = Single stroke, draw immediately + * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. + * + * For the modal version, use D+E -> Sculpt + */ + /* GPXX: disabled to make toolsystem works */ + //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0); + //RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ +} + +/* Stroke Editing Keymap - Only when editmode is enabled */ +static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ + keymap->poll = gp_stroke_editmode_poll; + + /* ----------------------------------------------- */ + + /* Brush Settings */ + /* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys + * in other modes. However, when we are dealing with Stroke Edit Mode, we know for certain + * that the only data being edited is that of the Grease Pencil strokes + */ + + /* Interpolation */ + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + + /* normal select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); + + /* Selection */ + ed_keymap_gpencil_selection(keymap); + /* Editing ----------------------------------------- */ /* duplicate and move selected points */ @@ -253,6 +363,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* menu edit specials */ WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_edit_specials", WKEY, KM_PRESS, 0, 0); + /* menu separate */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_separate", PKEY, KM_PRESS, 0, 0); + + /* split strokes */ + WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_split", VKEY, KM_PRESS, 0, 0); + /* join strokes */ WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0); @@ -271,7 +387,6 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* snap */ WM_keymap_add_menu(keymap, "GPENCIL_MT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); - /* convert to geometry */ WM_keymap_add_item(keymap, "GPENCIL_OT_convert", CKEY, KM_PRESS, KM_ALT, 0); @@ -288,6 +403,13 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0); + /* toogle multiedit support */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 1); + /* Isolate Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0); @@ -317,28 +439,246 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* Proportional Editing */ ED_keymap_proportional_cycle(keyconf, keymap); ED_keymap_proportional_editmode(keyconf, keymap, true); + + /* menu - add GP object (3d view only) */ + WM_keymap_add_item(keymap, "OBJECT_OT_gpencil_add", AKEY, KM_PRESS, KM_SHIFT, 0); + + /* menu vertex group */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_vertex_group", GKEY, KM_PRESS, KM_CTRL, 0); + + /* toggle edit mode */ + WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0); } +/* keys for draw with a drawing brush (no fill) */ +static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_draw_poll; + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* draw - straight lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* draw - poly lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* erase */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Tablet Mappings for Drawing ------------------ */ + /* For now, only support direct drawing using the eraser, as most users using a tablet + * may still want to use that as their primary pointing device! + */ +#if 0 + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); +#endif + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Selection (used by eraser) */ + /* border select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0); + + /* lasso select */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); +} + +/* keys for draw with a eraser brush (erase) */ +static void ed_keymap_gpencil_painting_erase(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_erase_poll; + + /* erase */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Selection (used by eraser) */ + /* border select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0); + + /* lasso select */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); +} + +/* keys for draw with a fill brush */ +static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_fill_poll; + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "on_back", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "on_back", true); + + /* if press alternative key, the brush now it's for drawing areas */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /* disable straight lines */ + RNA_boolean_set(kmi->ptr, "disable_straight", true); + + /* if press alternative key, the brush now it's for drawing lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /* disable straight lines */ + RNA_boolean_set(kmi->ptr, "disable_straight", true); + /* enable special stroke with no fill flag */ + RNA_boolean_set(kmi->ptr, "disable_fill", true); +} + +/* Stroke Painting Keymap - Only when paintmode is enabled */ +static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */ + keymap->poll = gp_stroke_paintmode_poll; + + /* FKEY = Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_paint.brush.size"); + + /* CTRL + FKEY = Eraser Radius */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); + + /* menu draw specials */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_specials", WKEY, KM_PRESS, 0, 0); + + /* menu draw delete */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_delete", XKEY, KM_PRESS, 0, 0); + +} + +/* Stroke Sculpting Keymap - Only when sculptmode is enabled */ +static void ed_keymap_gpencil_sculpting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + keymap->poll = gp_stroke_sculptmode_poll; + + /* Selection */ + ed_keymap_gpencil_selection(keymap); + + /* sculpt */ + ed_keymap_gpencil_sculpt(keymap); + + /* toogle multiedit support */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 1); + +} + +/* Stroke Weight Paint Keymap - Only when weight is enabled */ +static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + keymap->poll = gp_stroke_weightmode_poll; + + /* Selection */ + ed_keymap_gpencil_selection(keymap); + + /* sculpt */ + ed_keymap_gpencil_weight(keymap); + + /* Shift-FKEY = Sculpt Strength */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.weight_brush.strength"); + + /* FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.weight_brush.size"); + + /* toogle multiedit support */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_multiedit_toggle", QKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "toggle_visibility", 1); + +} /* ==================== */ void ED_keymap_gpencil(wmKeyConfig *keyconf) { ed_keymap_gpencil_general(keyconf); ed_keymap_gpencil_editing(keyconf); + ed_keymap_gpencil_painting(keyconf); + ed_keymap_gpencil_painting_draw(keyconf); + ed_keymap_gpencil_painting_erase(keyconf); + ed_keymap_gpencil_painting_fill(keyconf); + ed_keymap_gpencil_sculpting(keyconf); + ed_keymap_gpencil_weightpainting(keyconf); } /* ****************************************** */ void ED_operatortypes_gpencil(void) { + /* Annotations -------------------- */ + + WM_operatortype_append(GPENCIL_OT_annotate); + /* Drawing ----------------------- */ WM_operatortype_append(GPENCIL_OT_draw); + WM_operatortype_append(GPENCIL_OT_fill); /* Editing (Strokes) ------------ */ WM_operatortype_append(GPENCIL_OT_editmode_toggle); + 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_selection_opacity_toggle); + WM_operatortype_append(GPENCIL_OT_multiedit_toggle); WM_operatortype_append(GPENCIL_OT_select); WM_operatortype_append(GPENCIL_OT_select_all); @@ -352,6 +692,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_less); 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_duplicate); WM_operatortype_append(GPENCIL_OT_delete); @@ -391,6 +732,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); + WM_operatortype_append(GPENCIL_OT_frame_duplicate); + WM_operatortype_append(GPENCIL_OT_frame_clean_fill); WM_operatortype_append(GPENCIL_OT_convert); @@ -402,36 +745,46 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_join); WM_operatortype_append(GPENCIL_OT_stroke_flip); WM_operatortype_append(GPENCIL_OT_stroke_subdivide); + WM_operatortype_append(GPENCIL_OT_stroke_simplify); + WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed); + WM_operatortype_append(GPENCIL_OT_stroke_separate); + WM_operatortype_append(GPENCIL_OT_stroke_split); - WM_operatortype_append(GPENCIL_OT_palette_add); - WM_operatortype_append(GPENCIL_OT_palette_remove); - WM_operatortype_append(GPENCIL_OT_palette_change); - WM_operatortype_append(GPENCIL_OT_palette_lock_layer); - WM_operatortype_append(GPENCIL_OT_palettecolor_add); - WM_operatortype_append(GPENCIL_OT_palettecolor_remove); - WM_operatortype_append(GPENCIL_OT_palettecolor_isolate); - WM_operatortype_append(GPENCIL_OT_palettecolor_hide); - WM_operatortype_append(GPENCIL_OT_palettecolor_reveal); - WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all); - WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all); - WM_operatortype_append(GPENCIL_OT_palettecolor_move); - WM_operatortype_append(GPENCIL_OT_palettecolor_select); - WM_operatortype_append(GPENCIL_OT_palettecolor_copy); - - WM_operatortype_append(GPENCIL_OT_brush_add); - WM_operatortype_append(GPENCIL_OT_brush_remove); - WM_operatortype_append(GPENCIL_OT_brush_change); - WM_operatortype_append(GPENCIL_OT_brush_move); WM_operatortype_append(GPENCIL_OT_brush_presets_create); - WM_operatortype_append(GPENCIL_OT_brush_copy); WM_operatortype_append(GPENCIL_OT_brush_select); + WM_operatortype_append(GPENCIL_OT_sculpt_select); + + /* vertex groups */ + WM_operatortype_append(GPENCIL_OT_vertex_group_assign); + WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from); + WM_operatortype_append(GPENCIL_OT_vertex_group_select); + WM_operatortype_append(GPENCIL_OT_vertex_group_deselect); + WM_operatortype_append(GPENCIL_OT_vertex_group_invert); + WM_operatortype_append(GPENCIL_OT_vertex_group_smooth); + + /* color handle */ + WM_operatortype_append(GPENCIL_OT_lock_layer); + WM_operatortype_append(GPENCIL_OT_color_isolate); + WM_operatortype_append(GPENCIL_OT_color_hide); + WM_operatortype_append(GPENCIL_OT_color_reveal); + WM_operatortype_append(GPENCIL_OT_color_lock_all); + WM_operatortype_append(GPENCIL_OT_color_unlock_all); + WM_operatortype_append(GPENCIL_OT_color_select); + /* Editing (Time) --------------- */ /* Interpolation */ WM_operatortype_append(GPENCIL_OT_interpolate); WM_operatortype_append(GPENCIL_OT_interpolate_sequence); WM_operatortype_append(GPENCIL_OT_interpolate_reverse); + + /* Primitives */ + WM_operatortype_append(GPENCIL_OT_primitive); + + /* convert old 2.7 files to 2.8 */ + WM_operatortype_append(GPENCIL_OT_convert_old_files); + } void ED_operatormacros_gpencil(void) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 789e9865ae4..995ab91ff8b 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -46,26 +46,34 @@ #include "PIL_time.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_brush_types.h" +#include "DNA_windowmanager_types.h" + #include "BKE_colortools.h" +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_paint.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_main.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_layer.h" +#include "BKE_material.h" #include "BKE_screen.h" #include "BKE_tracking.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_brush_types.h" -#include "DNA_windowmanager_types.h" - #include "UI_view2d.h" #include "ED_gpencil.h" #include "ED_screen.h" +#include "ED_object.h" #include "ED_view3d.h" #include "ED_clip.h" @@ -82,6 +90,7 @@ #include "WM_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" @@ -110,6 +119,8 @@ typedef enum eGPencil_PaintFlags { GP_PAINTFLAG_STROKEADDED = (1 << 1), GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), GP_PAINTFLAG_SELECTMASK = (1 << 3), + GP_PAINTFLAG_HARD_ERASER = (1 << 4), + GP_PAINTFLAG_STROKE_ERASER = (1 << 5), } eGPencil_PaintFlags; @@ -117,10 +128,13 @@ typedef enum eGPencil_PaintFlags { * "p" = op->customdata */ typedef struct tGPsdata { - Main *bmain; + bContext *C; + + Main *bmain; /* main database pointer */ Scene *scene; /* current scene from context */ struct Depsgraph *depsgraph; + Object *ob; /* current object */ wmWindow *win; /* window where painting originated */ ScrArea *sa; /* area where painting originated */ ARegion *ar; /* region where painting originated */ @@ -165,14 +179,23 @@ typedef struct tGPsdata { void *erasercursor; /* radial cursor data for drawing eraser */ - bGPDpalettecolor *palettecolor; /* current palette color */ - bGPDbrush *brush; /* current drawing brush */ + /* mat settings are only used for 3D view */ + Material *material; /* current material */ + + Brush *brush; /* current drawing brush */ + Brush *eraser; /* default eraser brush */ short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ int lock_axis; /* lock drawing to one axis */ + bool disable_fill; /* the stroke is no fill mode */ RNG *rng; short keymodifier; /* key used for invoking the operator */ + short shift; /* shift modifier flag */ + + float totpixlen; /* size in pixels for uv calculation */ + + ReportList *reports; } tGPsdata; /* ------ */ @@ -183,6 +206,14 @@ typedef struct tGPsdata { /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) +static void gp_update_cache(bGPdata *gpd) +{ + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } +} + static bool gp_stroke_added_check(tGPsdata *p) { return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); @@ -192,6 +223,9 @@ static void gp_stroke_added_enable(tGPsdata *p) { BLI_assert(p->gpf->strokes.last != NULL); p->flags |= GP_PAINTFLAG_STROKEADDED; + + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); } /* ------ */ @@ -206,30 +240,42 @@ static void gp_session_validatebuffer(tGPsdata *p); static bool gpencil_draw_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) { - /* check if Grease Pencil isn't already running */ - if (ED_gpencil_session_active() == 0) - return 1; - else - CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + ScrArea *sa = CTX_wm_area(C); + if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() == 0) + return 1; + else + CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + return 0; } + /* 3D Viewport */ else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + if (ED_gpencil_session_active() == 0) { + return 1; + } + else { + return 0; + } } } else { CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return 0; } - - return 0; } /* check if projecting strokes into 3d-geometry in the 3D-View */ static bool gpencil_project_check(tGPsdata *p) { bGPdata *gpd = p->gpd; - return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); } /* ******************************************* */ @@ -241,38 +287,39 @@ static bool gpencil_project_check(tGPsdata *p) static void gp_get_3d_reference(tGPsdata *p, float vec[3]) { View3D *v3d = p->sa->spacedata.first; - const float *fp = ED_view3d_cursor3d_get(p->scene, v3d)->location; - - /* the reference point used depends on the owner... */ -#if 0 /* XXX: disabled for now, since we can't draw relative to the owner yet */ + Object *ob = NULL; if (p->ownerPtr.type == &RNA_Object) { - Object *ob = (Object *)p->ownerPtr.data; - - /* active Object - * - use relative distance of 3D-cursor from object center - */ - sub_v3_v3v3(vec, fp, ob->loc); - } - else -#endif - { - /* use 3D-cursor */ - copy_v3_v3(vec, fp); + ob = (Object *)p->ownerPtr.data; } + ED_gp_get_drawing_reference(v3d, p->scene, ob, p->gpl, *p->align_flag, vec); } /* Stroke Editing ---------------------------- */ - /* check if the current mouse position is suitable for adding a new point */ static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) { + Brush *brush = p->brush; int dx = abs(mval[0] - pmval[0]); int dy = abs(mval[1] - pmval[1]); + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; /* if buffer is empty, just let this go through (i.e. so that dots will work) */ - if (p->gpd->sbuffer_size == 0) + if (p->gpd->runtime.sbuffer_size == 0) { return true; - + } + /* if lazy mouse, check minimum distance */ + else if (GPENCIL_LAZY_MODE(brush, p->shift)) { + brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; + if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) { + return true; + } + else { + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + copy_v2_v2_int(p->mval, p->mvalo); + return false; + } + } /* check if mouse moved at least certain distance on both axes (best case) * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand */ @@ -291,47 +338,17 @@ static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) return false; } -/* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ -static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) -{ - float plane_normal[3]; - float vn[3]; - - float ray[3]; - float rpoint[3]; - - /* normal vector for a plane locked to axis */ - zero_v3(plane_normal); - plane_normal[axis] = 1.0f; - - /* Reproject the points in the plane */ - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - - /* get a vector from the point with the current view direction of the viewport */ - ED_view3d_global_to_vector(rv3d, &pt->x, vn); - - /* calculate line extrem point to create a ray that cross the plane */ - mul_v3_fl(vn, -50.0f); - add_v3_v3v3(ray, &pt->x, vn); - - /* if the line never intersect, the point is not changed */ - if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { - copy_v3_v3(&pt->x, rpoint); - } - } -} - /* reproject stroke to plane locked to axis in 3d cursor location */ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) { bGPdata *gpd = p->gpd; + Object *obact = (Object *)p->ownerPtr.data; + float origin[3]; - float cursor[3]; RegionView3D *rv3d = p->ar->regiondata; /* verify the stroke mode is CURSOR 3d space mode */ - if ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { return; } if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { @@ -341,12 +358,9 @@ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) return; } - /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ - gp_get_3d_reference(p, cursor); - zero_v3(origin); - origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; - - gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); + /* get drawing origin */ + gp_get_3d_reference(p, origin); + ED_gp_project_stroke_to_plane(obact, rv3d, gps, origin, p->lock_axis - 1); } /* convert screen-coordinates to buffer-coordinates */ @@ -356,7 +370,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] bGPdata *gpd = p->gpd; /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ - if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it @@ -365,7 +379,8 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] else { float mval_prj[2]; float rvec[3], dvec[3]; - float mval_f[2] = {UNPACK2(mval)}; + float mval_f[2]; + copy_v2fl_v2i(mval_f, mval); float zfac; /* Current method just converts each point in screen-coordinates to @@ -390,42 +405,23 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] } } } - - /* 2d - on 'canvas' (assume that p->v2d is set) */ - else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { - UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); - mul_v3_m4v3(out, p->imat, out); - } - - /* 2d - relative to screen (viewport area) */ - else { - if (p->subrect == NULL) { /* normal 3D view */ - out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; - out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; - } - else { /* camera view, use subrect */ - out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; - out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; - } - } } /* apply jitter to stroke */ -static void gp_brush_jitter( - bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) +static void gp_brush_jitter(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) { float pressure = pt->pressure; float tmp_pressure = pt->pressure; - if (brush->draw_jitter > 0.0f) { - float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure); - tmp_pressure = curvef * brush->draw_sensitivity; + if (brush->gpencil_settings->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); + tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; } - const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ + const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */ 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 */ - if (gpd->sbuffer_size > 1) { + if (gpd->runtime.sbuffer_size > 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -451,18 +447,18 @@ static void gp_brush_jitter( } /* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ -static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2]) +static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2]) { float mvec[2]; - float sen = brush->draw_angle_factor; /* sensitivity */; + float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */; float fac; float mpressure; - float angle = brush->draw_angle; /* default angle of brush in radians */; + float angle = brush->gpencil_settings->draw_angle; /* default angle of brush in radians */; float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ /* Apply to first point (only if there are 2 points because before no data to do it ) */ - if (gpd->sbuffer_size == 1) { + if (gpd->runtime.sbuffer_size == 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -475,7 +471,7 @@ static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const } /* apply from second point */ - if (gpd->sbuffer_size >= 1) { + if (gpd->runtime.sbuffer_size >= 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -490,21 +486,83 @@ static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const } +/* Apply smooth to buffer while drawing +* to smooth point C, use 2 before (A, B) and current point (D): +* +* A----B-----C------D +* +* \param p Temp data +* \param inf Influence factor +* \param idx Index of the last point (need minimum 3 points in the array) +*/ +static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) +{ + bGPdata *gpd = p->gpd; + short num_points = gpd->runtime.sbuffer_size; + + /* Do nothing if not enough points to smooth out */ + if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + float steps = 4.0f; + if (idx < 4) { + steps--; + } + + tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; + tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; + tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; + tGPspoint *ptd = &points[idx - 1]; + + float sco[2] = { 0.0f }; + float a[2], b[2], c[2], d[2]; + const float average_fac = 1.0f / steps; + + /* Compute smoothed coordinate by taking the ones nearby */ + if (pta) { + copy_v2fl_v2i(a, &pta->x); + madd_v2_v2fl(sco, a, average_fac); + } + if (ptb) { + copy_v2fl_v2i(b, &ptb->x); + madd_v2_v2fl(sco, b, average_fac); + } + if (ptc) { + copy_v2fl_v2i(c, &ptc->x); + madd_v2_v2fl(sco, c, average_fac); + } + if (ptd) { + copy_v2fl_v2i(d, &ptd->x); + madd_v2_v2fl(sco, d, average_fac); + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v2_v2v2(c, c, sco, inf); + round_v2i_v2fl(&ptc->x, c); +} + /* add current stroke-point to buffer (returns whether point was successfully added) */ static short gp_stroke_addpoint( tGPsdata *p, const int mval[2], float pressure, double curtime) { bGPdata *gpd = p->gpd; - bGPDbrush *brush = p->brush; + Brush *brush = p->brush; tGPspoint *pt; ToolSettings *ts = p->scene->toolsettings; + Object *obact = (Object *)p->ownerPtr.data; + Depsgraph *depsgraph = p->depsgraph; \ + RegionView3D *rv3d = p->ar->regiondata; + View3D *v3d = p->sa->spacedata.first; + MaterialGPencilStyle *gp_style = p->material->gp_style; /* check painting mode */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only - i.e. only store start and end point in buffer */ - if (gpd->sbuffer_size == 0) { + if (gpd->runtime.sbuffer_size == 0) { /* first point in buffer (start point) */ - pt = (tGPspoint *)(gpd->sbuffer); + pt = (tGPspoint *)(gpd->runtime.sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -513,13 +571,13 @@ static short gp_stroke_addpoint( pt->time = (float)(curtime - p->inittime); /* increment buffer size */ - gpd->sbuffer_size++; + gpd->runtime.sbuffer_size++; } else { /* just reset the endpoint to the latest value * - assume that pointers for this are always valid... */ - pt = ((tGPspoint *)(gpd->sbuffer) + 1); + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -528,31 +586,35 @@ static short gp_stroke_addpoint( pt->time = (float)(curtime - p->inittime); /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ - gpd->sbuffer_size = 2; + gpd->runtime.sbuffer_size = 2; } + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + /* can keep carrying on this way :) */ return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ /* check if still room in buffer */ - if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) return GP_STROKEADD_OVERFLOW; /* get pointer to destination point */ - pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); /* store settings */ /* pressure */ - if (brush->flag & GP_BRUSH_USE_PRESSURE) { - float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); - pt->pressure = curvef * brush->draw_sensitivity; + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); + pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; } else { pt->pressure = 1.0f; } + /* Apply jitter to position */ - if (brush->draw_jitter > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->draw_jitter > 0.0f)) { int r_mval[2]; gp_brush_jitter(gpd, brush, pt, mval, r_mval, p->rng); copy_v2_v2_int(&pt->x, r_mval); @@ -561,42 +623,62 @@ static short gp_stroke_addpoint( copy_v2_v2_int(&pt->x, mval); } /* apply randomness to pressure */ - if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) { - float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); - float tmp_pressure = curvef * brush->draw_sensitivity; + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_press > 0.0f)) + { + float curvef = 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->draw_random_press * BLI_rng_get_float(p->rng); + pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); } else { - pt->pressure += tmp_pressure * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); } 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; + } + else { + pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; + } + 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->draw_angle_factor > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_angle_factor > 0.0f)) + { gp_brush_angle(gpd, brush, pt, mval); } /* color strength */ - if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { - float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure); - float tmp_pressure = curvef * brush->draw_sensitivity; + if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, pressure); + float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - pt->strength = tmp_pressure * brush->draw_strength; + pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; } else { - pt->strength = brush->draw_strength; + pt->strength = brush->gpencil_settings->draw_strength; } CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); /* apply randomness to color strength */ - if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_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->draw_random_press * BLI_rng_get_float(p->rng); + pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); } else { - pt->strength += pt->strength * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); } CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } @@ -604,11 +686,49 @@ static short gp_stroke_addpoint( /* point time */ pt->time = (float)(curtime - p->inittime); + /* point uv (only 3d view) */ + if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_size > 1)) { + float pixsize = gp_style->texture_pixsize / 1000000.0f; + tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 2; + bGPDspoint spt, spt2; + + /* get origin to reproject point */ + float origin[3]; + gp_get_3d_reference(p, origin); + /* reproject current */ + ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt); + ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &spt); + + /* reproject previous */ + ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2); + ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &spt2); + + p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + pt->uv_fac = p->totpixlen; + if ((gp_style) && (gp_style->sima)) { + pt->uv_fac /= gp_style->sima->gen_x; + } + } + else { + p->totpixlen = 0.0f; + pt->uv_fac = 0.0f; + } + /* increment counters */ - gpd->sbuffer_size++; + gpd->runtime.sbuffer_size++; + + /* 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, brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_size - s); + } + } + + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); /* check if another operation can still occur */ - if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) return GP_STROKEADD_FULL; else return GP_STROKEADD_NORMAL; @@ -617,7 +737,7 @@ static short gp_stroke_addpoint( bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->sbuffer); + pt = (tGPspoint *)(gpd->runtime.sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -632,14 +752,17 @@ static short gp_stroke_addpoint( if (gp_stroke_added_check(p)) { bGPDstroke *gps = p->gpf->strokes.last; bGPDspoint *pts; + MDeformVert *dvert; /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ - if (gpd->sbuffer_size == 0) { + if (gpd->runtime.sbuffer_size == 0) { gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); gps->totpoints++; } pts = &gps->points[gps->totpoints - 1]; + dvert = &gps->dvert[gps->totpoints - 1]; /* special case for poly lines: normally, * depth is needed only when creating new stroke from buffer, @@ -647,8 +770,6 @@ static short gp_stroke_addpoint( * so initialize depth buffer before converting coordinates */ if (gpencil_project_check(p)) { - View3D *v3d = p->sa->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->ar); ED_view3d_autodist_init( p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); @@ -656,25 +777,32 @@ static short gp_stroke_addpoint( /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pts); - } + 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; + + dvert->totweight = 0; + dvert->dw = NULL; + /* force fill recalc */ gps->flag |= GP_STROKE_RECALC_CACHES; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); } /* increment counters */ - if (gpd->sbuffer_size == 0) - gpd->sbuffer_size++; + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); return GP_STROKEADD_NORMAL; } @@ -690,9 +818,9 @@ static short gp_stroke_addpoint( static void gp_stroke_simplify(tGPsdata *p) { bGPdata *gpd = p->gpd; - tGPspoint *old_points = (tGPspoint *)gpd->sbuffer; - short num_points = gpd->sbuffer_size; - short flag = gpd->sbuffer_sflag; + tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; + short num_points = gpd->runtime.sbuffer_size; + short flag = gpd->runtime.sbuffer_sflag; short i, j; /* only simplify if simplification is enabled, and we're not doing a straight line */ @@ -707,9 +835,9 @@ static void gp_stroke_simplify(tGPsdata *p) * - firstly set sbuffer to NULL, so a new one is allocated * - secondly, reset flag after, as it gets cleared auto */ - gpd->sbuffer = NULL; + gpd->runtime.sbuffer = NULL; gp_session_validatebuffer(p); - gpd->sbuffer_sflag = flag; + gpd->runtime.sbuffer_sflag = flag; /* macro used in loop to get position of new point * - used due to the mixture of datatypes in use here @@ -766,8 +894,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; - bGPDbrush *brush = p->brush; + MDeformVert *dvert; + Brush *brush = p->brush; ToolSettings *ts = p->scene->toolsettings; + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -777,14 +908,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * - drawing straight-lines only requires the endpoints */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) - totelem = (gpd->sbuffer_size >= 2) ? 2 : gpd->sbuffer_size; + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; else - totelem = gpd->sbuffer_size; + totelem = gpd->runtime.sbuffer_size; /* exit with error if no valid points from this stroke */ if (totelem == 0) { if (G.debug & G_DEBUG) - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->sbuffer_size); + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); return; } @@ -793,6 +924,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * 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; } @@ -803,95 +937,94 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; - gps->thickness = brush->thickness; - gps->flag = gpd->sbuffer_sflag; + gps->thickness = brush->size; + 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_CACHES; /* allocate enough memory for a continuous array for storage points */ - int sublevel = brush->sublevel; - int new_totpoints = gps->totpoints; + const int subdivide = brush->gpencil_settings->draw_subdivide; + + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); - for (i = 0; i < sublevel; i++) { - new_totpoints += new_totpoints - 1; - } - gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); /* initialize triangle memory to dummy data */ gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); gps->flag |= GP_STROKE_RECALC_CACHES; gps->tot_triangles = 0; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); + dvert = gps->dvert + (gps->totpoints - totelem); /* copy points from the buffer to the stroke */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only -> only endpoints */ { /* first point */ - ptc = gpd->sbuffer; + ptc = gpd->runtime.sbuffer; /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pt); - } /* copy pressure and time */ pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + dvert->totweight = 0; + dvert->dw = NULL; + pt++; + dvert++; } if (totelem == 2) { /* last point if applicable */ - ptc = ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1); + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pt); - } - /* copy pressure and time */ pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + 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); } } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { /* first point */ - ptc = gpd->sbuffer; + ptc = gpd->runtime.sbuffer; /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pt); - } + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); /* copy pressure and time */ pt->pressure = ptc->pressure; pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + + dvert->totweight = 0; + dvert->dw = NULL; + } else { float *depth_arr = NULL; @@ -902,9 +1035,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) int interp_depth = 0; int found_depth = 0; - depth_arr = MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points"); + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); - for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) { + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { copy_v2_v2_int(mval, &ptc->x); if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr + i) == 0) && @@ -921,7 +1054,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) if (found_depth == false) { /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ - for (i = gpd->sbuffer_size - 1; i >= 0; i--) + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) depth_arr[i] = 0.9999f; } else { @@ -930,13 +1063,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) int first_valid = 0; int last_valid = 0; - for (i = 0; i < gpd->sbuffer_size; i++) { + for (i = 0; i < gpd->runtime.sbuffer_size; i++) { if (depth_arr[i] != FLT_MAX) break; } first_valid = i; - for (i = gpd->sbuffer_size - 1; i >= 0; i--) { + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { if (depth_arr[i] != FLT_MAX) break; } @@ -950,16 +1083,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } if (interp_depth) { - interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); } } } - pt = gps->points; /* convert all points (normal behavior) */ - for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); @@ -968,20 +1100,18 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + pt->uv_fac = ptc->uv_fac; + pt->uv_rot = ptc->uv_rot; } - /* subdivide the stroke */ - if (sublevel > 0) { - int totpoints = gps->totpoints; - for (i = 0; i < sublevel; i++) { - /* we're adding one new point between each pair of verts on each step */ - totpoints += totpoints - 1; - - gp_subdivide_stroke(gps, totpoints); - } + /* subdivide and smooth the stroke */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { + gp_subdivide_stroke(gps, subdivide); } /* apply randomness to stroke */ - if (brush->draw_random_sub > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_sub > 0.0f)) + { gp_randomize_stroke(gps, brush, p->rng); } @@ -989,34 +1119,43 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ - if (brush->draw_smoothfac > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + (brush->gpencil_settings->draw_smoothfac > 0.0f)) + { float reduce = 0.0f; - for (int r = 0; r < brush->draw_smoothlvl; ++r) { + for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { for (i = 0; i < gps->totpoints; i++) { - /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ - gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); + BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); } reduce += 0.25f; // reduce the factor } } + /* 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; i++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); + } + } + } - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent(gpl, gps); - } + /* 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); if (depth_arr) MEM_freeN(depth_arr); } - /* Save palette color */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - gps->palcolor = palcolor; - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); + + /* Save material index */ + gps->mat_nr = BKE_object_material_slot_find_index(p->ob, p->material) - 1; + + /* 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 @@ -1047,6 +1186,8 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) /* only erase stroke points that are visible */ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) { + Object *obact = (Object *)p->ownerPtr.data; + if ((p->sa->spacetype == SPACE_VIEW3D) && (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { @@ -1059,7 +1200,7 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons float diff_mat[4][4]; /* calculate difference matrix if parent object */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, gpl, diff_mat); if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); @@ -1092,6 +1233,24 @@ static float gp_stroke_eraser_calc_influence(tGPsdata *p, const int mval[2], con return fac; } +/* helper to free a stroke */ +static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) +{ + if (gps->points) { + MEM_freeN(gps->points); + } + + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + if (gps->triangles) + MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + gp_update_cache(gpd); +} + /* eraser tool - evaluation per stroke */ /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ static void gp_stroke_eraser_dostroke(tGPsdata *p, @@ -1099,46 +1258,58 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, const int mval[2], const int mvalo[2], const int radius, const rcti *rect) { + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; + Brush *eraser = p->eraser; bGPDspoint *pt1, *pt2; int pc1[2] = {0}; int pc2[2] = {0}; int i; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); if (gps->totpoints == 0) { /* just free stroke */ - if (gps->points) - MEM_freeN(gps->points); - if (gps->triangles) - MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + gp_free_stroke(p->gpd, gpf, gps); } else if (gps->totpoints == 1) { /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - if (gpl->parent == NULL) { - gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); - } - else { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); - } + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, 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])) { /* only check if point is inside */ if (len_v2v2_int(mval, pc1) <= radius) { /* free stroke */ - // XXX: pressure sensitive eraser should apply here too? - MEM_freeN(gps->points); - if (gps->triangles) - MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + gp_free_stroke(p->gpd, gpf, gps); + } + } + } + } + else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) || (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_STROKE)) { + for (i = 0; (i + 1) < gps->totpoints; i++) { + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + /* get points to work with */ + pt1 = gps->points + i; + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &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])) { + /* only check if point is inside */ + if (len_v2v2_int(mval, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + return; } } } @@ -1181,18 +1352,12 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) continue; - if (gpl->parent == NULL) { - gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); - } - else { - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + bGPDspoint npt; + gp_point_to_parent_space(pt1, 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_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); - } + gp_point_to_parent_space(pt2, 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 */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || @@ -1215,11 +1380,15 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; /* 2) Tag any point with overly low influence for removal in the next pass */ - if (pt1->pressure < cull_thresh) { + if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) + { pt1->flag |= GP_SPOINT_TAG; do_cull = true; } - if (pt2->pressure < cull_thresh) { + if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) + { pt2->flag |= GP_SPOINT_TAG; do_cull = true; } @@ -1230,8 +1399,9 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* Second Pass: Remove any points that are tagged */ if (do_cull) { - gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG); + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); } + gp_update_cache(p->gpd); } } @@ -1275,7 +1445,7 @@ static void gp_stroke_doeraser(tGPsdata *p) for (gps = gpf->strokes.first; gps; gps = gpn) { gpn = gps->next; /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { continue; } /* Not all strokes in the datablock may be valid in the current editor/context @@ -1295,107 +1465,178 @@ static void gp_stroke_doeraser(tGPsdata *p) static void gp_session_validatebuffer(tGPsdata *p) { bGPdata *gpd = p->gpd; + Brush *brush = p->brush; /* clear memory of buffer (or allocate it if starting a new session) */ - if (gpd->sbuffer) { + if (gpd->runtime.sbuffer) { /* printf("\t\tGP - reset sbuffer\n"); */ - memset(gpd->sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); } else { /* printf("\t\tGP - allocate sbuffer\n"); */ - gpd->sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); } /* reset indices */ - gpd->sbuffer_size = 0; + gpd->runtime.sbuffer_size = 0; /* reset flags */ - gpd->sbuffer_sflag = 0; + gpd->runtime.sbuffer_sflag = 0; /* reset inittime */ p->inittime = 0.0; + + /* reset lazy */ + if (brush) { + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; + } } -/* create a new palette color */ -static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) +/* helper to get default eraser and create one if no eraser brush */ +static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) { - bGPDpalettecolor *palcolor; + Brush *brush_dft = NULL; + Paint *paint = BKE_brush_get_gpencil_paint(ts); + Brush *brush_old = paint->brush; + for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { + if ((brush->ob_mode == OB_MODE_GPENCIL_PAINT) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) + { + /* save first eraser to use later if no default */ + if (brush_dft == NULL) { + brush_dft = brush; + } + /* found default */ + if(brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { + return brush; + } + } + } + /* if no default, but exist eraser brush, return this and set as default */ + if (brush_dft) { + brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; + return brush_dft; + } + /* create a new soft eraser brush */ + else { + brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); + brush_dft->size = 30.0f; + brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush_dft->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + /* reset current brush */ + BKE_paint_brush_set(paint, brush_old); - return palcolor; + return brush_dft; + } } /* initialize a drawing brush */ -static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) +static void gp_init_drawing_brush(bContext *C, tGPsdata *p) { - bGPDbrush *brush; + Brush *brush; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); /* if not exist, create a new one */ - if (BLI_listbase_is_empty(&ts->gp_brushes)) { + if (paint->brush == NULL) { /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); - brush = BKE_gpencil_brush_getactive(ts); + BKE_brush_gpencil_presets(C); + brush = BKE_brush_getactive_gpencil(ts); } else { /* Use the current */ - brush = BKE_gpencil_brush_getactive(ts); + brush = BKE_brush_getactive_gpencil(ts); } /* be sure curves are initializated */ - curvemapping_initialize(brush->cur_sensitivity); - curvemapping_initialize(brush->cur_strength); - curvemapping_initialize(brush->cur_jitter); + curvemapping_initialize(brush->gpencil_settings->curve_sensitivity); + curvemapping_initialize(brush->gpencil_settings->curve_strength); + curvemapping_initialize(brush->gpencil_settings->curve_jitter); /* asign to temp tGPsdata */ p->brush = brush; + if (brush->gpencil_settings->brush_type != GP_BRUSH_TYPE_ERASE) { + p->eraser = gp_get_default_eraser(p->bmain, ts); + } + else { + p->eraser = brush; + } + /* 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. + * + */ + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); } -/* initialize a paint palette brush and a default color if not exist */ -static void gp_init_palette(tGPsdata *p) +/* initialize a paint brush and a default color if not exist */ +static void gp_init_colors(tGPsdata *p) { - bGPdata *gpd; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; - gpd = p->gpd; + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; - /* if not exist, create a new palette */ - if (BLI_listbase_is_empty(&gpd->palettes)) { - /* create new palette */ - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); - /* now create a default color */ - palcolor = gp_create_new_color(palette); + /* use brush material */ + ma = BKE_gpencil_get_material_from_brush(brush); + + /* if no brush defaults, get material and color info + * NOTE: Ensures that everything we need will exist... + */ + if ((ma == NULL) || (ma->gp_style == NULL)) { + BKE_gpencil_material_ensure(p->bmain, p->ob); + + /* assign always the first material to the brush */ + p->material = give_current_material(p->ob, 1); + brush->gpencil_settings->material = p->material; } else { - /* Use the current palette and color */ - palette = BKE_gpencil_palette_getactive(gpd); - /* the palette needs one color */ - if (BLI_listbase_is_empty(&palette->colors)) { - palcolor = gp_create_new_color(palette); - } - else { - palcolor = BKE_gpencil_palettecolor_getactive(palette); - } - /* in some situations can be null, so use first */ - if (palcolor == NULL) { - BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); - palcolor = palette->colors.first; - } + p->material = ma; } - /* asign to temp tGPsdata */ - p->palettecolor = palcolor; + /* check if the material is already on object material slots and add it if missing */ + if (BKE_object_material_slot_find_index(p->ob, p->material) == 0) { + BKE_object_material_slot_add(p->bmain, p->ob); + assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_EXISTING); + } + + /* assign color information to temp tGPsdata */ + gp_style = p->material->gp_style; + if (gp_style) { + + /* set colors */ + copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_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; + } } /* (re)init new painting data */ -static bool gp_session_initdata(bContext *C, tGPsdata *p) +static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) { Main *bmain = CTX_data_main(C); bGPdata **gpd_ptr = NULL; ScrArea *curarea = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); ToolSettings *ts = CTX_data_tool_settings(C); + Object *obact = CTX_data_active_object(C); + View3D *v3d = curarea->spacedata.first; /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { @@ -1406,10 +1647,12 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) } /* pass on current scene and window */ + p->C = C; p->bmain = CTX_data_main(C); p->scene = CTX_data_scene(C); p->depsgraph = CTX_data_depsgraph(C); p->win = CTX_wm_window(C); + p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill"); unit_m4(p->imat); unit_m4(p->mat); @@ -1424,7 +1667,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* set current area * - must verify that region data is 3D-view (and not something else) */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ p->sa = curarea; p->ar = ar; p->align_flag = &ts->gpencil_v3d_align; @@ -1435,92 +1678,19 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); return 0; } - break; - } - case SPACE_NODE: - { - /* SpaceNode *snode = curarea->spacedata.first; */ - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - break; - } - case SPACE_SEQ: - { - SpaceSeq *sseq = curarea->spacedata.first; - - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_seq_align; - - /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); - return 0; + /* if active object doesn't exist or isn't a GP Object, create one */ + float *cur = ED_view3d_cursor3d_get(p->scene, v3d)->location; + if ((!obact) || (obact->type != OB_GPENCIL)) { + /* create new default object */ + obact = ED_add_gpencil_object(C, p->scene, cur); } + /* assign object after all checks to be sure we have one active */ + p->ob = obact; + break; } - case SPACE_IMAGE: - { - /* SpaceImage *sima = curarea->spacedata.first; */ - - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_ima_align; - break; - } - case SPACE_CLIP: - { - SpaceClip *sc = curarea->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip == NULL) { - p->status = GP_STATUS_ERROR; - return false; - } - - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - - invert_m4_m4(p->imat, sc->unistabmat); - - /* custom color for new layer */ - p->custom_color[0] = 1.0f; - p->custom_color[1] = 0.0f; - p->custom_color[2] = 0.5f; - p->custom_color[3] = 0.9f; - - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - int framenr = ED_space_clip_get_clip_frame_number(sc); - MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; - - if (marker) { - p->imat[3][0] -= marker->pos[0]; - p->imat[3][1] -= marker->pos[1]; - } - else { - p->status = GP_STATUS_ERROR; - return false; - } - } - - invert_m4_m4(p->mat, p->imat); - copy_m4_m4(p->gsc.mat, p->mat); - break; - } + /* unsupported views */ default: { @@ -1533,7 +1703,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* get gp-data */ gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); - if (gpd_ptr == NULL) { + if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; if (G.debug & G_DEBUG) printf("Error: Current context doesn't allow for any Grease Pencil data\n"); @@ -1555,16 +1725,18 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); + /* set brush and create a new one if null */ - gp_init_drawing_brush(ts, p); - /* set palette info and create a new one if null */ - gp_init_palette(p); - /* set palette colors */ - bGPDpalettecolor *palcolor = p->palettecolor; - bGPdata *pdata = p->gpd; - copy_v4_v4(pdata->scolor, palcolor->color); - copy_v4_v4(pdata->sfill, palcolor->fill); - pdata->sflag = palcolor->flag; + gp_init_drawing_brush(C, p); + + /* setup active color */ + if (curarea->spacetype == SPACE_VIEW3D) { + /* NOTE: This is only done for 3D view, as Materials aren't used for + * annotations in 2D editors + */ + gp_init_colors(p); + } + /* lock axis */ p->lock_axis = ts->gp_sculpt.lock_axis; @@ -1572,20 +1744,22 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) } /* init new painting session */ -static tGPsdata *gp_session_initpaint(bContext *C) +static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op) { tGPsdata *p = NULL; /* create new context data */ p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); - gp_session_initdata(C, p); + gp_session_initdata(C, op, p); +#if 0 /* radius for eraser circle is defined in userprefs now */ /* NOTE: we do this here, so that if we exit immediately, * erase size won't get lost */ p->radius = U.gp_eraser; +#endif /* Random generator, only init once. */ uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); @@ -1606,15 +1780,15 @@ static void gp_session_cleanup(tGPsdata *p) return; /* free stroke buffer */ - if (gpd->sbuffer) { + if (gpd->runtime.sbuffer) { /* printf("\t\tGP - free sbuffer\n"); */ - MEM_freeN(gpd->sbuffer); - gpd->sbuffer = NULL; + MEM_freeN(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; } /* clear flags */ - gpd->sbuffer_size = 0; - gpd->sbuffer_sflag = 0; + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; p->inittime = 0.0; } @@ -1632,11 +1806,12 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps { Scene *scene = p->scene; ToolSettings *ts = scene->toolsettings; + int cfra_eval = (int)DEG_get_ctime(p->depsgraph); /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_getactive(p->gpd); if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); if (p->custom_color[3]) copy_v3_v3(p->gpl->color, p->custom_color); @@ -1670,7 +1845,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps * -> 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); + gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_COPY); has_layer_to_erase = true; } @@ -1708,7 +1883,9 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps else add_frame_mode = GP_GETFRAME_ADD_NEW; - p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_getframe(p->gpl, cfra_eval, add_frame_mode); + /* set as dirty draw manager cache */ + gp_update_cache(p->gpd); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -1724,7 +1901,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* set 'eraser' for this stroke if using eraser */ p->paintmode = paintmode; if (p->paintmode == GP_PAINTMODE_ERASER) { - p->gpd->sbuffer_sflag |= GP_STROKE_ERASER; + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; /* check if we should respect depth while erasing */ if (p->sa->spacetype == SPACE_VIEW3D) { @@ -1735,7 +1912,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps } else { /* disable eraser flags - so that we can switch modes during a session */ - p->gpd->sbuffer_sflag &= ~GP_STROKE_ERASER; + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; if (p->sa->spacetype == SPACE_VIEW3D) { if (p->gpl->flag & GP_LAYER_NO_XRAY) { @@ -1744,10 +1921,16 @@ 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 */ p->flags |= GP_PAINTFLAG_FIRSTRUN; - /* when drawing in the camera view, in 2D space, set the subrect */ p->subrect = NULL; if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { @@ -1782,41 +1965,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps switch (p->sa->spacetype) { case SPACE_VIEW3D: { - p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - case SPACE_NODE: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - break; - } - case SPACE_SEQ: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - break; - } - case SPACE_IMAGE: - { - SpaceImage *sima = (SpaceImage *)p->sa->spacedata.first; - - /* only set these flags if the image editor doesn't have an image active, - * otherwise user will be confused by strokes not appearing after they're drawn - * - * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint! - */ - if (ELEM(NULL, sima, sima->image)) { - /* make strokes be drawn in screen space */ - p->gpd->sbuffer_sflag &= ~GP_STROKE_2DSPACE; - *(p->align_flag) &= ~GP_PROJECT_VIEWSPACE; - } - else { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - } - break; - } - case SPACE_CLIP: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; break; } } @@ -1839,7 +1988,7 @@ static void gp_paint_strokeend(tGPsdata *p) } /* check if doing eraser or not */ - if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) { + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { /* simplify stroke before transferring? */ gp_stroke_simplify(p); @@ -1920,10 +2069,12 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en p->erasercursor = NULL; } else if (enable && !p->erasercursor) { + ED_gpencil_toggle_brush_cursor(p->C, false, NULL); /* enable cursor */ - p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), - NULL, /* XXX */ - gpencil_draw_eraser, p); + p->erasercursor = WM_paint_cursor_activate( + CTX_wm_manager(C), + NULL, /* XXX */ + gpencil_draw_eraser, p); } } @@ -1943,13 +2094,26 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) static void gpencil_draw_exit(bContext *C, wmOperator *op) { tGPsdata *p = op->customdata; + bGPdata *gpd = CTX_data_gpencil_data(C); /* clear undo stack */ gpencil_undo_finish(); /* restore cursor to indicate end of drawing */ - WM_cursor_modal_restore(CTX_wm_window(C)); + if (p->sa->spacetype != SPACE_VIEW3D) { + WM_cursor_modal_restore(CTX_wm_window(C)); + } + else { + /* or restore paint if 3D view */ + if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { + WM_cursor_modal_set(p->win, CURSOR_STD); + } + /* drawing batch cache is dirty now */ + if (gpd) { + gp_update_cache(gpd); + } + } /* don't assume that operator data exists at all */ if (p) { /* check size of buffer before cleanup, to determine if anything happened here */ @@ -1962,11 +2126,16 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) * NOTE: Do this even when not in eraser mode, as eraser may * have been toggled at some point. */ - U.gp_eraser = p->radius; + if (p->eraser) { + p->eraser->size = p->radius; + } /* cleanup */ gp_paint_cleanup(p); gp_session_cleanup(p); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + + /* finally, free the temp data */ gp_session_free(p); } @@ -1986,9 +2155,18 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p; eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + + /* if mode is draw and the brush is eraser, cancel */ + if (paintmode != GP_PAINTMODE_ERASER) { + if ((brush) && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) { + return 0; + } + } /* check context */ - p = op->customdata = gp_session_initpaint(C); + p = op->customdata = gp_session_initpaint(C, op); if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { /* something wasn't set correctly in context */ gpencil_draw_exit(C, op); @@ -2009,6 +2187,8 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) p->keymodifier = -1; } + p->reports = op->reports; + /* everything is now setup ok */ return 1; } @@ -2019,10 +2199,15 @@ 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) { - if (p->paintmode == GP_PAINTMODE_ERASER) + Brush *brush = p->brush; + if ((p->paintmode == GP_PAINTMODE_ERASER) || + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) + { WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ - else - WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); + } + else { + WM_cursor_modal_set(p->win, CURSOR_STD); + } } /* update UI indicators of status, including cursor and header prints */ @@ -2030,11 +2215,21 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) { /* header prints */ switch (p->status) { - case GP_STATUS_PAINTING: - /* only print this for paint-sessions, otherwise it gets annoying */ - if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) - ED_workspace_status_text(C, IFACE_("Grease Pencil: Drawing/erasing stroke... Release to end stroke")); - break; + +#if 0 /* FIXME, this never runs! */ + switch (p->paintmode) { + case GP_PAINTMODE_DRAW_POLY: + /* Provide usage tips, since this is modal, and unintuitive without hints */ + ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + default: + /* Do nothing - the others are self explanatory, exit quickly once the mouse is released + * Showing any text would just be annoying as it would flicker. + */ + break; + } +#endif case GP_STATUS_IDLING: /* print status info */ @@ -2048,12 +2243,11 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) "ESC/Enter to end (or click outside this area)")); break; case GP_PAINTMODE_DRAW: - ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "E/ESC/Enter to end (or click outside this area)")); + ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); break; case GP_PAINTMODE_DRAW_POLY: ED_workspace_status_text(C, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " - "ESC/Enter to end (or click outside this area)")); + "Release Shift/ESC/Enter to end (or click outside this area)")); break; default: /* unhandled future cases */ @@ -2067,14 +2261,19 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* clear status string */ ED_workspace_status_text(C, NULL); break; + case GP_STATUS_PAINTING: + break; } } /* ------------------------------- */ /* create a new stroke point at the point indicated by the painting context */ -static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +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 */ @@ -2087,6 +2286,17 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph } /* 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_v2fl_v2i(now_mouse, p->mval); + copy_v2fl_v2i(last_mouse, p->mvalo); + interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); + round_v2i_v2fl(p->mval, now_mouse); + } + /* try to add point */ short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); @@ -2124,11 +2334,24 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph p->mvalo[1] = p->mval[1]; p->opressure = p->pressure; p->ocurtime = p->curtime; + + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 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_size > 0)) + { + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 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(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph) +static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, int x, int y) { tGPsdata *p = op->customdata; PointerRNA itemptr; @@ -2136,13 +2359,15 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg int tablet = 0; /* convert from window-space to area-space mouse coordinates + * add any x,y override position for fake events * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... */ - p->mval[0] = event->mval[0] + 1; - p->mval[1] = event->mval[1] + 1; + p->mval[0] = event->mval[0] + 1 - x; + p->mval[1] = event->mval[1] + 1 - y; + p->shift = event->shift; /* verify key status for straight lines */ - if ((event->ctrl > 0) || (event->alt > 0)) { + if ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false)) { if (p->straight[0] == 0) { int dx = abs(p->mval[0] - p->mvalo[0]); int dy = abs(p->mval[1] - p->mvalo[1]); @@ -2151,12 +2376,12 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg if (dx >= dy) { /* horizontal */ p->straight[0] = 1; - p->straight[1] = p->mval[1]; /* save y */ + p->straight[1] = (short)p->mval[1]; /* save y */ } else { /* vertical */ p->straight[0] = 2; - p->straight[1] = p->mval[0]; /* save x */ + p->straight[1] = (short)p->mval[0]; /* save x */ } } } @@ -2191,6 +2416,22 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg p->pressure = 1.0f; } + /* special eraser modes */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if (event->shift > 0) { + p->flags |= GP_PAINTFLAG_HARD_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_HARD_ERASER; + } + if (event->alt > 0) { + p->flags |= GP_PAINTFLAG_STROKE_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; + } + } + /* special exception for start of strokes (i.e. maybe for just a dot) */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { p->flags &= ~GP_PAINTFLAG_FIRSTRUN; @@ -2233,7 +2474,7 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg RNA_float_set(&itemptr, "time", p->curtime - p->inittime); /* apply the current latest drawing point */ - gpencil_draw_apply(op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph); /* force refresh */ ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ @@ -2251,7 +2492,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) /* try to initialize context data needed while drawing */ if (!gpencil_draw_init(C, op, NULL)) { - if (op->customdata) MEM_freeN(op->customdata); + MEM_SAFE_FREE(op->customdata); /* printf("\tGP - no valid data\n"); */ return OPERATOR_CANCELLED; } @@ -2298,7 +2539,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) } /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph); } RNA_END; @@ -2324,6 +2565,11 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (G.debug & G_DEBUG) printf("GPencil - Starting Drawing\n"); + /* support for tablets eraser pen */ + if (gpencil_is_tablet_eraser_active(event)) { + RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); + } + /* try to initialize context data needed while drawing */ if (!gpencil_draw_init(C, op, event)) { if (op->customdata) @@ -2344,6 +2590,9 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event 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)... @@ -2357,7 +2606,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { @@ -2366,9 +2615,29 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event op->flag |= OP_IS_MODAL_CURSOR_REGION; } + /* enable paint mode */ + if (p->sa->spacetype == SPACE_VIEW3D) { + Object *ob = CTX_data_active_object(C); + if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { + /* Just set paintmode flag... */ + p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; + /* disable other GP modes */ + p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + /* set workspace mode */ + ob->restore_mode = ob->mode; + ob->mode = OB_MODE_GPENCIL_PAINT; + /* redraw mode on screen */ + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + } + } + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* add a modal handler for this operator, so that we can then draw continuous strokes */ WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } @@ -2397,7 +2666,7 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) /* 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, p)) + if (gp_session_initdata(C, op, p)) gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); if (p->status != GP_STATUS_ERROR) { @@ -2448,6 +2717,76 @@ static void gpencil_move_last_stroke_to_back(bContext *C) BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); } +/* add events for missing mouse movements when the artist draw very fast */ +static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +{ + Brush *brush = p->brush; + if (brush->gpencil_settings->input_samples == 0) { + return; + } + RegionView3D *rv3d = p->ar->regiondata; + float defaultpixsize = rv3d->pixsize * 1000.0f; + int samples = (GP_MAX_INPUT_SAMPLES - brush->gpencil_settings->input_samples + 1); + float thickness = (float)brush->size; + + float pt[2], a[2], b[2]; + float vec[3]; + float scale = 1.0f; + + /* 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; + } + + /* 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; + } + float factor = ((thickness * dot_factor) / scale) * samples; + + copy_v2fl_v2i(a, p->mvalo); + b[0] = event->mval[0] + 1; + b[1] = event->mval[1] + 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, CTX_data_depsgraph(C), + (int)pt[0], (int)pt[1]); + } + 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, CTX_data_depsgraph(C), + (int)pt[0], (int)pt[1]); + } + } +} + /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -2488,10 +2827,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } - else if ((ELEM(event->type, p->keymodifier)) && (event->val == KM_RELEASE)) { - /* enable continuous if release D key in mid drawing */ - p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; - } else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { /* Add Blank Frame * - Since this operator is non-modal, we can just call it here, and keep going... @@ -2510,7 +2845,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* exit painting mode (and/or end current stroke) * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] */ - if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { + /* 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 */ @@ -2537,10 +2875,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) int sketch = 0; /* basically, this should be mouse-button up = end stroke - * BUT what happens next depends on whether we 'painting sessions' is enabled + * BUT, polyline drawing is an exception -- all knots should be added during one session */ - sketch |= GPENCIL_SKETCH_SESSIONS_ON(p->scene); - /* polyline drawing is also 'sketching' -- all knots should be added during one session */ sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); if (sketch) { @@ -2585,6 +2921,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } } + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); + p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2689,7 +3028,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { /* handle drawing event */ /* printf("\t\tGP - add point\n"); */ - gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + gpencil_add_missing_events(C, op, event, p); + + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); /* finish painting operation if anything went wrong just now */ if (p->status == GP_STATUS_ERROR) { @@ -2791,7 +3132,7 @@ void GPENCIL_OT_draw(wmOperatorType *ot) /* identifiers */ ot->name = "Grease Pencil Draw"; ot->idname = "GPENCIL_OT_draw"; - ot->description = "Make annotations on the active data"; + ot->description = "Draw a new stroke in the active Grease Pencil Object"; /* api callbacks */ ot->exec = gpencil_draw_exec; @@ -2811,5 +3152,11 @@ void GPENCIL_OT_draw(wmOperatorType *ot) /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "disable_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c new file mode 100644 index 00000000000..ef09c5c3f76 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -0,0 +1,712 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for creating new Grease Pencil primitives (boxes, circles, ...) + */ + +/** \file blender/editors/gpencil/gpencil_primitive.c + * \ingroup edgpencil + */ + + +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_material.h" +#include "BKE_paint.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 "ED_gpencil.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_space_api.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +#define MIN_EDGES 2 +#define MAX_EDGES 100 + +#define IDLE 0 +#define IN_PROGRESS 1 + +/* ************************************************ */ +/* Core/Shared Utilities */ + +/* Poll callback for primitive operators */ +static bool gpencil_primitive_add_poll(bContext *C) +{ + /* only 3D view */ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return 0; + } + + /* need data to create primitive */ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (gpd == NULL) { + return 0; + } + + /* only in edit and paint modes + * - paint as it's the "drawing/creation mode" + * - edit as this is more of an atomic editing operation + * (similar to copy/paste), and also for consistency + */ + if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) { + CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes"); + return 0; + } + + /* 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); + 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"); + return 0; + } + + return 1; +} + + +/* ****************** Primitive Interactive *********************** */ + +/* Helper: Create internal strokes primitives data */ +static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Brush *brush; + + /* if brush doesn't exist, create a new one */ + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + brush = BKE_brush_getactive_gpencil(ts); + } + else { + /* Use the current */ + brush = BKE_brush_getactive_gpencil(ts); + } + tgpi->brush = brush; + + /* if layer doesn't exist, create a new one */ + if (gpl == NULL) { + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + } + tgpi->gpl = gpl; + + /* create a new temporary frame */ + tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe"); + tgpi->gpf->framenum = tgpi->cframe = cfra_eval; + + /* create new temp stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke"); + gps->thickness = 2.0f; + gps->inittime = 0.0f; + + /* enable recalculation flag by default */ + gps->flag |= GP_STROKE_RECALC_CACHES; + /* the polygon must be closed, so enabled cyclic */ + gps->flag |= GP_STROKE_CYCLIC; + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_object_material_slot_find_index(tgpi->ob, tgpi->mat) - 1; + + /* allocate memory for storage points, but keep empty */ + gps->totpoints = 0; + gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert), "gp_stroke_weights"); + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* add to strokes */ + BLI_addtail(&tgpi->gpf->strokes, gps); +} + +/* ----------------------- */ +/* Drawing Callbacks */ + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) +{ + tGPDprimitive *tgpi = (tGPDprimitive *)arg; + ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW); +} + +/* ----------------------- */ + +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi) +{ + Scene *scene = tgpi->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + if (tgpi->type == GP_STROKE_BOX) { + BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square"), UI_MAX_DRAW_STR); + } + else if (tgpi->type == GP_STROKE_LINE) { + BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm"), UI_MAX_DRAW_STR); + } + else { + BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL to adjust edge number, Shift to square"), UI_MAX_DRAW_STR); + } + + if (tgpi->type == GP_STROKE_CIRCLE) { + if (hasNumInput(&tgpi->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&tgpi->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf(status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges, + tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges, + tgpi->bottom[0], tgpi->bottom[1]); + } + } + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf(status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str, + tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: (%d, %d)", msg_str, + tgpi->bottom[0], tgpi->bottom[1]); + } + } + ED_workspace_status_text(C, status_str); +} + +/* ----------------------- */ + +/* create a rectangle */ +static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + BLI_assert(tgpi->tot_edges == 4); + + points2D[0].x = tgpi->top[0]; + points2D[0].y = tgpi->top[1]; + + points2D[1].x = tgpi->bottom[0]; + points2D[1].y = tgpi->top[1]; + + points2D[2].x = tgpi->bottom[0]; + points2D[2].y = tgpi->bottom[1]; + + points2D[3].x = tgpi->top[0]; + points2D[3].y = tgpi->bottom[1]; +} + +/* create a line */ +static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + BLI_assert(tgpi->tot_edges == 2); + + points2D[0].x = tgpi->top[0]; + points2D[0].y = tgpi->top[1]; + + points2D[1].x = tgpi->bottom[0]; + points2D[1].y = tgpi->bottom[1]; +} + +/* create a circle */ +static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + const int totpoints = tgpi->tot_edges; + const float step = (2.0f * M_PI) / (float)(totpoints); + float center[2]; + float radius[2]; + float a = 0.0f; + + /* TODO: Use math-lib functions for these? */ + center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f); + center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f); + radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f)); + radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f)); + + for (int i = 0; i < totpoints; i++) { + tGPspoint *p2d = &points2D[i]; + + p2d->x = (int)(center[0] + cosf(a) * radius[0]); + p2d->y = (int)(center[1] + sinf(a) * radius[1]); + a += step; + } +} + +/* Helper: Update shape of the stroke */ +static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) +{ + ToolSettings *ts = tgpi->scene->toolsettings; + bGPdata *gpd = tgpi->gpd; + bGPDstroke *gps = tgpi->gpf->strokes.first; + + /* realloc points to new size */ + /* TODO: only do this if the size has changed? */ + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges); + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges); + gps->totpoints = tgpi->tot_edges; + + /* compute screen-space coordinates for points */ + tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D"); + switch (tgpi->type) { + case GP_STROKE_BOX: + gp_primitive_rectangle(tgpi, points2D); + break; + case GP_STROKE_LINE: + gp_primitive_line(tgpi, points2D); + break; + case GP_STROKE_CIRCLE: + gp_primitive_circle(tgpi, points2D); + break; + default: + break; + } + + /* convert screen-coordinates to 3D coordinates */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + tGPspoint *p2d = &points2D[i]; + + + /* convert screen-coordinates to 3D coordinates */ + gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->v3d, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x); + + pt->pressure = 1.0f; + pt->strength = tgpi->brush->gpencil_settings->draw_strength; + pt->time = 0.0f; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* if axis locked, reproject to plane locked */ + if (tgpi->lock_axis > GP_LOCKAXIS_NONE) { + bGPDspoint *tpt = gps->points; + float origin[3]; + ED_gp_get_drawing_reference(tgpi->v3d, tgpi->scene, tgpi->ob, tgpi->gpl, + ts->gpencil_v3d_align, origin); + + for (int i = 0; i < gps->totpoints; i++, tpt++) { + ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin, + ts->gp_sculpt.lock_axis - 1, + tpt); + } + } + + /* 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); + } + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* free temp data */ + MEM_SAFE_FREE(points2D); + + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +/* Update screen and stroke */ +static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi) +{ + /* update indicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + /* apply... */ + tgpi->type = RNA_enum_get(op->ptr, "type"); + tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); + /* update points position */ + gp_primitive_update_strokes(C, tgpi); +} + +/* ----------------------- */ + +/* Exit and free memory */ +static void gpencil_primitive_exit(bContext *C, wmOperator *op) +{ + tGPDprimitive *tgpi = op->customdata; + bGPdata *gpd = tgpi->gpd; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + + /* clear status message area */ + ED_workspace_status_text(C, NULL); + + /* finally, free memory used by temp data */ + BKE_gpencil_free_strokes(tgpi->gpf); + MEM_freeN(tgpi->gpf); + MEM_freeN(tgpi); + } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; +} + +/* Init new temporary primitive data */ +static void gpencil_primitive_init(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + /* create temporary operator data */ + tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data"); + op->customdata = tgpi; + + /* set current scene and window info */ + tgpi->scene = scene; + tgpi->ob = CTX_data_active_object(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->rv3d = tgpi->ar->regiondata; + tgpi->v3d = tgpi->sa->spacedata.first; + tgpi->depsgraph = CTX_data_depsgraph(C); + tgpi->win = CTX_wm_window(C); + + /* set current frame number */ + tgpi->cframe = cfra_eval; + + /* set GP datablock */ + tgpi->gpd = gpd; + + /* getcolor info */ + tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob); + + /* set parameters */ + tgpi->type = RNA_enum_get(op->ptr, "type"); + + /* if circle set default to 32 */ + if (tgpi->type == GP_STROKE_CIRCLE) { + RNA_int_set(op->ptr, "edges", 32); + } + else if(tgpi->type == GP_STROKE_BOX) { + RNA_int_set(op->ptr, "edges", 4); + } + else { /* LINE */ + RNA_int_set(op->ptr, "edges", 2); + } + + tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); + tgpi->flag = IDLE; + + tgpi->lock_axis = ts->gp_sculpt.lock_axis; + + /* set temp layer, frame and stroke */ + gp_primitive_set_initdata(C, tgpi); +} + +/* ----------------------- */ + +/* Invoke handler: Initialize the operator */ +static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + tGPDprimitive *tgpi = NULL; + + /* initialize operator runtime data */ + gpencil_primitive_init(C, op); + tgpi = op->customdata; + + /* if in tools region, wait till we get to the main (3d-space) + * region before allowing drawing to take place. + */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; + + /* Enable custom drawing handlers */ + tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_CROSSCURSOR); + + /* update sindicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* Helper to complete a primitive */ +static void gpencil_primitive_done(bContext *C, wmOperator *op, wmWindow *win, tGPDprimitive *tgpi) +{ + bGPDframe *gpf; + bGPDstroke *gps; + + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + + /* prepare stroke to get transfered */ + gps = tgpi->gpf->strokes.first; + if (gps) { + gps->thickness = tgpi->brush->size; + gps->flag |= GP_STROKE_RECALC_CACHES; + } + + /* transfer stroke from temporary buffer to the actual frame */ + BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes); + BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes)); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); +} + +/* Modal handler: Events handling during interactive part */ +static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDprimitive *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: + if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) { + /* start drawing primitive */ + /* TODO: Ignore if not in main region yet */ + tgpi->flag = IN_PROGRESS; + + tgpi->top[0] = event->mval[0]; + tgpi->top[1] = event->mval[1]; + + tgpi->bottom[0] = event->mval[0]; + tgpi->bottom[1] = event->mval[1]; + } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + /* stop drawing primitive */ + tgpi->flag = IDLE; + gpencil_primitive_done(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + else { + if (G.debug & G_DEBUG) { + printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); + } + } + break; + case RETKEY: /* confirm */ + { + tgpi->flag = IDLE; + gpencil_primitive_done(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: + { + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + case WHEELUPMOUSE: + { + if (tgpi->type == GP_STROKE_CIRCLE) { + tgpi->tot_edges = tgpi->tot_edges + 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case WHEELDOWNMOUSE: + { + if (tgpi->type == GP_STROKE_CIRCLE) { + tgpi->tot_edges = tgpi->tot_edges - 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update position of mouse */ + tgpi->bottom[0] = event->mval[0]; + tgpi->bottom[1] = event->mval[1]; + if (tgpi->flag == IDLE) { + tgpi->top[0] = event->mval[0]; + tgpi->top[1] = event->mval[1]; + } + /* Keep square if shift key */ + if (event->shift) { + tgpi->bottom[1] = tgpi->top[1] - (tgpi->bottom[0] - tgpi->top[0]); + } + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + default: + { + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + float value; + + /* Grab data from numeric input, and store this new value (the user see an int) */ + value = tgpi->tot_edges; + applyNumInput(&tgpi->num, &value); + tgpi->tot_edges = value; + + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; +} + +/* Cancel handler */ +static void gpencil_primitive_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_primitive_exit(C, op); +} + +void GPENCIL_OT_primitive(wmOperatorType *ot) +{ + static EnumPropertyItem primitive_type[] = { + { GP_STROKE_BOX, "BOX", 0, "Box", "" }, + { GP_STROKE_LINE, "LINE", 0, "Line", "" }, + { GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", "" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Grease Pencil Shapes"; + ot->idname = "GPENCIL_OT_primitive"; + ot->description = "Create predefined grease pencil stroke shapes"; + + /* callbacks */ + ot->invoke = gpencil_primitive_invoke; + ot->modal = gpencil_primitive_modal; + ot->cancel = gpencil_primitive_cancel; + ot->poll = gpencil_primitive_add_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES); + RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape"); +} + +/* *************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index dd556e99264..9386bfb3333 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -43,6 +43,7 @@ #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_object_types.h" @@ -62,6 +63,9 @@ #include "ED_gpencil.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ********************************************** */ @@ -71,8 +75,8 @@ static bool gpencil_select_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - /* we just need some visible strokes, and to be in editmode */ - if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { + /* 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? */ if (gpd->layers.first) return true; @@ -94,6 +98,11 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + /* for "toggle", test for existing selected strokes */ if (action == SEL_TOGGLE) { action = SEL_SELECT; @@ -180,6 +189,11 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -213,6 +227,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + /* select all points in selected strokes */ CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { @@ -228,6 +247,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -247,6 +271,86 @@ void GPENCIL_OT_select_linked(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************************************** */ +/* Select Alternate */ + +static int gpencil_select_alternate_exec(bContext *C, wmOperator *op) +{ + const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* select all points in selected strokes */ + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) { + bGPDspoint *pt; + int row = 0; + int start = 0; + if (unselect_ends) { + start = 1; + } + + for (int i = start; i < gps->totpoints; i++) { + pt = &gps->points[i]; + if ((row % 2) == 0) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + row++; + } + + /* unselect start and end points */ + if (unselect_ends) { + pt = &gps->points[0]; + pt->flag &= ~GP_SPOINT_SELECT; + + pt = &gps->points[gps->totpoints - 1]; + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_alternate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Alternated"; + ot->idname = "GPENCIL_OT_select_alternate"; + ot->description = "Select alternative points in same strokes as already selected points"; + + /* callbacks */ + ot->exec = gpencil_select_alternate_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "unselect_ends", true, "Unselect Ends", "Do not select the first and last point of the stroke"); +} + /* ********************************************** */ /* Select Grouped */ @@ -266,11 +370,12 @@ typedef enum eGP_SelectGrouped { /* On each visible layer, check for selected strokes - if found, select all others */ static void gp_select_same_layer(bContext *C) { - Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); bGPDstroke *gps; bool found = false; @@ -309,10 +414,7 @@ static void gp_select_same_layer(bContext *C) /* Select all strokes with same colors as selected ones */ static void gp_select_same_color(bContext *C) { - /* First, build set containing all the colors of selected strokes - * - We use the palette names, so that we can select all strokes with one - * (potentially missing) color, and remap them to something else - */ + /* First, build set containing all the colors of selected strokes */ GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) @@ -321,7 +423,7 @@ static void gp_select_same_color(bContext *C) /* add instead of insert here, otherwise the uniqueness check gets skipped, * and we get many duplicate entries... */ - BLI_gset_add(selected_colors, gps->colorname); + BLI_gset_add(selected_colors, &gps->mat_nr); } } CTX_DATA_END; @@ -329,7 +431,7 @@ static void gp_select_same_color(bContext *C) /* Second, select any visible stroke that uses these colors */ CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { - if (BLI_gset_haskey(selected_colors, gps->colorname)) { + if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) { /* select this stroke */ bGPDspoint *pt; int i; @@ -342,6 +444,11 @@ static void gp_select_same_color(bContext *C) } } CTX_DATA_END; + + /* free memomy */ + if (selected_colors != NULL) { + BLI_gset_free(selected_colors, NULL); + } } @@ -350,6 +457,11 @@ static void gp_select_same_color(bContext *C) static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) { eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } switch (mode) { case GP_SEL_SAME_LAYER: @@ -365,6 +477,11 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -399,6 +516,12 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) static int gpencil_select_first_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -429,6 +552,11 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -459,6 +587,12 @@ void GPENCIL_OT_select_first(wmOperatorType *ot) static int gpencil_select_last_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -489,6 +623,11 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -519,6 +658,12 @@ void GPENCIL_OT_select_last(wmOperatorType *ot) static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { if (gps->flag & GP_STROKE_SELECT) { @@ -565,6 +710,11 @@ static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -589,6 +739,12 @@ void GPENCIL_OT_select_more(wmOperatorType *ot) static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { if (gps->flag & GP_STROKE_SELECT) { @@ -636,6 +792,11 @@ static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -665,7 +826,7 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) static bool gp_stroke_do_circle_sel( bGPDstroke *gps, GP_SpaceConversion *gsc, const int mx, const int my, const int radius, - const bool select, rcti *rect, const bool parented, float diff_mat[4][4]) + const bool select, rcti *rect, float diff_mat[4][4]) { bGPDspoint *pt1, *pt2; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -673,14 +834,9 @@ static bool gp_stroke_do_circle_sel( bool changed = false; if (gps->totpoints == 1) { - if (!parented) { - gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); - } - else { - bGPDspoint pt_temp; - gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); - gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); - } + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { @@ -708,18 +864,12 @@ static bool gp_stroke_do_circle_sel( /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - if (!parented) { - gp_point_to_xy(gsc, gps, pt1, &x0, &y0); - gp_point_to_xy(gsc, gps, pt2, &x1, &y1); - } - else { - bGPDspoint npt; - gp_point_to_parent_space(pt1, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &x0, &y0); + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x0, &y0); - gp_point_to_parent_space(pt2, diff_mat, &npt); - gp_point_to_xy(gsc, gps, &npt, &x1, &y1); - } + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x1, &y1); /* check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || @@ -763,6 +913,12 @@ static bool gp_stroke_do_circle_sel( static int gpencil_circle_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + ScrArea *sa = CTX_wm_area(C); const int mx = RNA_int_get(op->ptr, "x"); @@ -798,13 +954,17 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { changed |= gp_stroke_do_circle_sel( - gps, &gsc, mx, my, radius, select, &rect, - (gpl->parent != NULL), diff_mat); + gps, &gsc, mx, my, radius, select, &rect, diff_mat); } GP_EDITABLE_STROKES_END; /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -837,10 +997,11 @@ void GPENCIL_OT_select_circle(wmOperatorType *ot) static int gpencil_border_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); ScrArea *sa = CTX_wm_area(C); const bool select = !RNA_boolean_get(op->ptr, "deselect"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0); GP_SpaceConversion gsc = {NULL}; rcti rect = {0}; @@ -888,14 +1049,9 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) int x0, y0; /* convert point coords to screenspace */ - if (gpl->parent == NULL) { - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - } - else { - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); - } + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); /* test if in selection rect */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) { @@ -915,8 +1071,20 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) } GP_EDITABLE_STROKES_END; + /* if paint mode,delete selected points */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + gp_delete_selected_point_wrap(C); + changed = true; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -950,10 +1118,11 @@ void GPENCIL_OT_select_border(wmOperatorType *ot) static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); GP_SpaceConversion gsc = {NULL}; rcti rect = {0}; - const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0); const bool select = !RNA_boolean_get(op->ptr, "deselect"); int mcords_tot; @@ -997,14 +1166,9 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) int x0, y0; /* convert point coords to screenspace */ - if (gpl->parent == NULL) { - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - } - else { - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); - } + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); /* test if in lasso boundbox + within the lasso noose */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) && BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) @@ -1028,8 +1192,20 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) /* cleanup */ MEM_freeN((void *)mcords); + /* if paint mode,delete selected points */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + gp_delete_selected_point_wrap(C); + changed = true; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -1061,6 +1237,7 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot) static int gpencil_select_exec(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */ const float radius = 0.75f * U.widget_unit; @@ -1102,14 +1279,9 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - if (gpl->parent == NULL) { - gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); - } - else { - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); - } + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { @@ -1197,6 +1369,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* updates */ if (hit_point != NULL) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index d35df8bc380..708d8f37e58 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "DNA_listBase.h" #include "DNA_windowmanager_types.h" @@ -51,6 +52,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "DEG_depsgraph.h" + #include "gpencil_intern.h" typedef struct bGPundonode { @@ -111,6 +114,9 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) } } } + /* drawing batch cache is dirty now */ + DEG_id_tag_update(&new_gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + new_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; } WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 8b65855f7c4..7262c537321 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -40,7 +40,9 @@ #include "BLT_translation.h" #include "BLI_rand.h" +#include "DNA_meshdata_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -48,9 +50,13 @@ #include "DNA_view3d_types.h" #include "BKE_action.h" +#include "BKE_main.h" +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_gpencil.h" -#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_material.h" #include "BKE_tracking.h" #include "WM_api.h" @@ -65,8 +71,14 @@ #include "ED_gpencil.h" #include "ED_clip.h" #include "ED_view3d.h" +#include "ED_object.h" +#include "ED_screen.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" @@ -76,7 +88,7 @@ /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, * when context info is not available. */ -bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) +bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob, PointerRNA *r_ptr) { /* if there's an active area, check if the particular editor may * have defined any special Grease Pencil context for editing... @@ -85,26 +97,37 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr SpaceLink *sl = sa->spacedata.first; switch (sa->spacetype) { - case SPACE_VIEW3D: /* 3D-View */ + /* XXX: Should we reduce reliance on context.gpencil_data for these cases? */ + case SPACE_BUTS: /* properties */ + case SPACE_INFO: /* header info (needed after workspaces merge) */ { - BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, - GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); - - if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { - /* legacy behaviour for usage with old addons requiring object-linked to objects */ - - /* just in case no active/selected object... */ - if (ob && (ob->flag & SELECT)) { - /* for now, as long as there's an object, default to using that in 3D-View */ - if (ptr) RNA_id_pointer_create(&ob->id, ptr); - return &ob->gpd; - } - /* else: defaults to scene... */ + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; } else { - if (ptr) RNA_id_pointer_create(&scene->id, ptr); + return NULL; + } + + break; + } + + case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ + case SPACE_VIEW3D: /* 3D-View */ + { + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; + } + else { + /* Annotations */ + /* XXX: */ + if (r_ptr) RNA_id_pointer_create(&scene->id, r_ptr); return &scene->gpd; } + break; } case SPACE_NODE: /* Nodes Editor */ @@ -114,7 +137,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* return the GP data for the active node block/node */ if (snode && snode->nodetree) { /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ - if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr); + if (r_ptr) RNA_id_pointer_create(&snode->nodetree->id, r_ptr); return &snode->nodetree->gpd; } @@ -127,7 +150,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* for now, Grease Pencil data is associated with the space (actually preview region only) */ /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr); + if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); return &sseq->gpd; } case SPACE_IMAGE: /* Image/UV Editor */ @@ -136,7 +159,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* for now, Grease Pencil data is associated with the space... */ /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr); + if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); return &sima->gpd; } case SPACE_CLIP: /* Nodes Editor */ @@ -151,15 +174,11 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr if (!track) return NULL; - if (ptr) - RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr); - + if (r_ptr) RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, r_ptr); return &track->gpd; } else { - if (ptr) - RNA_id_pointer_create(&clip->id, ptr); - + if (r_ptr) RNA_id_pointer_create(&clip->id, r_ptr); return &clip->gpd; } } @@ -170,79 +189,102 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr } } - /* just fall back on the scene's GP data */ - if (ptr) RNA_id_pointer_create((ID *)scene, ptr); - return (scene) ? &scene->gpd : NULL; + return NULL; } /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ -bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) +bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr) { ID *screen_id = (ID *)CTX_wm_screen(C); Scene *scene = CTX_data_scene(C); ScrArea *sa = CTX_wm_area(C); Object *ob = CTX_data_active_object(C); - return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); + return ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, r_ptr); } /* -------------------------------------------------------- */ /* Get the active Grease Pencil datablock, when context is not available */ -bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) +bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } -/* Get the active Grease Pencil datablock */ +/** + * Get the active Grease Pencil datablock + * \note This is the original (bmain) copy of the datablock, stored in files. + * Do not use for reading evaluated copies of GP Objects data + */ bGPdata *ED_gpencil_data_get_active(const bContext *C) { bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } +/** + * Get the evaluated copy of the active Grease Pencil datablock (where applicable) + * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock + * (i.e. a copy of the active GP datablock for the active object, where modifiers have been + * applied). This is needed to correctly work with "Copy-on-Write" + * - For all other editors (i.e. "GP Annotations"), this just gives the active datablock + * like for ED_gpencil_data_get_active() + */ +bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) +{ + ID *screen_id = (ID *)CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + + const Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + /* if (ob && ob->type == OB_GPENCIL) BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); */ + return ED_gpencil_data_get_active_direct(screen_id, sa, scene_eval, ob_eval); +} + +/* -------------------------------------------------------- */ + +/** + * Utility to check whether the r_ptr output of ED_gpencil_data_get_pointers() + * is for annotation usage. + */ +bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr) +{ + /* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data. + * Otherwise, the GP datablock is being used for annotations (i.e. everywhere else) + */ + return ((owner_ptr) && (owner_ptr->type != &RNA_Object)); +} + /* -------------------------------------------------------- */ // XXX: this should be removed... We really shouldn't duplicate logic like this! -bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, ViewLayer *view_layer) +bGPdata *ED_gpencil_data_get_active_v3d(ViewLayer *view_layer) { Base *base = view_layer->basact; bGPdata *gpd = NULL; + /* We have to make sure active object is actually visible and selected, else we must use default scene gpd, * to be consistent with ED_gpencil_data_get_active's behavior. */ - if (base && TESTBASE(base)) { - gpd = base->object->gpd; + if (base->object->type == OB_GPENCIL) + gpd = base->object->data; } - return gpd ? gpd : scene->gpd; + return gpd ? gpd : NULL; } /* ******************************************************** */ /* Keyframe Indicator Checks */ /* Check whether there's an active GP keyframe on the current frame */ -bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) +bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) { - /* just check both for now... */ - // XXX: this could get confusing (e.g. if only on the object, but other places don't show this) - if (scene->gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd); - if (gpl) { - if (gpl->actframe) { - // XXX: assumes that frame has been fetched already - return (gpl->actframe->framenum == cfra); - } - else { - /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ - } - } - } - - if (ob && ob->gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd); + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -281,28 +323,13 @@ bool gp_active_layer_poll(bContext *C) bool gp_active_brush_poll(bContext *C) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - return (brush != NULL); -} - -/* poll callback for checking if there is an active palette */ -bool gp_active_palette_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - - return (palette != NULL); -} - -/* poll callback for checking if there is an active palette color */ -bool gp_active_palettecolor_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - - return (palcolor != NULL); + Paint *paint = &ts->gp_paint->paint; + if (paint) { + return (paint->brush != NULL); + } + else { + return false; + } } /* ******************************************************** */ @@ -360,7 +387,7 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( /* Create new layer */ /* TODO: have some way of specifying that we don't want this? */ { - /* active Keying Set */ + /* "New Layer" entry */ item_tmp.identifier = "__CREATE__"; item_tmp.name = "New Layer"; item_tmp.value = -1; @@ -392,7 +419,6 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( } - /* ******************************************************** */ /* Brush Tool Core */ @@ -426,7 +452,7 @@ bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), /* Stroke Validity Testing */ /* Check whether given stroke can be edited given the supplied context */ -// XXX: do we need additional flags for screenspace vs dataspace? +/* TODO: do we need additional flags for screenspace vs dataspace? */ bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) { /* sanity check */ @@ -436,7 +462,7 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) /* filter stroke types by flags + spacetype */ if (gps->flag & GP_STROKE_3DSPACE) { /* 3D strokes - only in 3D view */ - return (sa->spacetype == SPACE_VIEW3D); + return ((sa->spacetype == SPACE_VIEW3D) || (sa->spacetype == SPACE_BUTS)); } else if (gps->flag & GP_STROKE_2DIMAGE) { /* Special "image" strokes - only in Image Editor */ @@ -460,59 +486,21 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) } /* Check whether given stroke can be edited for the current color */ -bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) +bool ED_gpencil_stroke_color_use(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps) { /* check if the color is editable */ - bGPDpalettecolor *palcolor = gps->palcolor; - if (palcolor != NULL) { - if (palcolor->flag & PC_COLOR_HIDE) + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + if (gp_style != NULL) { + if (gp_style->flag & GP_STYLE_COLOR_HIDE) return false; - if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) return false; } return true; } -/* Get palette color or create a new one */ -bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps) -{ - bGPDpalette *palette; - bGPDpalettecolor *palcolor; - - if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0)) - return gps->palcolor; - - /* get palette */ - palette = BKE_gpencil_palette_getactive(gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); - } - /* get color */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); - if (palcolor == NULL) { - if (gps->palcolor == NULL) { - palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); - /* set to a different color */ - ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f); - } - else { - palcolor = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true); - /* set old color and attributes */ - bGPDpalettecolor *gpscolor = gps->palcolor; - copy_v4_v4(palcolor->color, gpscolor->color); - copy_v4_v4(palcolor->fill, gpscolor->fill); - palcolor->flag = gpscolor->flag; - } - } - - /* clear flag and set pointer */ - gps->flag &= ~GP_STROKE_RECALC_COLOR; - gps->palcolor = palcolor; - - return palcolor; -} - /* ******************************************************** */ /* Space Conversion */ @@ -573,9 +561,9 @@ void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint * } /** - * Change points position relative to parent object + * Change position relative to parent object */ -void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) +void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -585,7 +573,7 @@ void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) float inverse_diff_mat[4][4]; float fpt[3]; - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); for (i = 0; i < gps->totpoints; i++) { @@ -598,14 +586,14 @@ void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) /** * Change point position relative to parent object */ -void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) +void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt) { /* undo matrix */ float diff_mat[4][4]; float inverse_diff_mat[4][4]; float fpt[3]; - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); @@ -770,194 +758,259 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen } /** - * Apply smooth to stroke point - * \param gps Stroke to smooth - * \param i Point index - * \param inf Amount of smoothing to apply - * \param affect_pressure Apply smoothing to pressure values too? + * Convert tGPspoint (temporary 2D/screenspace point data used by GP modal operators) + * to 3D coordinates. + * + * \param point2D: The screenspace 2D point data to convert + * \param depth: Depth array (via ED_view3d_autodist_depth()) + * \param[out] r_out: The resulting 2D point data */ -bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) +void gp_stroke_convertcoords_tpoint( + Scene *scene, ARegion *ar, View3D *v3d, + Object *ob, bGPDlayer *gpl, + const tGPspoint *point2D, float *depth, + float r_out[3]) { - bGPDspoint *pt = &gps->points[i]; - float pressure = 0.0f; - float sco[3] = {0.0f}; + ToolSettings *ts = scene->toolsettings; + const int mval[2] = {point2D->x, point2D->y}; - /* Do nothing if not enough points to smooth out */ - if (gps->totpoints <= 2) { - return false; + if ((depth != NULL) && (ED_view3d_autodist_simple(ar, mval, r_out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ } + else { + float mval_f[2] = {(float)point2D->x, (float)point2D->y}; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; - /* Only affect endpoints by a fraction of the normal strength, - * to prevent the stroke from shrinking too much - */ - if ((i == 0) || (i == gps->totpoints - 1)) { - inf *= 0.1f; - } + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. + */ + ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, rvec); + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); - /* Compute smoothed coordinate by taking the ones nearby */ - /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ - { - // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) - const int steps = 2; - const float average_fac = 1.0f / (float)(steps * 2 + 1); - int step; - - /* add the point itself */ - madd_v3_v3fl(sco, &pt->x, average_fac); - - if (affect_pressure) { - pressure += pt->pressure * average_fac; + if (ED_view3d_project_float_global(ar, 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(ar, mval_f, dvec, zfac); + sub_v3_v3v3(r_out, rvec, dvec); } + else { + zero_v3(r_out); + } + } +} - /* n-steps before/after current point */ - // XXX: review how the endpoints are treated by this algorithm - // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight - for (step = 1; step <= steps; step++) { - bGPDspoint *pt1, *pt2; - int before = i - step; - int after = i + step; +/** + * Get drawing reference point for conversion or projection of the stroke + * \param[out] r_vec : Reference point found + */ +void ED_gp_get_drawing_reference( + View3D *v3d, Scene *scene, Object *ob, bGPDlayer *UNUSED(gpl), + char align_flag, float r_vec[3]) +{ + const float *fp = ED_view3d_cursor3d_get(scene, v3d)->location; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - - pt1 = &gps->points[before]; - pt2 = &gps->points[after]; - - /* add both these points to the average-sum (s += p[i]/n) */ - madd_v3_v3fl(sco, &pt1->x, average_fac); - madd_v3_v3fl(sco, &pt2->x, average_fac); - -#if 0 - /* XXX: Disabled because get weird result */ - /* do pressure too? */ - if (affect_pressure) { - pressure += pt1->pressure * average_fac; - pressure += pt2->pressure * average_fac; + /* if using a gpencil object at cursor mode, can use the location of the object */ + if (align_flag & GP_PROJECT_VIEWSPACE) { + if (ob && (ob->type == OB_GPENCIL)) { + /* fallback (no strokes) - use cursor or object location */ + if (align_flag & GP_PROJECT_CURSOR) { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); } -#endif + else { + /* use object location */ + copy_v3_v3(r_vec, ob->obmat[3]); + } + } + } + else { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); + } +} + + +/** + * Reproject all points of the stroke to a plane locked to axis to avoid stroke offset + */ +void ED_gp_project_stroke_to_plane(Object *ob, RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) +{ + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + mul_mat3_m4_v3(ob->obmat, plane_normal); } } - /* Based on influence factor, blend between original and optimal smoothed coordinate */ - interp_v3_v3v3(&pt->x, &pt->x, sco, inf); + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; -#if 0 - /* XXX: Disabled because get weird result */ - if (affect_pressure) { - pt->pressure = pressure; + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extreme point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } } -#endif - - return true; } /** -* Apply smooth for strength to stroke point -* \param gps Stroke to smooth -* \param i Point index -* \param inf Amount of smoothing to apply -*/ -bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf) + * Reproject given point to a plane locked to axis to avoid stroke offset + * \param[in, out] pt : Point to affect + */ +void ED_gp_project_point_to_plane(Object *ob, RegionView3D *rv3d, const float origin[3], const int axis, bGPDspoint *pt) { - bGPDspoint *ptb = &gps->points[i]; + float plane_normal[3]; + float vn[3]; - /* Do nothing if not enough points */ - if (gps->totpoints <= 2) { - return false; + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + mul_mat3_m4_v3(ob->obmat, plane_normal); + } } - /* Compute theoretical optimal value using distances */ - bGPDspoint *pta, *ptc; - int before = i - 1; - int after = i + 1; - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); + /* Reproject the points in the plane */ + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); - pta = &gps->points[before]; - ptc = &gps->points[after]; + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); - /* the optimal value is the corresponding to the interpolation of the strength - * at the distance of point b - */ - const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); - const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; - - /* Based on influence factor, blend between original and optimal */ - ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; - - return true; -} - -/** -* Apply smooth for thickness to stroke point (use pressure) -* \param gps Stroke to smooth -* \param i Point index -* \param inf Amount of smoothing to apply -*/ -bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf) -{ - bGPDspoint *ptb = &gps->points[i]; - - /* Do nothing if not enough points */ - if (gps->totpoints <= 2) { - return false; + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); } - - /* Compute theoretical optimal value using distances */ - bGPDspoint *pta, *ptc; - int before = i - 1; - int after = i + 1; - - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - - pta = &gps->points[before]; - ptc = &gps->points[after]; - - /* the optimal value is the corresponding to the interpolation of the pressure - * at the distance of point b - */ - float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); - float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure; - - /* Based on influence factor, blend between original and optimal */ - ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; - - return true; } +/* ******************************************************** */ +/* Stroke Operations */ +// XXX: Check if these functions duplicate stuff in blenkernel, and/or whether we should just deduplicate + /** * Subdivide a stroke once, by adding a point half way between each pair of existing points * \param gps Stroke data - * \param new_totpoints Total number of points (after subdividing) + * \param subdivide Number of times to subdivide */ -void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) +void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) { - /* Move points towards end of enlarged points array to leave space for new points */ - int y = 1; - for (int i = gps->totpoints - 1; i > 0; i--) { - gps->points[new_totpoints - y] = gps->points[i]; - y += 2; + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + int i2; + + /* loop as many times as levels */ + for (int s = 0; s < subdivide; s++) { + totnewpoints = gps->totpoints - 1; + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* resize the points arrys */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* move points from last to first to new place */ + i2 = gps->totpoints - 1; + for (int i = oldtotpoints - 1; i > 0; i--) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + + i2 -= 2; + } + /* interpolate mid points */ + i2 = 1; + for (int i = 0; i < oldtotpoints - 1; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + /* add a half way point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); + pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + + i2 += 2; + } + + MEM_SAFE_FREE(temp_points); + + /* move points to smooth stroke */ + /* duplicate points in a temp area with the new subdivide data */ + temp_points = MEM_dupallocN(gps->points); + + /* extreme points are not changed */ + for (int i = 0; i < gps->totpoints - 2; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i + 1]; + + /* move point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); } - - /* Create interpolated points */ - for (int i = 0; i < new_totpoints - 1; i += 2) { - bGPDspoint *prev = &gps->points[i]; - bGPDspoint *pt = &gps->points[i + 1]; - bGPDspoint *next = &gps->points[i + 2]; - - /* Interpolate all values */ - interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); - - pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); - pt->strength = interpf(prev->strength, next->strength, 0.5f); - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - pt->time = interpf(prev->time, next->time, 0.5f); - } - - /* Update to new total number of points */ - gps->totpoints = new_totpoints; } /** @@ -965,7 +1018,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) * \param gps Stroke data * \param brush Brush data */ -void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) +void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, RNG *rng) { bGPDspoint *pt1, *pt2, *pt3; float v1[3]; @@ -995,10 +1048,10 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) 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) { + 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->draw_random_sub / 10.0f); + 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) { @@ -1011,31 +1064,46 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) /* apply shift */ add_v3_v3(&pt->x, svec); } - } -/* calculate difference matrix */ -void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) -{ - Object *ob = gpl->parent; - if (ob == NULL) { +/* ******************************************************** */ +/* 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, ob->obmat, gpl->inverse); + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); return; } else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); + bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); if (pchan) { float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); + mul_m4_m4m4(tmp_mat, obparent_eval->obmat, pchan->pose_mat); mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); } else { - mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */ + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); /* if bone not found use object (armature) */ } return; } @@ -1046,7 +1114,7 @@ void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) } /* reset parent matrix for all layers */ -void ED_gpencil_reset_layers_parent(bGPdata *gpd) +void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd) { bGPDspoint *pt; int i; @@ -1071,7 +1139,7 @@ void ED_gpencil_reset_layers_parent(bGPdata *gpd) /* only redo if any change */ if (!equals_m4m4(gpl->inverse, cur_mat)) { /* first apply current transformation to all strokes */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { @@ -1086,90 +1154,580 @@ void ED_gpencil_reset_layers_parent(bGPdata *gpd) } } /* ******************************************************** */ -bool ED_gpencil_stroke_minmax( - const bGPDstroke *gps, const bool use_select, - float r_min[3], float r_max[3]) -{ - const bGPDspoint *pt; - int i; - bool changed = false; +/* GP Object Stuff */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {; - minmax_v3v3_v3(r_min, r_max, &pt->x); - changed = true; +/* Helper function to create new OB_GPENCIL Object */ +Object *ED_add_gpencil_object(bContext *C, Scene *scene, const float loc[3]) +{ + float rot[3] = {0.0f}; + + Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, scene->lay); + + /* define size */ + BKE_object_obdata_size_init(ob, GP_OBGPENCIL_DEFAULT_SIZE); + /* create default brushes and colors */ + ED_gpencil_add_defaults(C); + + return ob; +} + +/* Helper function to create default colors and drawing brushes */ +void ED_gpencil_add_defaults(bContext *C) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + /* first try to reuse default material */ + if (ob->actcol > 0) { + Material *ma = give_current_material(ob, ob->actcol); + if ((ma) && (ma->gp_style == NULL)) { + BKE_material_init_gpencil_settings(ma); } } - return changed; + + /* ensure color exist */ + BKE_gpencil_material_ensure(bmain, ob); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + } -/* Dynamic Enums of GP Brushes */ -const EnumPropertyItem *ED_gpencil_brushes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free) +/* ******************************************************** */ +/* Vertex Groups */ + +/* assign points to vertex group */ +void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + if (pt->flag & GP_SPOINT_SELECT) { + BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, weight); + } + } + } + } + CTX_DATA_END; +} + +/* remove points from vertex group */ +void ED_gpencil_vgroup_remove(bContext *C, Object *ob) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + } + } + CTX_DATA_END; +} + +/* select points of vertex group */ +void ED_gpencil_vgroup_select(bContext *C, Object *ob) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { + pt->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + } + } + CTX_DATA_END; +} + +/* unselect points of vertex group */ +void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { + pt->flag &= ~GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + } + } + CTX_DATA_END; +} + +/* ******************************************************** */ +/* Cursor drawing */ + +/* check if cursor is in drawing region */ +static bool gp_check_cursor_region(bContext *C, int mval[2]) +{ + ARegion *ar = CTX_wm_region(C); + ScrArea *sa = CTX_wm_area(C); + /* TODO: add more spacetypes */ + if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { + return false; + } + if ((ar) && (ar->regiontype != RGN_TYPE_WINDOW)) { + return false; + } + else if (ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(ar, ®ion_rect); + return BLI_rcti_isect_pt_v(®ion_rect, mval); + } + else { + return false; + } +} + +/* draw eraser cursor */ +void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) +{ + short radius = (short)brush->size; + + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); + + imm_draw_circle_wire_2d(shdr_pos, x, y, radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, radius / 2)); /* was fixed 40 */ + + immUnbindProgram(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} + +/* Helper callback for drawing the cursor itself */ +static void gp_brush_drawcursor(bContext *C, int x, int y, void *customdata) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ARegion *ar = CTX_wm_region(C); + + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + bGPdata *gpd = ED_gpencil_data_get_active(C); + GP_EditBrush_Data *brush = NULL; + Brush *paintbrush = NULL; + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; + int *last_mouse_position = customdata; + + if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { + brush = &gset->brush[gset->weighttype]; + } + else { + brush = &gset->brush[gset->brushtype]; + } + + /* default radius and color */ + float color[3] = {1.0f, 1.0f, 1.0f}; + float darkcolor[3]; + float radius = 3.0f; + + int mval[2] = {x, y}; + /* check if cursor is in drawing region and has valid datablock */ + if ((!gp_check_cursor_region(C, mval)) || (gpd == NULL)) { + return; + } + + /* for paint use paint brush size and color */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + paintbrush = BKE_brush_getactive_gpencil(scene->toolsettings); + /* while drawing hide */ + if ((gpd->runtime.sbuffer_size > 0) && + (paintbrush) && ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0)) + { + return; + } + + if (paintbrush) { + if ((paintbrush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { + return; + } + + /* eraser has special shape and use a different shader program */ + if (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE) { + ED_gpencil_brush_draw_eraser(paintbrush, x, y); + return; + } + + /* get current drawing color */ + ma = BKE_gpencil_get_material_from_brush(paintbrush); + if (ma == NULL) { + BKE_gpencil_material_ensure(bmain, ob); + /* assign the first material to the brush */ + ma = give_current_material(ob, 1); + paintbrush->gpencil_settings->material = ma; + } + gp_style = ma->gp_style; + + /* after some testing, display the size of the brush is not practical because + * is too disruptive and the size of cursor does not change with zoom factor. + * The decision was to use a fix size, instead of paintbrush->thickness value. + */ + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) + { + radius = 2.0f; + copy_v3_v3(color, gp_style->stroke_rgba); + } + else { + radius = 5.0f; + copy_v3_v3(color, paintbrush->add_col); + } + } + else { + return; + } + } + + /* for sculpt use sculpt brush size */ + if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { + if (brush) { + if ((brush->flag & GP_EDITBRUSH_FLAG_ENABLE_CURSOR) == 0) { + return; + } + + radius = brush->size; + if (brush->flag & (GP_EDITBRUSH_FLAG_INVERT | GP_EDITBRUSH_FLAG_TMP_INVERT)) { + copy_v3_v3(color, brush->curcolor_sub); + } + else { + copy_v3_v3(color, brush->curcolor_add); + } + } + } + + /* draw icon */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + /* Inner Ring: Color from UI panel */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) + { + imm_draw_circle_fill_2d(pos, x, y, radius, 40); + } + else { + imm_draw_circle_wire_2d(pos, x, y, radius, 40); + } + + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + mul_v3_v3fl(darkcolor, color, 0.40f); + immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); + imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + + /* Draw line for lazy mouse */ + if ((last_mouse_position) && + (paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) + { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + copy_v3_v3(color, paintbrush->add_col); + immUniformColor4f(color[0], color[1], color[2], 0.8f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, x, y); + immVertex2f(pos, last_mouse_position[0] + ar->winrct.xmin, + last_mouse_position[1] + ar->winrct.ymin); + immEnd(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } + + immUnbindProgram(); +} + +/* Turn brush cursor in on/off */ +void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata) +{ + Scene *scene = CTX_data_scene(C); + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + int *lastpost = customdata; + + if (gset->paintcursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + else if (enable) { + /* in some situations cursor could be duplicated, so it is better disable first if exist */ + if (gset->paintcursor) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + /* enable cursor */ + gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), + NULL, + gp_brush_drawcursor, + (lastpost) ? customdata : NULL); + } +} + +/* verify if is using the right brush */ +static void gpencil_verify_brush_type(bContext *C, int newmode) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush; - EnumPropertyItem *item = NULL, item_tmp = { 0 }; - int totitem = 0; - int i = 0; + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; - if (ELEM(NULL, C, ts)) { - return DummyRNA_DEFAULT_items; + switch (newmode) { + case OB_MODE_GPENCIL_SCULPT: + gset->flag &= ~GP_BRUSHEDIT_FLAG_WEIGHT_MODE; + if ((gset->brushtype < 0) || (gset->brushtype >= GP_EDITBRUSH_TYPE_WEIGHT)) { + gset->brushtype = GP_EDITBRUSH_TYPE_PUSH; + } + break; + case OB_MODE_GPENCIL_WEIGHT: + gset->flag |= GP_BRUSHEDIT_FLAG_WEIGHT_MODE; + if ((gset->weighttype < GP_EDITBRUSH_TYPE_WEIGHT) || (gset->weighttype >= TOT_GP_EDITBRUSH_TYPES)) { + gset->weighttype = GP_EDITBRUSH_TYPE_WEIGHT; + } + break; + default: + break; } - - /* Existing brushes */ - for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) { - item_tmp.identifier = brush->info; - item_tmp.name = brush->info; - item_tmp.value = i; - - if (brush->flag & GP_BRUSH_ACTIVE) - item_tmp.icon = ICON_BRUSH_DATA; - else - item_tmp.icon = ICON_NONE; - - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; } -/* Dynamic Enums of GP Palettes */ -const EnumPropertyItem *ED_gpencil_palettes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free) +/* set object modes */ +void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDpalette *palette; - EnumPropertyItem *item = NULL, item_tmp = { 0 }; - int totitem = 0; - int i = 0; - - if (ELEM(NULL, C, gpd)) { - return DummyRNA_DEFAULT_items; + if (!gpd) { + return; } - /* Existing palettes */ - for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { - item_tmp.identifier = palette->info; - item_tmp.name = palette->info; - item_tmp.value = i; - - if (palette->flag & PL_PALETTE_ACTIVE) - item_tmp.icon = ICON_COLOR; - else - item_tmp.icon = ICON_NONE; - - RNA_enum_item_add(&item, &totitem, &item_tmp); + switch (newmode) { + case OB_MODE_GPENCIL_EDIT: + gpd->flag |= GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; + case OB_MODE_GPENCIL_PAINT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag |= GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_GPENCIL_SCULPT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + 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_GPENCIL_SCULPT); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_GPENCIL_WEIGHT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + 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_GPENCIL_WEIGHT); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + default: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; } +} - RNA_enum_item_end(&item, &totitem); - *r_free = true; +/* helper to convert 2d to 3d for simple drawing buffer */ +static void gpencil_stroke_convertcoords(ARegion *ar, const tGPspoint *point2D, float origin[3], float out[3]) +{ + float mval_f[2] = { (float)point2D->x, (float)point2D->y }; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; - return item; + copy_v3_v3(rvec, origin); + + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(ar, 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(ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } +} + +/* convert 2d tGPspoint to 3d bGPDspoint */ +void ED_gpencil_tpoint_to_point(ARegion *ar, float origin[3], const tGPspoint *tpt, bGPDspoint *pt) +{ + float p3d[3]; + /* conversion to 3d format */ + gpencil_stroke_convertcoords(ar, tpt, origin, p3d); + copy_v3_v3(&pt->x, p3d); + + pt->pressure = tpt->pressure; + pt->strength = tpt->strength; + pt->uv_fac = tpt->uv_fac; + 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_material_gpencil_settings_get(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.000100f; + } + pixsize = MAX2(pixsize, 0.0000001f); + + bGPDspoint *pt = NULL; + bGPDspoint *ptb = NULL; + int i; + float totlen = 0; + + /* 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->sima)) { + factor = gp_style->sima->gen_x; + } + 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) +{ + Material *gps_ma = NULL; + /* read all strokes */ + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + if (gpd == NULL) { + continue; + } + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* 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) { + /* check if it is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + gps_ma = give_current_material(ob, gps->mat_nr + 1); + /* update */ + if ((gps_ma) && (gps_ma == mat)) { + ED_gpencil_calc_stroke_uv(ob, gps); + } + } + } + } + } + } + } } /* ******************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 59a54f03e56..ae86e4a5fbf 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -197,6 +197,8 @@ typedef enum eAnim_ChannelType { ANIMTYPE_NLATRACK, ANIMTYPE_NLAACTION, + ANIMTYPE_PALETTE, + /* always as last item, the total number of channel types... */ ANIMTYPE_NUM_TYPES } eAnim_ChannelType; @@ -347,6 +349,9 @@ typedef enum eAnimFilter_Flags { /* Movie clip only */ #define EXPANDED_MCLIP(clip) (clip->flag & MCLIP_DATA_EXPAND) +/* Palette only */ +#define EXPANDED_PALETTE(palette) (palette->flag & PALETTE_DATA_EXPAND) + /* AnimData - NLA mostly... */ #define SEL_ANIMDATA(adt) (adt->flag & ADT_UI_SELECTED) diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index 7d509d1243a..333e3d72615 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -42,6 +42,9 @@ extern char datatoc_preview_blend[]; extern int datatoc_preview_cycles_blend_size; extern char datatoc_preview_cycles_blend[]; +extern int datatoc_preview_grease_pencil_blend_size; +extern char datatoc_preview_grease_pencil_blend[]; + extern int datatoc_blender_icons16_png_size; extern char datatoc_blender_icons16_png[]; @@ -239,6 +242,66 @@ extern char datatoc_mc23_jpg[]; extern int datatoc_mc24_jpg_size; extern char datatoc_mc24_jpg[]; +/* grease pencil sculpt brushes files */ +extern int datatoc_gp_brush_smooth_png_size; +extern char datatoc_gp_brush_smooth_png[]; + +extern int datatoc_gp_brush_thickness_png_size; +extern char datatoc_gp_brush_thickness_png[]; + +extern int datatoc_gp_brush_strength_png_size; +extern char datatoc_gp_brush_strength_png[]; + +extern int datatoc_gp_brush_grab_png_size; +extern char datatoc_gp_brush_grab_png[]; + +extern int datatoc_gp_brush_push_png_size; +extern char datatoc_gp_brush_push_png[]; + +extern int datatoc_gp_brush_twist_png_size; +extern char datatoc_gp_brush_twist_png[]; + +extern int datatoc_gp_brush_pinch_png_size; +extern char datatoc_gp_brush_pinch_png[]; + +extern int datatoc_gp_brush_randomize_png_size; +extern char datatoc_gp_brush_randomize_png[]; + +extern int datatoc_gp_brush_clone_png_size; +extern char datatoc_gp_brush_clone_png[]; + +extern int datatoc_gp_brush_weight_png_size; +extern char datatoc_gp_brush_weight_png[]; + +extern int datatoc_gp_brush_pencil_png_size; +extern char datatoc_gp_brush_pencil_png[]; + +extern int datatoc_gp_brush_pen_png_size; +extern char datatoc_gp_brush_pen_png[]; + +extern int datatoc_gp_brush_ink_png_size; +extern char datatoc_gp_brush_ink_png[]; + +extern int datatoc_gp_brush_inknoise_png_size; +extern char datatoc_gp_brush_inknoise_png[]; + +extern int datatoc_gp_brush_block_png_size; +extern char datatoc_gp_brush_block_png[]; + +extern int datatoc_gp_brush_marker_png_size; +extern char datatoc_gp_brush_marker_png[]; + +extern int datatoc_gp_brush_fill_png_size; +extern char datatoc_gp_brush_fill_png[]; + +extern int datatoc_gp_brush_erase_soft_png_size; +extern char datatoc_gp_brush_erase_soft_png[]; + +extern int datatoc_gp_brush_erase_hard_png_size; +extern char datatoc_gp_brush_erase_hard_png[]; + +extern int datatoc_gp_brush_erase_stroke_png_size; +extern char datatoc_gp_brush_erase_stroke_png[]; #endif /* __ED_DATAFILES_H__ */ diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index f1f2ce29e7f..3013b455de4 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -30,64 +30,44 @@ #ifndef __ED_GPENCIL_H__ #define __ED_GPENCIL_H__ -#include "ED_numinput.h" - struct ID; struct ListBase; -struct bContext; -struct Depsgraph; -struct ScrArea; -struct ARegion; -struct View3D; -struct Object; +struct PointerRNA; +struct rcti; + struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; -struct bGPDpalette; -struct bGPDpalettecolor; +struct bGPDspoint; +struct Brush; + +struct Main; +struct bContext; +struct EvaluationContext; +struct Depsgraph; +struct ScrArea; +struct ARegion; +struct RegionView3D; +struct Scene; +struct ToolSettings; +struct ViewLayer; +struct View3D; + +struct Object; +struct Material; + struct bAnimContext; struct KeyframeEditData; -struct PointerRNA; -struct Scene; -struct ViewLayer; -struct wmWindowManager; + struct wmKeyConfig; +struct wmOperator; +struct wmWindow; +struct wmWindowManager; +/* ------------- Grease-Pencil Runtime Data ---------------- */ -/* ------------- Grease-Pencil Helpers ---------------- */ -typedef struct tGPDinterpolate_layer { - struct tGPDinterpolate_layer *next, *prev; - - struct bGPDlayer *gpl; /* layer */ - struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */ - struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */ - struct bGPDframe *interFrame; /* interpolated frame */ - float factor; /* interpolate factor */ - -} tGPDinterpolate_layer; - -/* Temporary interpolate operation data */ -typedef struct tGPDinterpolate { - struct Scene *scene; /* current scene from context */ - struct ScrArea *sa; /* area where painting originated */ - struct ARegion *ar; /* region where painting originated */ - struct bGPdata *gpd; /* current GP datablock */ - - int cframe; /* current frame number */ - ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */ - float shift; /* value for determining the displacement influence */ - float init_factor; /* initial interpolation factor for active layer */ - float low_limit; /* shift low limit (-100%) */ - float high_limit; /* shift upper limit (200%) */ - int flag; /* flag from toolsettings */ - - NumInput num; /* numeric input */ - void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ - void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */ -} tGPDinterpolate; - -/* Temporary 'Stroke Point' data +/* Temporary 'Stroke Point' data (2D / screen-space) * * Used as part of the 'stroke cache' used during drawing of new strokes */ @@ -96,27 +76,43 @@ typedef struct tGPspoint { float pressure; /* pressure of tablet at this point */ float strength; /* pressure of tablet at this point for alpha factor */ float time; /* Time relative to stroke start (used when converting to path) */ + float uv_fac; /* factor of uv along the stroke */ + float uv_rot; /* uv rotation for dor mode */ } tGPspoint; - -/* Check if 'sketching sessions' are enabled */ -#define GPENCIL_SKETCH_SESSIONS_ON(scene) ((scene)->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINTSESSIONS_ON) +/* 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 */ -struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr); +struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *r_ptr); + struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C); +struct bGPdata *ED_gpencil_data_get_active_evaluated(const struct bContext *C); /* Context independent (i.e. each required part is passed in instead) */ -struct bGPdata **ED_gpencil_data_get_pointers_direct(struct ID *screen_id, struct Scene *scene, - struct ScrArea *sa, struct Object *ob, - struct PointerRNA *ptr); -struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, struct Scene *scene, - struct ScrArea *sa, struct Object *ob); +struct bGPdata **ED_gpencil_data_get_pointers_direct( + struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene, + struct Object *ob, + struct PointerRNA *r_ptr); +struct bGPdata *ED_gpencil_data_get_active_direct( + struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene, + struct Object *ob); + +bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr); /* 3D View */ -struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct ViewLayer *view_layer); +struct bGPdata *ED_gpencil_data_get_active_v3d(struct ViewLayer *view_layer); bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra); @@ -124,13 +120,7 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); -bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps); - -struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps); - -bool ED_gpencil_stroke_minmax( - const struct bGPDstroke *gps, const bool use_select, - float r_min[3], float r_max[3]); +bool ED_gpencil_stroke_color_use(struct Object *ob, const struct bGPDlayer *gpl, const struct bGPDstroke *gps); /* ----------- Grease Pencil Operators ----------------- */ @@ -150,16 +140,29 @@ void ED_gpencil_strokes_copybuf_free(void); void ED_gpencil_draw_2dimage(const struct bContext *C); void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d); -void ED_gpencil_draw_view3d(struct wmWindowManager *wm, - struct Scene *scene, - struct ViewLayer *view_layer, - struct Depsgraph *depsgraph, - struct View3D *v3d, - struct ARegion *ar, - bool only3d); -void ED_gpencil_draw_ex(struct Scene *scene, struct bGPdata *gpd, int winx, int winy, - const int cfra, const char spacetype); -void ED_gp_draw_interpolation(struct tGPDinterpolate *tgpi, const int type); +void ED_gpencil_draw_view3d( + struct wmWindowManager *wm, + struct Scene *scene, + struct ViewLayer *view_layer, + struct Depsgraph *depsgraph, + struct View3D *v3d, + struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_view3d_annotations( + struct Scene *scene, struct Depsgraph *depsgraph, + struct View3D *v3d, struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_view3d_object( + struct wmWindowManager *wm, + struct Scene *scene, + struct Depsgraph *depsgraph, + struct Object *ob, + struct View3D *v3d, + struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_ex( + struct RegionView3D *rv3d, struct Scene *scene, struct bGPdata *gpd, int winx, int winy, + const int cfra, const char spacetype); /* ----------- Grease-Pencil AnimEdit API ------------------ */ bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, @@ -192,10 +195,45 @@ int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); /* ------------ Transformation Utilities ------------ */ -/* get difference matrix using parent */ -void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]); +/* 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 bGPdata *gpd); +void ED_gpencil_reset_layers_parent(struct Depsgraph *depsgraph, struct Object *obact, struct bGPdata *gpd); +/* cursor utilities */ +void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); + +/* ----------- Add Primitive Utilities -------------- */ + +void ED_gpencil_create_monkey(struct bContext *C, float mat[4][4]); + +/* ------------ Object Utilities ------------ */ +struct Object *ED_add_gpencil_object(struct bContext *C, struct Scene *scene, const float loc[3]); +void ED_gpencil_add_defaults(struct bContext *C); +/* set object modes */ +void ED_gpencil_setup_modes(struct bContext *C, struct bGPdata *gpd, int newmode); + +void ED_gp_project_stroke_to_plane(struct Object *ob, struct RegionView3D *rv3d, struct bGPDstroke *gps, const float origin[3], const int axis); +void ED_gp_project_point_to_plane(struct Object *ob, struct RegionView3D *rv3d, const float origin[3], const int axis, struct bGPDspoint *pt); +void ED_gp_get_drawing_reference(struct View3D *v3d, struct Scene *scene, struct Object *ob, struct bGPDlayer *gpl, char align_flag, float vec[3]); + +/* set sculpt cursor */ +void ED_gpencil_toggle_brush_cursor(struct bContext *C, bool enable, void *customdata); + +/* vertex groups */ +void ED_gpencil_vgroup_assign(struct bContext *C, struct Object *ob, float weight); +void ED_gpencil_vgroup_remove(struct bContext *C, struct Object *ob); +void ED_gpencil_vgroup_select(struct bContext *C, struct Object *ob); +void ED_gpencil_vgroup_deselect(struct bContext *C, struct Object *ob); + +/* join objects */ +int ED_gpencil_join_objects_exec(struct bContext *C, struct wmOperator *op); + +/* texture coordinate utilities */ +void ED_gpencil_tpoint_to_point(struct ARegion *ar, 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); #endif /* __ED_GPENCIL_H__ */ diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 64dee410526..45a0680e0c1 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -42,6 +42,7 @@ struct bActionGroup; struct Object; struct ListBase; struct bGPDlayer; +struct Palette; struct MaskLayer; struct Scene; struct View2D; @@ -151,9 +152,11 @@ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tr /* DopeSheet Summary */ void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Grease Pencil datablock summary */ -void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys); +void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys, const bool active); /* Grease Pencil Layer */ void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); +/* Palette */ +void palette_to_keylist(struct bDopeSheet *ads, struct Palette *palette, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Mask */ void mask_to_keylist(struct bDopeSheet *UNUSED(ads), struct MaskLayer *masklay, struct DLRBT_Tree *keys); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 9fd5cc99073..a39523983ba 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -42,6 +42,7 @@ struct ID; struct Main; struct Menu; struct ModifierData; +struct ShaderFxData; struct Object; struct ReportList; struct Scene; @@ -261,6 +262,40 @@ bool ED_object_iter_other( bool ED_object_multires_update_totlevels_cb(struct Object *ob, void *totlevel_v); + +/* object_greasepencil_modifier.c */ +struct GpencilModifierData *ED_object_gpencil_modifier_add( + struct ReportList *reports, struct Main *bmain, struct Scene *scene, + struct Object *ob, const char *name, int type); +bool ED_object_gpencil_modifier_remove( + struct ReportList *reports, struct Main *bmain, + struct Object *ob, struct GpencilModifierData *md); +void ED_object_gpencil_modifier_clear( + struct Main *bmain, struct Object *ob); +int ED_object_gpencil_modifier_move_down( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); +int ED_object_gpencil_modifier_move_up( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); +int ED_object_gpencil_modifier_apply( + struct Main *bmain, struct ReportList *reports, struct Depsgraph *depsgraph, + struct Object *ob, struct GpencilModifierData *md, int mode); +int ED_object_gpencil_modifier_copy( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); + +/* object_shader_fx.c */ +struct ShaderFxData *ED_object_shaderfx_add( + struct ReportList *reports, struct Main *bmain, struct Scene *scene, + struct Object *ob, const char *name, int type); +bool ED_object_shaderfx_remove( + struct ReportList *reports, struct Main *bmain, + struct Object *ob, struct ShaderFxData *fx); +void ED_object_shaderfx_clear( + struct Main *bmain, struct Object *ob); +int ED_object_shaderfx_move_down( + struct ReportList *reports, struct Object *ob, struct ShaderFxData *fx); +int ED_object_shaderfx_move_up( + struct ReportList *reports, struct Object *ob, struct ShaderFxData *fx); + /* object_select.c */ void ED_object_select_linked_by_id(struct bContext *C, struct ID *id); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ec4c7dddd4c..dec02faf85c 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -1001,6 +1001,30 @@ DEF_ICON(MATCAP_22) DEF_ICON(MATCAP_23) DEF_ICON(MATCAP_24) +/* grease pencil sculpt */ +DEF_ICON(GPBRUSH_SMOOTH) +DEF_ICON(GPBRUSH_THICKNESS) +DEF_ICON(GPBRUSH_STRENGTH) +DEF_ICON(GPBRUSH_GRAB) +DEF_ICON(GPBRUSH_PUSH) +DEF_ICON(GPBRUSH_TWIST) +DEF_ICON(GPBRUSH_PINCH) +DEF_ICON(GPBRUSH_RANDOMIZE) +DEF_ICON(GPBRUSH_CLONE) +DEF_ICON(GPBRUSH_WEIGHT) + +DEF_ICON(GPBRUSH_PENCIL) +DEF_ICON(GPBRUSH_PEN) +DEF_ICON(GPBRUSH_INK) +DEF_ICON(GPBRUSH_INKNOISE) +DEF_ICON(GPBRUSH_BLOCK) +DEF_ICON(GPBRUSH_MARKER) +DEF_ICON(GPBRUSH_CUSTOM) +DEF_ICON(GPBRUSH_FILL) +DEF_ICON(GPBRUSH_ERASE_SOFT) +DEF_ICON(GPBRUSH_ERASE_HARD) +DEF_ICON(GPBRUSH_ERASE_STROKE) + /* vector icons, VICO_ prefix added */ DEF_VICO(SMALL_TRI_RIGHT_VEC) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b4756eaed0f..fc3dd3f9968 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1022,7 +1022,8 @@ uiLayout *uiLayoutRadial(uiLayout *layout); void uiTemplateHeader(uiLayout *layout, struct bContext *C); void uiTemplateID( uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, - const char *newop, const char *openop, const char *unlinkop, int filter); + const char *newop, const char *openop, const char *unlinkop, + int filter, const bool live_icon); void uiTemplateIDBrowse( uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter); @@ -1052,6 +1053,12 @@ void uiTemplatePathBuilder( uiLayout *layout, struct PointerRNA *ptr, const char *propname, struct PointerRNA *root_ptr, const char *text); uiLayout *uiTemplateModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); +uiLayout *uiTemplateGpencilModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); +void uiTemplateGpencilColorPreview( + uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, + int rows, int cols, float scale, int filter); + +uiLayout *uiTemplateShaderFx(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 7255640bedc..4f77b797ec0 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -47,6 +47,7 @@ #include "DNA_brush_types.h" #include "DNA_curve_types.h" #include "DNA_dynamicpaint_types.h" +#include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -109,6 +110,7 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); #define ICON_TYPE_VECTOR 4 #define ICON_TYPE_GEOM 5 #define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */ +#define ICON_TYPE_GPLAYER 7 typedef struct DrawInfo { int type; @@ -391,6 +393,28 @@ DEF_VICON_COLORSET_DRAW_NTH(20, 19) #undef DEF_VICON_COLORSET_DRAW_NTH +/* Dynamically render icon instead of rendering a plain color to a texture/buffer + * This is mot strictly a "vicon", as it needs access to icon->obj to get the color info, + * but it works in a very similar way. + */ +static void vicon_gplayer_color_draw(Icon *icon, int x, int y, int w, int h) +{ + bGPDlayer *gpl = (bGPDlayer *)icon->obj; + + /* Just draw a colored rect - Like for vicon_colorset_draw() */ + /* TODO: Make this have rounded corners, and maybe be a bit smaller. + * However, UI_draw_roundbox_aa() draws the colors too dark, so can't be used. + */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3fv(gpl->color); + immRecti(pos, x, y, x + w - 1, y + h - 1); + + immUnbindProgram(); +} + + #ifndef WITH_HEADLESS static void init_brush_icons(void) @@ -443,6 +467,30 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw); + /* grease pencil sculpt */ + INIT_BRUSH_ICON(ICON_GPBRUSH_SMOOTH, gp_brush_smooth); + INIT_BRUSH_ICON(ICON_GPBRUSH_THICKNESS, gp_brush_thickness); + INIT_BRUSH_ICON(ICON_GPBRUSH_STRENGTH, gp_brush_strength); + INIT_BRUSH_ICON(ICON_GPBRUSH_GRAB, gp_brush_grab); + INIT_BRUSH_ICON(ICON_GPBRUSH_PUSH, gp_brush_push); + INIT_BRUSH_ICON(ICON_GPBRUSH_TWIST, gp_brush_twist); + INIT_BRUSH_ICON(ICON_GPBRUSH_PINCH, gp_brush_pinch); + INIT_BRUSH_ICON(ICON_GPBRUSH_RANDOMIZE, gp_brush_randomize); + INIT_BRUSH_ICON(ICON_GPBRUSH_CLONE, gp_brush_clone); + INIT_BRUSH_ICON(ICON_GPBRUSH_WEIGHT, gp_brush_weight); + + /* grease pencil drawing brushes */ + INIT_BRUSH_ICON(ICON_GPBRUSH_PENCIL, gp_brush_pencil); + INIT_BRUSH_ICON(ICON_GPBRUSH_PEN, gp_brush_pen); + INIT_BRUSH_ICON(ICON_GPBRUSH_INK, gp_brush_ink); + INIT_BRUSH_ICON(ICON_GPBRUSH_INKNOISE, gp_brush_inknoise); + INIT_BRUSH_ICON(ICON_GPBRUSH_BLOCK, gp_brush_block); + INIT_BRUSH_ICON(ICON_GPBRUSH_MARKER, gp_brush_marker); + INIT_BRUSH_ICON(ICON_GPBRUSH_FILL, gp_brush_fill); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_SOFT, gp_brush_erase_soft); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke); + #undef INIT_BRUSH_ICON } @@ -877,6 +925,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon) else if (icon_data_type == ICON_DATA_STUDIOLIGHT) { di->type = ICON_TYPE_BUFFER; } + else if (icon_data_type == ICON_DATA_GPLAYER) { + di->type = ICON_TYPE_GPLAYER; + } else { BLI_assert(0); } @@ -1484,6 +1535,15 @@ static void icon_draw_size( GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } } + else if (di->type == ICON_TYPE_GPLAYER) { + BLI_assert(icon->obj != NULL); + + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); + + /* Just draw a colored rect - Like for vicon_colorset_draw() */ + vicon_gplayer_color_draw(icon, (int)x, (int)y, w, h); + } } static void ui_id_preview_image_render_size( @@ -1576,7 +1636,45 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) } /* reset the icon */ - if (mode == OB_MODE_SCULPT) { + if (ob->mode & OB_MODE_GPENCIL_PAINT) { + switch (br->gpencil_settings->icon_id) { + case GP_BRUSH_ICON_PENCIL: + br->id.icon_id = ICON_GPBRUSH_PENCIL; + break; + case GP_BRUSH_ICON_PEN: + br->id.icon_id = ICON_GPBRUSH_PEN; + break; + case GP_BRUSH_ICON_INK: + br->id.icon_id = ICON_GPBRUSH_INK; + break; + case GP_BRUSH_ICON_INKNOISE: + br->id.icon_id = ICON_GPBRUSH_INKNOISE; + break; + case GP_BRUSH_ICON_BLOCK: + br->id.icon_id = ICON_GPBRUSH_BLOCK; + break; + case GP_BRUSH_ICON_MARKER: + br->id.icon_id = ICON_GPBRUSH_MARKER; + break; + case GP_BRUSH_ICON_FILL: + br->id.icon_id = ICON_GPBRUSH_FILL; + break; + case GP_BRUSH_ICON_ERASE_SOFT: + br->id.icon_id = ICON_GPBRUSH_ERASE_SOFT; + break; + case GP_BRUSH_ICON_ERASE_HARD: + br->id.icon_id = ICON_GPBRUSH_ERASE_HARD; + break; + case GP_BRUSH_ICON_ERASE_STROKE: + br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE; + break; + default: + br->id.icon_id = ICON_GPBRUSH_PEN; + break; + } + return id->icon_id; + } + else if (mode == OB_MODE_SCULPT) { items = rna_enum_brush_sculpt_tool_items; tool = br->sculpt_tool; } diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 82ed4c5acba..89e1a8caec8 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1244,7 +1244,15 @@ void uiItemsFullEnumO( bool free; if (ui_layout_is_radial(layout)) { + /* XXX: While "_all()" guarantees spatial stability, it's bad when an enum has > 8 items total, + * but only a small subset will ever be shown at once (e.g. Mode Switch menu, after the + * introduction of GP editing modes) + */ +#if 0 RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, &totitem, &free); +#else + RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free); +#endif } else { RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 131ffbca377..513bfd32191 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -39,6 +39,8 @@ #include "DNA_object_force_types.h" #include "DNA_brush_types.h" #include "DNA_texture_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "BLI_utildefines.h" #include "BLI_alloca.h" @@ -57,6 +59,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_gpencil_modifier.h" #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_layer.h" @@ -71,6 +74,7 @@ #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_shader_fx.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -110,7 +114,7 @@ static void template_add_button_search_menu( const bContext *C, uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, uiBlockCreateFunc block_func, void *block_argN, const char * const tip, - const bool use_previews, const bool editable) + const bool use_previews, const bool editable, const bool live_icon) { PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; @@ -146,7 +150,14 @@ static void template_add_button_search_menu( } else { but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip); - ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + + if (live_icon) { + int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + } + else { + ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + } if (id) { /* default dragging of icon for id browse buttons */ UI_but_drag_set_id(but, id); @@ -162,7 +173,8 @@ static uiBlock *template_common_search_menu( const bContext *C, ARegion *region, uiButSearchFunc search_func, void *search_arg, uiButHandleFunc handle_func, void *active_item, - const int preview_rows, const int preview_cols) + const int preview_rows, const int preview_cols, + float scale) { static char search[256]; wmWindow *win = CTX_wm_window(C); @@ -177,8 +189,8 @@ static uiBlock *template_common_search_menu( /* preview thumbnails */ if (preview_rows > 0 && preview_cols > 0) { - const int w = 4 * U.widget_unit * preview_cols; - const int h = 5 * U.widget_unit * preview_rows; + const int w = 4 * U.widget_unit * preview_cols * scale; + const int h = 5 * U.widget_unit * preview_rows * scale; /* fake button, it holds space for search items */ uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL); @@ -237,6 +249,7 @@ typedef struct TemplateID { short filter; int prv_rows, prv_cols; bool preview; + float scale; } TemplateID; /* Search browse menu, assign */ @@ -382,7 +395,7 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) return template_common_search_menu( C, ar, id_search_cb_p, &template_ui, template_ID_set_property_cb, active_item_ptr.data, - template_ui.prv_rows, template_ui.prv_cols); + template_ui.prv_rows, template_ui.prv_cols, template_ui.scale); } /************************ ID Template ***************************/ @@ -630,7 +643,8 @@ static uiBut *template_id_def_new_but( static void template_ID( bContext *C, uiLayout *layout, TemplateID *template_ui, StructRNA *type, int flag, - const char *newop, const char *openop, const char *unlinkop) + const char *newop, const char *openop, const char *unlinkop, + const bool live_icon) { uiBut *but; uiBlock *block; @@ -655,7 +669,7 @@ static void template_ID( template_add_button_search_menu( C, layout, block, &template_ui->ptr, template_ui->prop, id_search_menu, MEM_dupallocN(template_ui), TIP_(template_id_browse_tip(type)), - use_previews, editable); + use_previews, editable, live_icon); } /* text button with name */ @@ -860,7 +874,8 @@ static void ui_template_id( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, - int flag, int prv_rows, int prv_cols, int filter, bool use_tabs) + int flag, int prv_rows, int prv_cols, int filter, bool use_tabs, + float scale, bool live_icon) { TemplateID *template_ui; PropertyRNA *prop; @@ -879,6 +894,7 @@ static void ui_template_id( template_ui->prop = prop; template_ui->prv_rows = prv_rows; template_ui->prv_cols = prv_cols; + template_ui->scale = scale; if ((flag & UI_ID_PIN) == 0) { template_ui->filter = filter; @@ -907,7 +923,7 @@ static void ui_template_id( } else { uiLayoutRow(layout, true); - template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop); + template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop, live_icon); } } @@ -916,13 +932,14 @@ static void ui_template_id( void uiTemplateID( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop, int filter) + const char *openop, const char *unlinkop, + int filter, const bool live_icon) { ui_template_id( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, - 0, 0, filter, false); + 0, 0, filter, false, 1.0f, live_icon); } void uiTemplateIDBrowse( @@ -933,7 +950,7 @@ void uiTemplateIDBrowse( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME, - 0, 0, filter, false); + 0, 0, filter, false, 1.0f, false); } void uiTemplateIDPreview( @@ -944,7 +961,18 @@ void uiTemplateIDPreview( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, - rows, cols, filter, false); + rows, cols, filter, false, 1.0f, false); +} + +void uiTemplateGpencilColorPreview( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, + int rows, int cols, float scale, int filter) +{ + ui_template_id( + layout, C, ptr, propname, + NULL, NULL, NULL, + UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE, + rows, cols, filter, false, scale < 0.5f ? 0.5f : scale, false); } /** @@ -960,7 +988,7 @@ void uiTemplateIDTabs( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, - 0, 0, filter, true); + 0, 0, filter, true, 1.0f, false); } /************************ ID Chooser Template ***************************/ @@ -1057,12 +1085,12 @@ static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_tem return template_common_search_menu( C, region, ui_rna_collection_search_cb, &template_search, template_search_handle_cb, active_ptr.data, - template_search.preview_rows, template_search.preview_cols); + template_search.preview_rows, template_search.preview_cols, 1.0f); } static void template_search_add_button_searchmenu( const bContext *C, uiLayout *layout, uiBlock *block, - TemplateSearch *template_search, const bool editable) + TemplateSearch *template_search, const bool editable, const bool live_icon) { const char *ui_description = RNA_property_ui_description(template_search->search_data.target_prop); @@ -1070,7 +1098,7 @@ static void template_search_add_button_searchmenu( C, layout, block, &template_search->search_data.target_ptr, template_search->search_data.target_prop, template_search_menu, MEM_dupallocN(template_search), ui_description, - template_search->use_previews, editable); + template_search->use_previews, editable, live_icon); } static void template_search_add_button_name( @@ -1116,7 +1144,7 @@ static void template_search_buttons( uiLayoutRow(layout, true); UI_block_align_begin(block); - template_search_add_button_searchmenu(C, layout, block, template_search, editable); + template_search_add_button_searchmenu(C, layout, block, template_search, editable, false); template_search_add_button_name(block, &active_ptr, type); template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, editable); template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable); @@ -1539,6 +1567,246 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr) return NULL; } +/************************ Grease Pencil Modifier Template *************************/ + +static uiLayout *gpencil_draw_modifier(uiLayout *layout, Object *ob, + GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + PointerRNA ptr; + uiBlock *block; + uiLayout *box, *column, *row, *sub; + uiLayout *result = NULL; + + /* create RNA pointer */ + RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr); + + column = uiLayoutColumn(layout, true); + uiLayoutSetContextPointer(column, "modifier", &ptr); + + /* rounded header ------------------------------------------------------------------- */ + box = uiLayoutBox(column); + + row = uiLayoutRow(box, false); + block = uiLayoutGetBlock(row); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* Open/Close ................................. */ + uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE); + + /* modifier-type icon */ + uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); + UI_block_emboss_set(block, UI_EMBOSS); + + /* modifier name */ + if (mti->isDisabled && mti->isDisabled(md, 0)) { + uiLayoutSetRedAlert(row, true); + } + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiLayoutSetRedAlert(row, false); + + /* mode enabling buttons */ + UI_block_align_begin(block); + uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); + uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); + + if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) { + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, false); + uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + } + + UI_block_align_end(block); + + /* Up/Down + Delete ........................... */ + UI_block_align_begin(block); + uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_gpencil_modifier_move_up"); + uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_gpencil_modifier_move_down"); + UI_block_align_end(block); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove"); + UI_block_emboss_set(block, UI_EMBOSS); + + /* modifier settings (under the header) --------------------------------------------------- */ + if (md->mode & eGpencilModifierMode_Expanded) { + /* apply/convert/copy */ + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + + /* only here obdata, the rest of modifiers is ob level */ + UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE); + + uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); + uiItemEnumO(row, "OBJECT_OT_gpencil_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + 0, "apply_as", MODIFIER_APPLY_DATA); + + uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE, + "OBJECT_OT_gpencil_modifier_copy"); + + /* result is the layout block inside the box, that we return so that modifier settings can be drawn */ + result = uiLayoutColumn(box, false); + block = uiLayoutAbsoluteBlock(box); + } + + /* error messages */ + if (md->error) { + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + uiItemL(row, md->error, ICON_ERROR); + } + + return result; +} + +uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + Object *ob; + GpencilModifierData *md, *vmd; + int i; + + /* verify we have valid data */ + if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) { + RNA_warning("Expected modifier on object"); + return NULL; + } + + ob = ptr->id.data; + md = ptr->data; + + if (!ob || !(GS(ob->id.name) == ID_OB)) { + RNA_warning("Expected modifier on object"); + return NULL; + } + + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE); + + /* find modifier and draw it */ + vmd = ob->greasepencil_modifiers.first; + for (i = 0; vmd; i++, vmd = vmd->next) { + if (md == vmd) + return gpencil_draw_modifier(layout, ob, md); + } + + return NULL; +} + +/************************ Shader FX Template *************************/ + +static uiLayout *gpencil_draw_shaderfx(uiLayout *layout, Object *ob, + ShaderFxData *md) +{ + const ShaderFxTypeInfo *mti = BKE_shaderfxType_getInfo(md->type); + PointerRNA ptr; + uiBlock *block; + uiLayout *box, *column, *row, *sub; + uiLayout *result = NULL; + + /* create RNA pointer */ + RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, &ptr); + + column = uiLayoutColumn(layout, true); + uiLayoutSetContextPointer(column, "shaderfx", &ptr); + + /* rounded header ------------------------------------------------------------------- */ + box = uiLayoutBox(column); + + row = uiLayoutRow(box, false); + block = uiLayoutGetBlock(row); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* Open/Close ................................. */ + uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE); + + /* shader-type icon */ + uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); + UI_block_emboss_set(block, UI_EMBOSS); + + /* effect name */ + if (mti->isDisabled && mti->isDisabled(md, 0)) { + uiLayoutSetRedAlert(row, true); + } + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiLayoutSetRedAlert(row, false); + + /* mode enabling buttons */ + UI_block_align_begin(block); + uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); + uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); + + if (mti->flags & eShaderFxTypeFlag_SupportsEditmode) { + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, false); + uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + } + + UI_block_align_end(block); + + /* Up/Down + Delete ........................... */ + UI_block_align_begin(block); + uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_shaderfx_move_up"); + uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_shaderfx_move_down"); + UI_block_align_end(block); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove"); + UI_block_emboss_set(block, UI_EMBOSS); + + /* effect settings (under the header) --------------------------------------------------- */ + if (md->mode & eShaderFxMode_Expanded) { + /* apply/convert/copy */ + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + + /* only here obdata, the rest of effect is ob level */ + UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE); + + /* result is the layout block inside the box, that we return so that effect settings can be drawn */ + result = uiLayoutColumn(box, false); + block = uiLayoutAbsoluteBlock(box); + } + + /* error messages */ + if (md->error) { + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + uiItemL(row, md->error, ICON_ERROR); + } + + return result; +} + +uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + Object *ob; + ShaderFxData *fx, *vfx; + int i; + + /* verify we have valid data */ + if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) { + RNA_warning("Expected shader fx on object"); + return NULL; + } + + ob = ptr->id.data; + fx = ptr->data; + + if (!ob || !(GS(ob->id.name) == ID_OB)) { + RNA_warning("Expected shader fx on object"); + return NULL; + } + + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE); + + /* find modifier and draw it */ + vfx = ob->shader_fx.first; + for (i = 0; vfx; i++, vfx = vfx->next) { + if (fx == vfx) + return gpencil_draw_shaderfx(layout, ob, fx); + } + + return NULL; +} /************************ Redo Buttons Template *************************/ @@ -4638,7 +4906,7 @@ void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr); - uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!file) { return; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 3cb8a277e9a..e6422c423b9 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -1536,15 +1536,6 @@ void init_userdef_do_versions(Main *bmain) #undef USER_VERSION_ATLEAST #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST((&(U)), ver, subver) - - if (!USER_VERSION_ATLEAST(269, 9)) { - /* grease pencil - new layer color */ - if (U.gpencil_new_layer_col[3] < 0.1f) { - /* defaults to black, but must at least be visible! */ - U.gpencil_new_layer_col[3] = 0.9f; - } - } - if (!USER_VERSION_ATLEAST(271, 5)) { U.pie_menu_radius = 100; U.pie_menu_threshold = 12; @@ -1582,6 +1573,17 @@ void init_userdef_do_versions(Main *bmain) for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) { memcpy(btheme, &U_theme_default, sizeof(*btheme)); } + + /* Annotations - new layer color + * Replace anything that used to be set if it looks like was left + * on the old default (i.e. black), which most users used + */ + if ((U.gpencil_new_layer_col[3] < 0.1f) || (U.gpencil_new_layer_col[0] < 0.1f)) { + /* - New color matches the annotation pencil icon + * - Non-full alpha looks better! + */ + ARRAY_SET_ITEMS(U.gpencil_new_layer_col, 0.38f, 0.61f, 0.78f, 0.9f); + } } /** diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 305e3287029..739975a6278 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -31,6 +31,8 @@ set(INC ../../makesdna ../../makesrna ../../modifiers + ../../gpencil_modifiers + ../../shader_fx ../../python ../../render/extern/include ../../windowmanager @@ -53,6 +55,8 @@ set(SRC object_hook.c object_modes.c object_modifier.c + object_gpencil_modifier.c + object_shader_fx.c object_ops.c object_random.c object_relations.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 68e84ec3f3b..87eddc2674c 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -71,6 +71,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_font.h" +#include "BKE_gpencil.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_layer.h" @@ -103,6 +104,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_gpencil.h" #include "ED_mball.h" #include "ED_mesh.h" #include "ED_node.h" @@ -985,6 +987,106 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) ED_object_add_generic_props(ot, false); } +/********************* Add Gpencil Operator ********************/ + +static int object_gpencil_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL; + + const int type = RNA_enum_get(op->ptr, "type"); + + float loc[3], rot[3]; + unsigned int layer; + bool newob = false; + + /* Hack: Force view-align to be on by default + * since it's not nice for adding shapes in 2D + * for them to end up aligned oddly, but only for Monkey + */ + if ((RNA_struct_property_is_set(op->ptr, "view_align") == false) && + (type == GP_MONKEY)) { + RNA_boolean_set(op->ptr, "view_align", true); + } + + /* Note: We use 'Y' here (not 'Z'), as */ + WM_operator_view3d_unit_defaults(C, op); + if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, &layer, NULL)) + return OPERATOR_CANCELLED; + + /* add new object if not currently editing a GP object, + * or if "empty" was chosen (i.e. user wants a blank GP canvas) + */ + if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) { + const char *ob_name = (type == GP_MONKEY) ? "Suzanne" : NULL; + float radius = RNA_float_get(op->ptr, "radius"); + + ob = ED_object_add_type(C, OB_GPENCIL, ob_name, loc, rot, true, layer); + gpd = ob->data; + newob = true; + + BKE_object_obdata_size_init(ob, GP_OBGPENCIL_DEFAULT_SIZE * radius); + } + else { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL); + } + + /* create relevant geometry */ + switch (type) { + case GP_MONKEY: + { + float radius = RNA_float_get(op->ptr, "radius"); + float mat[4][4]; + + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + mul_v3_fl(mat[0], radius); + mul_v3_fl(mat[1], radius); + mul_v3_fl(mat[2], radius); + + ED_gpencil_create_monkey(C, mat); + break; + } + + case GP_EMPTY: + /* do nothing */ + break; + + default: + BKE_report(op->reports, RPT_WARNING, "Not implemented"); + break; + } + + /* if this is a new object, initialise default stuff (colors, etc.) */ + if (newob) { + ED_gpencil_add_defaults(C); + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_gpencil_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add GPencil"; + ot->description = "Add a grease pencil object to the scene"; + ot->idname = "OBJECT_OT_gpencil_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = object_gpencil_add_exec; + ot->poll = ED_operator_scene_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ED_object_add_unit_props(ot); + ED_object_add_generic_props(ot, false); + + ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); +} + /********************* Add Light Operator ********************/ static const char *get_light_defname(int type) @@ -1781,6 +1883,10 @@ static int convert_exec(bContext *C, wmOperator *op) if (ob->type == OB_MESH) { BKE_object_free_modifiers(ob, 0); /* after derivedmesh calls! */ } + if (ob->type == OB_GPENCIL) { + BKE_object_free_modifiers(ob, 0); /* after derivedmesh calls! */ + BKE_object_free_shaderfx(ob, 0); + } } } else if (ob->type == OB_MESH && target == OB_CURVE) { @@ -2122,6 +2228,10 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer ID_NEW_REMAP_US(obn->mat[a]) else { obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); + /* duplicate grease pencil settings */ + if (ob->mat[a]->gp_style) { + obn->mat[a]->gp_style = MEM_dupallocN(ob->mat[a]->gp_style); + } } id_us_min(id); @@ -2258,6 +2368,16 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer id_us_min(id); } break; + case OB_GPENCIL: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; } /* check if obdata is copied */ @@ -2482,7 +2602,7 @@ static bool join_poll(bContext *C) if (!ob || ID_IS_LINKED(ob)) return 0; - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE)) + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) return ED_operator_screenactive(C); else return 0; @@ -2500,6 +2620,13 @@ static int join_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata"); return OPERATOR_CANCELLED; } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = (bGPdata *)ob->data; + if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { + BKE_report(op->reports, RPT_ERROR, "This data does not support joining in this mode"); + return OPERATOR_CANCELLED; + } + } if (ob->type == OB_MESH) return join_mesh_exec(C, op); @@ -2507,6 +2634,8 @@ static int join_exec(bContext *C, wmOperator *op) return join_curve_exec(C, op); else if (ob->type == OB_ARMATURE) return join_armature_exec(C, op); + else if (ob->type == OB_GPENCIL) + return ED_gpencil_join_objects_exec(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index a6c3c86922d..48048319cb7 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -100,6 +100,7 @@ #include "ED_screen.h" #include "ED_undo.h" #include "ED_image.h" +#include "ED_gpencil.h" #include "RNA_access.h" #include "RNA_define.h" @@ -1539,7 +1540,6 @@ static const EnumPropertyItem *object_mode_set_itemsf( const EnumPropertyItem *input = rna_enum_object_mode_items; EnumPropertyItem *item = NULL; Object *ob; - bGPdata *gpd; int totitem = 0; if (!C) /* needed for docs */ @@ -1555,7 +1555,9 @@ static const EnumPropertyItem *object_mode_set_itemsf( (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) || (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || (ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, - OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || + OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || + (ELEM(input->value, OB_MODE_GPENCIL_EDIT, OB_MODE_GPENCIL_PAINT, + OB_MODE_GPENCIL_SCULPT, OB_MODE_GPENCIL_WEIGHT) && (ob->type == OB_GPENCIL)) || (input->value == OB_MODE_OBJECT)) { RNA_enum_item_add(&item, &totitem, input); @@ -1568,14 +1570,6 @@ static const EnumPropertyItem *object_mode_set_itemsf( RNA_enum_items_add_value(&item, &totitem, input, OB_MODE_OBJECT); } - /* On top of all the rest, GPencil Stroke Edit Mode - * is available if there's a valid gp datablock... - */ - gpd = CTX_data_gpencil_data(C); - if (gpd) { - RNA_enum_items_add_value(&item, &totitem, rna_enum_object_mode_items, OB_MODE_GPENCIL); - } - RNA_enum_item_end(&item, &totitem); *r_free = true; @@ -1600,7 +1594,6 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) { bool use_submode = STREQ(op->idname, "OBJECT_OT_mode_set_or_submode"); Object *ob = CTX_data_active_object(C); - bGPdata *gpd = CTX_data_gpencil_data(C); eObjectMode mode = RNA_enum_get(op->ptr, "mode"); eObjectMode restore_mode = (ob) ? ob->mode : OB_MODE_OBJECT; const bool toggle = RNA_boolean_get(op->ptr, "toggle"); @@ -1620,22 +1613,9 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) } } - if (gpd) { - /* GP Mode is not bound to a specific object. Therefore, - * we don't want it to be actually saved on any objects, - * as weirdness can happen if you select other objects, - * or load old files. - * - * Instead, we use the following 2 rules to ensure that - * the mode selector works as expected: - * 1) If there's no object, we want to enter editmode. - * (i.e. with no object, we're in object mode) - * 2) Otherwise, exit stroke editmode, so that we can - * enter another mode... - */ - if (!ob || (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL); - } + /* by default the operator assume is a mesh, but if gp object change mode */ + if ((ob != NULL) && (ob->type == OB_GPENCIL) && (mode == OB_MODE_EDIT)) { + mode = OB_MODE_GPENCIL_EDIT; } if (!ob || !ED_object_mode_compat_test(ob, mode)) @@ -1666,6 +1646,14 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) } } + /* if type is OB_GPENCIL, set cursor mode */ + if ((ob) && (ob->type == OB_GPENCIL)) { + if (ob->data) { + bGPdata *gpd = (bGPdata *)ob->data; + ED_gpencil_setup_modes(C, gpd, ob->mode); + } + } + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c new file mode 100644 index 00000000000..175fb1706fb --- /dev/null +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -0,0 +1,637 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2018 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/object/object_gpencil_modifier.c + * \ingroup edobj + */ + + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_report.h" +#include "BKE_object.h" +#include "BKE_gpencil.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "object_intern.h" + +/******************************** API ****************************/ + +GpencilModifierData *ED_object_gpencil_modifier_add( + ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) +{ + GpencilModifierData *new_md = NULL; + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type); + + if (ob->type != OB_GPENCIL) { + BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); + return NULL; + } + + if (mti->flags & eGpencilModifierTypeFlag_Single) { + if (BKE_gpencil_modifiers_findByType(ob, type)) { + BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); + return NULL; + } + } + + /* get new modifier data to add */ + new_md = BKE_gpencil_modifier_new(type); + + BLI_addtail(&ob->greasepencil_modifiers, new_md); + + if (name) { + BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name)); + } + + /* make sure modifier data has unique name */ + BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md); + + + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return new_md; +} + +/* Return true if the object has a modifier of type 'type' other than + * the modifier pointed to be 'exclude', otherwise returns false. */ +static bool UNUSED_FUNCTION(gpencil_object_has_modifier)( + const Object *ob, const GpencilModifierData *exclude, + GpencilModifierType type) +{ + GpencilModifierData *md; + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if ((md != exclude) && (md->type == type)) + return true; + } + + return false; +} + +static bool gpencil_object_modifier_remove( + Main *bmain, Object *ob, GpencilModifierData *md, + bool *UNUSED(r_sort_depsgraph)) +{ + /* It seems on rapid delete it is possible to + * get called twice on same modifier, so make + * sure it is in list. */ + if (BLI_findindex(&ob->greasepencil_modifiers, md) == -1) { + return 0; + } + + DEG_relations_tag_update(bmain); + + BLI_remlink(&ob->greasepencil_modifiers, md); + BKE_gpencil_modifier_free(md); + BKE_object_free_derived_caches(ob); + + return 1; +} + +bool ED_object_gpencil_modifier_remove(ReportList *reports, Main *bmain, Object *ob, GpencilModifierData *md) +{ + bool sort_depsgraph = false; + bool ok; + + ok = gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name); + return 0; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return 1; +} + +void ED_object_gpencil_modifier_clear(Main *bmain, Object *ob) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + bool sort_depsgraph = false; + + if (!md) + return; + + while (md) { + GpencilModifierData *next_md; + + next_md = md->next; + + gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); + + md = next_md; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); +} + +int ED_object_gpencil_modifier_move_up(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + if (md->prev) { + BLI_remlink(&ob->greasepencil_modifiers, md); + BLI_insertlinkbefore(&ob->greasepencil_modifiers, md->prev, md); + } + + return 1; +} + +int ED_object_gpencil_modifier_move_down(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + if (md->next) { + BLI_remlink(&ob->greasepencil_modifiers, md); + BLI_insertlinkafter(&ob->greasepencil_modifiers, md->next, md); + } + + return 1; +} + +static int gpencil_modifier_apply_obdata( + ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->isDisabled && mti->isDisabled(md, 0)) { + BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); + return 0; + } + + if (ob->type == OB_GPENCIL) { + if (ELEM(NULL, ob, ob->data)) { + return 0; + } + else if (mti->bakeModifier == NULL) { + BKE_report(reports, RPT_ERROR, "Not implemented"); + return 0; + } + mti->bakeModifier(bmain, depsgraph, md, ob); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + else { + BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); + return 0; + } + + return 1; +} + +int ED_object_gpencil_modifier_apply( + Main *bmain, ReportList *reports, Depsgraph *depsgraph, + Object *ob, GpencilModifierData *md, int UNUSED(mode)) +{ + + if (ob->type == OB_GPENCIL) { + if (ob->mode != OB_MODE_OBJECT) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in paint, sculpt or edit mode"); + return 0; + } + + if (((ID *)ob->data)->us > 1) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); + return 0; + } + } + else if (((ID *)ob->data)->us > 1) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); + return 0; + } + + if (md != ob->greasepencil_modifiers.first) + BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected"); + + if (!gpencil_modifier_apply_obdata(reports, bmain, depsgraph, ob, md)) { + return 0; + } + + BLI_remlink(&ob->greasepencil_modifiers, md); + BKE_gpencil_modifier_free(md); + + return 1; +} + +int ED_object_gpencil_modifier_copy(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + GpencilModifierData *nmd; + + nmd = BKE_gpencil_modifier_new(md->type); + BKE_gpencil_modifier_copyData(md, nmd); + BLI_insertlinkafter(&ob->greasepencil_modifiers, md, nmd); + BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, nmd); + + return 1; +} + +/************************ add modifier operator *********************/ + +static int gpencil_modifier_add_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_active_context(C); + int type = RNA_enum_get(op->ptr, "type"); + + if (!ED_object_gpencil_modifier_add(op->reports, bmain, scene, ob, NULL, type)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static const EnumPropertyItem *gpencil_modifier_add_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + Object *ob = ED_object_active_context(C); + EnumPropertyItem *item = NULL; + const EnumPropertyItem *md_item, *group_item = NULL; + const GpencilModifierTypeInfo *mti; + int totitem = 0, a; + + if (!ob) + return rna_enum_object_greasepencil_modifier_type_items; + + for (a = 0; rna_enum_object_greasepencil_modifier_type_items[a].identifier; a++) { + md_item = &rna_enum_object_greasepencil_modifier_type_items[a]; + if (md_item->identifier[0]) { + mti = BKE_gpencil_modifierType_getInfo(md_item->value); + + if (mti->flags & eGpencilModifierTypeFlag_NoUserAdd) + continue; + } + else { + group_item = md_item; + md_item = NULL; + + continue; + } + + if (group_item) { + RNA_enum_item_add(&item, &totitem, group_item); + group_item = NULL; + } + + RNA_enum_item_add(&item, &totitem, md_item); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Grease Pencil Modifier"; + ot->description = "Add a procedural operation/effect to the active grease pencil object"; + ot->idname = "OBJECT_OT_gpencil_modifier_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gpencil_modifier_add_exec; + ot->poll = ED_operator_object_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", rna_enum_object_modifier_type_items, eGpencilModifierType_Thick, "Type", ""); + RNA_def_enum_funcs(prop, gpencil_modifier_add_itemf); + ot->prop = prop; +} + +/************************ generic functions for operators using mod names and data context *********************/ + +static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); + Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + + if (!ptr.data) { + CTX_wm_operator_poll_msg_set(C, "Context missing 'modifier'"); + return 0; + } + + if (!ob || ID_IS_LINKED(ob)) return 0; + if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0; + if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) return 0; + + if (ID_IS_STATIC_OVERRIDE(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit modifiers comming from static override"); + return (((GpencilModifierData *)ptr.data)->flag & eGpencilModifierFlag_StaticOverride_Local) != 0; + } + + return 1; +} + +static bool gpencil_edit_modifier_poll(bContext *C) +{ + return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0); +} + +static void gpencil_edit_modifier_properties(wmOperatorType *ot) +{ + RNA_def_string(ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit"); +} + +static int gpencil_edit_modifier_invoke_properties(bContext *C, wmOperator *op) +{ + GpencilModifierData *md; + + if (RNA_struct_property_is_set(op->ptr, "modifier")) { + return true; + } + else { + PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier); + if (ptr.data) { + md = ptr.data; + RNA_string_set(op->ptr, "modifier", md->name); + return true; + } + } + + return false; +} + +static GpencilModifierData *gpencil_edit_modifier_property_get(wmOperator *op, Object *ob, int type) +{ + char modifier_name[MAX_NAME]; + GpencilModifierData *md; + RNA_string_get(op->ptr, "modifier", modifier_name); + + md = BKE_gpencil_modifiers_findByName(ob, modifier_name); + + if (md && type != 0 && md->type != type) + md = NULL; + + return md; +} + +/************************ remove modifier operator *********************/ + +static int gpencil_modifier_remove_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_remove_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot) +{ + ot->name = "Remove Grease Pencil Modifier"; + ot->description = "Remove a modifier from the active grease pencil object"; + ot->idname = "OBJECT_OT_gpencil_modifier_remove"; + + ot->invoke = gpencil_modifier_remove_invoke; + ot->exec = gpencil_modifier_remove_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ move up modifier operator *********************/ + +static int gpencil_modifier_move_up_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_move_up(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_move_up_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_move_up(wmOperatorType *ot) +{ + ot->name = "Move Up Modifier"; + ot->description = "Move modifier up in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_move_up"; + + ot->invoke = gpencil_modifier_move_up_invoke; + ot->exec = gpencil_modifier_move_up_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ move down modifier operator *********************/ + +static int gpencil_modifier_move_down_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_move_down(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_move_down_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot) +{ + ot->name = "Move Down Modifier"; + ot->description = "Move modifier down in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_move_down"; + + ot->invoke = gpencil_modifier_move_down_invoke; + ot->exec = gpencil_modifier_move_down_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ apply modifier operator *********************/ + +static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + int apply_as = RNA_enum_get(op->ptr, "apply_as"); + + if (!md || !ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_apply_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +static const EnumPropertyItem gpencil_modifier_apply_as_items[] = { + {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"}, + {MODIFIER_APPLY_SHAPE, "SHAPE", 0, "New Shape", "Apply deform-only modifier to a new shape on this object"}, + {0, NULL, 0, NULL, NULL} +}; + +void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot) +{ + ot->name = "Apply Modifier"; + ot->description = "Apply modifier and remove from the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_apply"; + + ot->invoke = gpencil_modifier_apply_invoke; + ot->exec = gpencil_modifier_apply_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_enum(ot->srna, "apply_as", gpencil_modifier_apply_as_items, MODIFIER_APPLY_DATA, "Apply as", "How to apply the modifier to the geometry"); + gpencil_edit_modifier_properties(ot); +} + +/************************ copy modifier operator *********************/ + +static int gpencil_modifier_copy_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_copy(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_copy_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot) +{ + ot->name = "Copy Modifier"; + ot->description = "Duplicate modifier at the same position in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_copy"; + + ot->invoke = gpencil_modifier_copy_invoke; + ot->exec = gpencil_modifier_copy_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 1b5c6df2632..ef8653541f0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -115,6 +115,7 @@ void OBJECT_OT_armature_add(struct wmOperatorType *ot); void OBJECT_OT_empty_add(struct wmOperatorType *ot); void OBJECT_OT_lightprobe_add(struct wmOperatorType *ot); void OBJECT_OT_drop_named_image(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_add(struct wmOperatorType *ot); void OBJECT_OT_light_add(struct wmOperatorType *ot); void OBJECT_OT_effector_add(struct wmOperatorType *ot); void OBJECT_OT_camera_add(struct wmOperatorType *ot); @@ -175,6 +176,20 @@ void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); +/* grease pencil modifiers */ +void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_remove(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_move_up(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); + +/* shader fx */ +void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot); + /* object_constraint.c */ void OBJECT_OT_constraint_add(struct wmOperatorType *ot); void OBJECT_OT_constraint_add_with_targets(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index e9bd6fbce8f..261f7f42bc0 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -27,6 +27,7 @@ * actual mode switching logic is per-object type. */ +#include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_workspace_types.h" @@ -71,8 +72,14 @@ static const char *object_mode_op_string(eObjectMode mode) return "PARTICLE_OT_particle_edit_toggle"; if (mode == OB_MODE_POSE) return "OBJECT_OT_posemode_toggle"; - if (mode == OB_MODE_GPENCIL) + if (mode == OB_MODE_GPENCIL_EDIT) return "GPENCIL_OT_editmode_toggle"; + if (mode == OB_MODE_GPENCIL_PAINT) + return "GPENCIL_OT_paintmode_toggle"; + if (mode == OB_MODE_GPENCIL_SCULPT) + return "GPENCIL_OT_sculptmode_toggle"; + if (mode == OB_MODE_GPENCIL_WEIGHT) + return "GPENCIL_OT_weightmode_toggle"; return NULL; } @@ -85,8 +92,6 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) if (ob) { if (mode == OB_MODE_OBJECT) return true; - else if (mode == OB_MODE_GPENCIL) - return true; /* XXX: assume this is the case for now... */ switch (ob->type) { case OB_MESH: @@ -111,6 +116,13 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) return true; break; + case OB_GPENCIL: + if (mode & (OB_MODE_EDIT | OB_MODE_GPENCIL_EDIT | OB_MODE_GPENCIL_PAINT | + OB_MODE_GPENCIL_SCULPT | OB_MODE_GPENCIL_WEIGHT)) + { + return true; + } + break; } } diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 11d0fd9f9d5..f83c6af08ee 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1,31 +1,31 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * Contributor(s): Blender Foundation, 2009 - * - * ***** END GPL LICENSE BLOCK ***** - */ +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. +* All rights reserved. +* +* Contributor(s): Blender Foundation, 2009 +* +* ***** END GPL LICENSE BLOCK ***** +*/ /** \file blender/editors/object/object_modifier.c - * \ingroup edobj - */ +* \ingroup edobj +*/ #include @@ -117,8 +117,8 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc if (type == eModifierType_ParticleSystem) { /* don't need to worry about the new modifier's name, since that is set to the number - * of particle systems which shouldn't have too many duplicates - */ + * of particle systems which shouldn't have too many duplicates + */ new_md = object_add_particle_system(bmain, scene, ob, name); } else { @@ -182,9 +182,9 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc } /* Return true if the object has a modifier of type 'type' other than - * the modifier pointed to be 'exclude', otherwise returns false. */ +* the modifier pointed to be 'exclude', otherwise returns false. */ static bool object_has_modifier(const Object *ob, const ModifierData *exclude, - ModifierType type) + ModifierType type) { ModifierData *md; @@ -197,16 +197,16 @@ static bool object_has_modifier(const Object *ob, const ModifierData *exclude, } /* If the object data of 'orig_ob' has other users, run 'callback' on - * each of them. - * - * If include_orig is true, the callback will run on 'orig_ob' too. - * - * If the callback ever returns true, iteration will stop and the - * function value will be true. Otherwise the function returns false. - */ +* each of them. +* +* If include_orig is true, the callback will run on 'orig_ob' too. +* +* If the callback ever returns true, iteration will stop and the +* function value will be true. Otherwise the function returns false. +*/ bool ED_object_iter_other(Main *bmain, Object *orig_ob, const bool include_orig, - bool (*callback)(Object *ob, void *callback_data), - void *callback_data) + bool (*callback)(Object *ob, void *callback_data), + void *callback_data) { ID *ob_data_id = orig_ob->data; int users = ob_data_id->us; @@ -220,10 +220,10 @@ bool ED_object_iter_other(Main *bmain, Object *orig_ob, const bool include_orig, int totfound = include_orig ? 0 : 1; for (ob = bmain->object.first; ob && totfound < users; - ob = ob->id.next) + ob = ob->id.next) { if (((ob != orig_ob) || include_orig) && - (ob->data == orig_ob->data)) + (ob->data == orig_ob->data)) { if (callback(ob, callback_data)) return true; @@ -247,8 +247,8 @@ static bool object_has_modifier_cb(Object *ob, void *data) } /* Use with ED_object_iter_other(). Sets the total number of levels - * for any multires modifiers on the object to the int pointed to by - * callback_data. */ +* for any multires modifiers on the object to the int pointed to by +* callback_data. */ bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) { ModifierData *md; @@ -265,20 +265,20 @@ bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) /* Return true if no modifier of type 'type' other than 'exclude' */ static bool object_modifier_safe_to_delete(Main *bmain, Object *ob, - ModifierData *exclude, - ModifierType type) + ModifierData *exclude, + ModifierType type) { return (!object_has_modifier(ob, exclude, type) && - !ED_object_iter_other(bmain, ob, false, - object_has_modifier_cb, &type)); + !ED_object_iter_other(bmain, ob, false, + object_has_modifier_cb, &type)); } static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md, - bool *r_sort_depsgraph) + bool *r_sort_depsgraph) { /* It seems on rapid delete it is possible to - * get called twice on same modifier, so make - * sure it is in list. */ + * get called twice on same modifier, so make + * sure it is in list. */ if (BLI_findindex(&ob->modifiers, md) == -1) { return 0; } @@ -318,7 +318,7 @@ static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md, } if (ELEM(md->type, eModifierType_Softbody, eModifierType_Cloth) && - BLI_listbase_is_empty(&ob->particlesystem)) + BLI_listbase_is_empty(&ob->particlesystem)) { ob->mode &= ~OB_MODE_PARTICLE_EDIT; } @@ -522,7 +522,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * } static int modifier_apply_shape( - Main *bmain, ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md) + Main *bmain, ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md) { const ModifierTypeInfo *mti = modifierType_getInfo(md->type); @@ -532,15 +532,15 @@ static int modifier_apply_shape( } /* - * It should be ridiculously easy to extract the original verts that we want - * and form the shape data. We can probably use the CD KEYINDEX layer (or - * whatever I ended up calling it, too tired to check now), though this would - * by necessity have to make some potentially ugly assumptions about the order - * of the mesh data :-/ you can probably assume in 99% of cases that the first - * element of a given index is the original, and any subsequent duplicates are - * copies/interpolates, but that's an assumption that would need to be tested - * and then predominantly stated in comments in a half dozen headers. - */ + * It should be ridiculously easy to extract the original verts that we want + * and form the shape data. We can probably use the CD KEYINDEX layer (or + * whatever I ended up calling it, too tired to check now), though this would + * by necessity have to make some potentially ugly assumptions about the order + * of the mesh data :-/ you can probably assume in 99% of cases that the first + * element of a given index is the original, and any subsequent duplicates are + * copies/interpolates, but that's an assumption that would need to be tested + * and then predominantly stated in comments in a half dozen headers. + */ if (ob->type == OB_MESH) { Mesh *mesh_applied; @@ -563,7 +563,7 @@ static int modifier_apply_shape( key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* if that was the first key block added, then it was the basis. - * Initialize it with the mesh, and add another for the modifier */ + * Initialize it with the mesh, and add another for the modifier */ kb = BKE_keyblock_add(key, NULL); BKE_keyblock_convert_from_mesh(me, key, kb); } @@ -667,8 +667,8 @@ static int modifier_apply_obdata(ReportList *reports, Depsgraph *depsgraph, Scen } int ED_object_modifier_apply( - Main *bmain, ReportList *reports, Depsgraph *depsgraph, - Scene *scene, Object *ob, ModifierData *md, int mode) + Main *bmain, ReportList *reports, Depsgraph *depsgraph, + Scene *scene, Object *ob, ModifierData *md, int mode) { int prev_mode; @@ -681,8 +681,8 @@ int ED_object_modifier_apply( return 0; } else if ((ob->mode & OB_MODE_SCULPT) && - (find_multires_modifier_before(scene, md)) && - (modifier_isSameTopology(md) == false)) + (find_multires_modifier_before(scene, md)) && + (modifier_isSameTopology(md) == false)) { BKE_report(reports, RPT_ERROR, "Constructive modifier cannot be applied to multi-res data in sculpt mode"); return 0; @@ -692,7 +692,7 @@ int ED_object_modifier_apply( BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected"); /* Get evaluated modifier, so object links pointer to evaluated data, - * but still use original object it is applied to the original mesh. */ + * but still use original object it is applied to the original mesh. */ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); ModifierData *md_eval = (ob_eval) ? modifiers_findByName(ob_eval, md->name) : md; @@ -752,7 +752,7 @@ static int modifier_add_exec(bContext *C, wmOperator *op) } static const EnumPropertyItem *modifier_add_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { Object *ob = ED_object_active_context(C); EnumPropertyItem *item = NULL; @@ -1165,8 +1165,8 @@ static int multires_higher_levels_delete_exec(bContext *C, wmOperator *op) multiresModifier_del_levels(mmd, scene, ob, 1); ED_object_iter_other(CTX_data_main(C), ob, true, - ED_object_multires_update_totlevels_cb, - &mmd->totlvl); + ED_object_multires_update_totlevels_cb, + &mmd->totlvl); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1210,8 +1210,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) multiresModifier_subdivide(mmd, scene, ob, 0, mmd->simple); ED_object_iter_other(CTX_data_main(C), ob, true, - ED_object_multires_update_totlevels_cb, - &mmd->totlvl); + ED_object_multires_update_totlevels_cb, + &mmd->totlvl); DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1387,8 +1387,8 @@ void OBJECT_OT_multires_external_save(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BTX, FILE_SPECIAL, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + ot, FILE_TYPE_FOLDER | FILE_TYPE_BTX, FILE_SPECIAL, FILE_SAVE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); edit_modifier_properties(ot); } @@ -1480,13 +1480,13 @@ static void modifier_skin_customdata_delete(Object *ob) static bool skin_poll(bContext *C) { return (!CTX_data_edit_object(C) && - edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); + edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); } static bool skin_edit_poll(bContext *C) { return (CTX_data_edit_object(C) && - edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); + edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); } static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_skin_offset) @@ -1525,7 +1525,7 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op)) BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT) && - BLI_gset_add(visited, bm_vert)) + BLI_gset_add(visited, bm_vert)) { MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset); @@ -1579,8 +1579,8 @@ static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op) BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, - bm_vert->head.data, - CD_MVERT_SKIN); + bm_vert->head.data, + CD_MVERT_SKIN); switch (action) { @@ -1636,8 +1636,8 @@ static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op)) BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, - bm_vert->head.data, - CD_MVERT_SKIN); + bm_vert->head.data, + CD_MVERT_SKIN); float avg = (vs->radius[0] + vs->radius[1]) * 0.5f; vs->radius[0] = vs->radius[1] = avg; @@ -1664,12 +1664,12 @@ void OBJECT_OT_skin_radii_equalize(wmOperatorType *ot) } static void skin_armature_bone_create(Object *skin_ob, - MVert *mvert, MEdge *medge, - bArmature *arm, - BLI_bitmap *edges_visited, - const MeshElemMap *emap, - EditBone *parent_bone, - int parent_v) + MVert *mvert, MEdge *medge, + bArmature *arm, + BLI_bitmap *edges_visited, + const MeshElemMap *emap, + EditBone *parent_bone, + int parent_v) { int i; @@ -1704,12 +1704,12 @@ static void skin_armature_bone_create(Object *skin_ob, } skin_armature_bone_create(skin_ob, - mvert, medge, - arm, - edges_visited, - emap, - bone, - v); + mvert, medge, + arm, + edges_visited, + emap, + bone, + v); } } @@ -1731,10 +1731,10 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, /* add vertex weights to original mesh */ CustomData_add_layer(&me->vdata, - CD_MDEFORMVERT, - CD_CALLOC, - NULL, - me->totvert); + CD_MDEFORMVERT, + CD_CALLOC, + NULL, + me->totvert); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, NULL); @@ -1747,19 +1747,19 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN); BKE_mesh_vert_edge_map_create(&emap, &emap_mem, - me->medge, me->totvert, me->totedge); + me->medge, me->totvert, me->totedge); edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited"); /* note: we use EditBones here, easier to set them up and use - * edit-armature functions to convert back to regular bones */ + * edit-armature functions to convert back to regular bones */ for (v = 0; v < me->totvert; v++) { if (mvert_skin[v].flag & MVERT_SKIN_ROOT) { EditBone *bone = NULL; /* Unless the skin root has just one adjacent edge, create - * a fake root bone (have it going off in the Y direction - * (arbitrary) */ + * a fake root bone (have it going off in the Y direction + * (arbitrary) */ if (emap[v].count > 1) { bone = ED_armature_ebone_add(arm, "Bone"); @@ -1772,12 +1772,12 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, if (emap[v].count >= 1) { skin_armature_bone_create(skin_ob, - mvert, me->medge, - arm, - edges_visited, - emap, - bone, - v); + mvert, me->medge, + arm, + edges_visited, + emap, + bone, + v); } } } @@ -2092,7 +2092,7 @@ static int oceanbake_breakjob(void *UNUSED(customdata)) //return *(ob->stop); /* this is not nice yet, need to make the jobs list template better - * for identifying/acting upon various different jobs */ + * for identifying/acting upon various different jobs */ /* but for now we'll reuse the render break... */ return (G.is_break); } @@ -2166,8 +2166,8 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) } och = BKE_ocean_init_cache(omd->cachepath, modifier_path_relbase(bmain, ob), - omd->bakestart, omd->bakeend, omd->wave_scale, - omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); + omd->bakestart, omd->bakeend, omd->wave_scale, + omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); och->time = MEM_mallocN(och->duration * sizeof(float), "foam bake time"); @@ -2176,22 +2176,22 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* precalculate time variable before baking */ for (f = omd->bakestart; f <= omd->bakeend; f++) { /* from physics_fluid.c: - * - * XXX: This can't be used due to an anim sys optimization that ignores recalc object animation, - * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ ) - * --> BKE_animsys_evaluate_all_animation(bmain, eval_time); - * This doesn't work with drivers: - * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL); - */ + * + * XXX: This can't be used due to an anim sys optimization that ignores recalc object animation, + * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ ) + * --> BKE_animsys_evaluate_all_animation(bmain, eval_time); + * This doesn't work with drivers: + * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL); + */ /* Modifying the global scene isn't nice, but we can do it in - * this part of the process before a threaded job is created */ + * this part of the process before a threaded job is created */ //scene->r.cfra = f; //ED_update_for_newframe(bmain, scene); /* ok, this doesn't work with drivers, but is way faster. - * let's use this for now and hope nobody wants to drive the time value... */ + * let's use this for now and hope nobody wants to drive the time value... */ BKE_animsys_evaluate_animdata(CTX_data_depsgraph(C), scene, (ID *)ob, ob->adt, f, ADT_RECALC_ANIM); och->time[i] = omd->time; @@ -2220,7 +2220,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* setup job */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Ocean Simulation", - WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_SIM_OCEAN); + WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_SIM_OCEAN); oj = MEM_callocN(sizeof(OceanBakeJob), "ocean bake job"); oj->owner = ob; oj->ocean = ocean; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 7b6c1156874..ac2eb60456f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -115,6 +115,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_empty_add); WM_operatortype_append(OBJECT_OT_lightprobe_add); WM_operatortype_append(OBJECT_OT_drop_named_image); + WM_operatortype_append(OBJECT_OT_gpencil_add); WM_operatortype_append(OBJECT_OT_light_add); WM_operatortype_append(OBJECT_OT_camera_add); WM_operatortype_append(OBJECT_OT_speaker_add); @@ -147,6 +148,20 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_skin_radii_equalize); WM_operatortype_append(OBJECT_OT_skin_armature_create); + /* grease pencil modifiers */ + WM_operatortype_append(OBJECT_OT_gpencil_modifier_add); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_remove); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_up); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_down); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_apply); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy); + + /* shader fx */ + WM_operatortype_append(OBJECT_OT_shaderfx_add); + WM_operatortype_append(OBJECT_OT_shaderfx_remove); + WM_operatortype_append(OBJECT_OT_shaderfx_move_up); + WM_operatortype_append(OBJECT_OT_shaderfx_move_down); + WM_operatortype_append(OBJECT_OT_correctivesmooth_bind); WM_operatortype_append(OBJECT_OT_meshdeform_bind); WM_operatortype_append(OBJECT_OT_explode_refresh); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 331b4af077d..a6751ee12a4 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -70,6 +70,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_lamp.h" @@ -1609,21 +1610,11 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /**************************** Make Single User ********************************/ -static Object *single_object_users_object(Main *bmain, Scene *scene, Object *ob) +static Object *single_object_users_object(Main *bmain, Object *ob) { /* base gets copy of object */ Object *obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob)); - /* remap gpencil parenting */ - - if (scene->gpd) { - bGPdata *gpd = scene->gpd; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->parent == ob) { - gpl->parent = obn; - } - } - } id_us_plus(&obn->id); id_us_min(&ob->id); @@ -1648,7 +1639,7 @@ static void single_object_users_collection(Main *bmain, Scene *scene, Collection /* an object may be in more than one collection */ if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { if (!ID_IS_LINKED(ob) && ob->id.us > 1) { - cob->ob = single_object_users_object(bmain, scene, cob->ob); + cob->ob = single_object_users_object(bmain, cob->ob); } } } @@ -1702,6 +1693,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in /* collection pointers in scene */ BKE_scene_groups_relink(scene); + /* active camera */ ID_NEW_REMAP(scene->camera); if (v3d) ID_NEW_REMAP(v3d->camera); @@ -1805,6 +1797,9 @@ static void single_obdata_users(Main *bmain, Scene *scene, ViewLayer *view_layer case OB_LIGHTPROBE: ob->data = ID_NEW_SET(ob->data, BKE_lightprobe_copy(bmain, ob->data)); break; + case OB_GPENCIL: + ob->data = ID_NEW_SET(ob->data, BKE_gpencil_copy(bmain, ob->data)); + break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); BLI_assert(!"This should never happen."); @@ -1940,10 +1935,6 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo } } - if (scene->gpd) { - IDP_RelinkProperty(scene->gpd->id.properties); - } - if (scene->world) { IDP_RelinkProperty(scene->world->id.properties); } diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index d5f7a93cc6e..c23a1d64ee8 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -41,6 +41,7 @@ #include "DNA_armature_types.h" #include "DNA_lamp_types.h" #include "DNA_workspace_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -88,8 +89,8 @@ * this takes into account the 'restrict selection in 3d view' flag. * deselect works always, the restriction just prevents selection */ -/* Note: send a NC_SCENE|ND_OB_SELECT notifier yourself! (or - * or a NC_SCENE|ND_OB_VISIBLE in case of visibility toggling */ + /* Note: send a NC_SCENE|ND_OB_SELECT notifier yourself! (or + * or a NC_SCENE|ND_OB_VISIBLE in case of visibility toggling */ void ED_object_base_select(Base *base, eObjectSelect_Mode mode) { diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c new file mode 100644 index 00000000000..681851850a5 --- /dev/null +++ b/source/blender/editors/object/object_shader_fx.c @@ -0,0 +1,469 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2018 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/object/object_shader_fx.c + * \ingroup edobj + */ + + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_shader_fx.h" +#include "BKE_report.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "object_intern.h" + +/******************************** API ****************************/ + +ShaderFxData *ED_object_shaderfx_add(ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) +{ + ShaderFxData *new_fx = NULL; + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(type); + + if (ob->type != OB_GPENCIL) { + BKE_reportf(reports, RPT_WARNING, "Effect cannot be added to object '%s'", ob->id.name + 2); + return NULL; + } + + if (fxi->flags & eShaderFxTypeFlag_Single) { + if (BKE_shaderfx_findByType(ob, type)) { + BKE_report(reports, RPT_WARNING, "Only one Effect of this type is allowed"); + return NULL; + } + } + + /* get new effect data to add */ + new_fx = BKE_shaderfx_new(type); + + BLI_addtail(&ob->shader_fx, new_fx); + + if (name) { + BLI_strncpy_utf8(new_fx->name, name, sizeof(new_fx->name)); + } + + /* make sure effect data has unique name */ + BKE_shaderfx_unique_name(&ob->shader_fx, new_fx); + + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return new_fx; +} + +/* Return true if the object has a effect of type 'type' other than + * the shaderfx pointed to be 'exclude', otherwise returns false. */ +static bool UNUSED_FUNCTION(object_has_shaderfx)( + const Object *ob, const ShaderFxData *exclude, + ShaderFxType type) +{ + ShaderFxData *fx; + + for (fx = ob->shader_fx.first; fx; fx = fx->next) { + if ((fx != exclude) && (fx->type == type)) + return true; + } + + return false; +} + +static bool object_shaderfx_remove( + Main *bmain, Object *ob, ShaderFxData *fx, + bool *UNUSED(r_sort_depsgraph)) +{ + /* It seems on rapid delete it is possible to + * get called twice on same effect, so make + * sure it is in list. */ + if (BLI_findindex(&ob->shader_fx, fx) == -1) { + return 0; + } + + DEG_relations_tag_update(bmain); + + BLI_remlink(&ob->shader_fx, fx); + BKE_shaderfx_free(fx); + BKE_object_free_derived_caches(ob); + + return 1; +} + +bool ED_object_shaderfx_remove(ReportList *reports, Main *bmain, Object *ob, ShaderFxData *fx) +{ + bool sort_depsgraph = false; + bool ok; + + ok = object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph); + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Effect '%s' not in object '%s'", fx->name, ob->id.name); + return 0; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return 1; +} + +void ED_object_shaderfx_clear(Main *bmain, Object *ob) +{ + ShaderFxData *fx = ob->shader_fx.first; + bool sort_depsgraph = false; + + if (!fx) + return; + + while (fx) { + ShaderFxData *next_fx; + + next_fx = fx->next; + + object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph); + + fx = next_fx; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); +} + +int ED_object_shaderfx_move_up(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx) +{ + if (fx->prev) { + BLI_remlink(&ob->shader_fx, fx); + BLI_insertlinkbefore(&ob->shader_fx, fx->prev, fx); + } + + return 1; +} + +int ED_object_shaderfx_move_down(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx) +{ + if (fx->next) { + BLI_remlink(&ob->shader_fx, fx); + BLI_insertlinkafter(&ob->shader_fx, fx->next, fx); + } + + return 1; +} + +/************************ add effect operator *********************/ + +static int shaderfx_add_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_active_context(C); + int type = RNA_enum_get(op->ptr, "type"); + + if (!ED_object_shaderfx_add(op->reports, bmain, scene, ob, NULL, type)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static const EnumPropertyItem *shaderfx_add_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + Object *ob = ED_object_active_context(C); + EnumPropertyItem *item = NULL; + const EnumPropertyItem *fx_item, *group_item = NULL; + const ShaderFxTypeInfo *mti; + int totitem = 0, a; + + if (!ob) + return rna_enum_object_shaderfx_type_items; + + for (a = 0; rna_enum_object_shaderfx_type_items[a].identifier; a++) { + fx_item = &rna_enum_object_shaderfx_type_items[a]; + if (fx_item->identifier[0]) { + mti = BKE_shaderfxType_getInfo(fx_item->value); + + if (mti->flags & eShaderFxTypeFlag_NoUserAdd) + continue; + } + else { + group_item = fx_item; + fx_item = NULL; + + continue; + } + + if (group_item) { + RNA_enum_item_add(&item, &totitem, group_item); + group_item = NULL; + } + + RNA_enum_item_add(&item, &totitem, fx_item); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OBJECT_OT_shaderfx_add(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Effect"; + ot->description = "Add a visual effect to the active object"; + ot->idname = "OBJECT_OT_shaderfx_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = shaderfx_add_exec; + ot->poll = ED_operator_object_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", rna_enum_object_shaderfx_type_items, eShaderFxType_Blur, "Type", ""); + RNA_def_enum_funcs(prop, shaderfx_add_itemf); + ot->prop = prop; +} + +/************************ generic functions for operators using names and data context *********************/ + +static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type); + Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + + if (!ptr.data) { + CTX_wm_operator_poll_msg_set(C, "Context missing 'shaderfx'"); + return 0; + } + + if (!ob || ID_IS_LINKED(ob)) return 0; + if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0; + if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) return 0; + + if (ID_IS_STATIC_OVERRIDE(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs comming from static override"); + return (((ShaderFxData *)ptr.data)->flag & eShaderFxFlag_StaticOverride_Local) != 0; + } + + return 1; +} + +static bool edit_shaderfx_poll(bContext *C) +{ + return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); +} + +static void edit_shaderfx_properties(wmOperatorType *ot) +{ + RNA_def_string(ot->srna, "shaderfx", NULL, MAX_NAME, "Shader", "Name of the shaderfx to edit"); +} + +static int edit_shaderfx_invoke_properties(bContext *C, wmOperator *op) +{ + ShaderFxData *fx; + + if (RNA_struct_property_is_set(op->ptr, "shaderfx")) { + return true; + } + else { + PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx); + if (ptr.data) { + fx = ptr.data; + RNA_string_set(op->ptr, "shaderfx", fx->name); + return true; + } + } + + return false; +} + +static ShaderFxData *edit_shaderfx_property_get(wmOperator *op, Object *ob, int type) +{ + char shaderfx_name[MAX_NAME]; + ShaderFxData *fx; + RNA_string_get(op->ptr, "shaderfx", shaderfx_name); + + fx = BKE_shaderfx_findByName(ob, shaderfx_name); + + if (fx && type != 0 && fx->type != type) + fx = NULL; + + return fx; +} + +/************************ remove shaderfx operator *********************/ + +static int shaderfx_remove_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_remove_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_remove(wmOperatorType *ot) +{ + ot->name = "Remove Grease Pencil Modifier"; + ot->description = "Remove a shaderfx from the active grease pencil object"; + ot->idname = "OBJECT_OT_shaderfx_remove"; + + ot->invoke = shaderfx_remove_invoke; + ot->exec = shaderfx_remove_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} + +/************************ move up shaderfx operator *********************/ + +static int shaderfx_move_up_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_move_up(op->reports, ob, fx)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_move_up_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot) +{ + ot->name = "Move Up Modifier"; + ot->description = "Move shaderfx up in the stack"; + ot->idname = "OBJECT_OT_shaderfx_move_up"; + + ot->invoke = shaderfx_move_up_invoke; + ot->exec = shaderfx_move_up_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} + +/************************ move down shaderfx operator *********************/ + +static int shaderfx_move_down_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_move_down(op->reports, ob, fx)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_move_down_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot) +{ + ot->name = "Move Down Modifier"; + ot->description = "Move shaderfx down in the stack"; + ot->idname = "OBJECT_OT_shaderfx_move_down"; + + ot->invoke = shaderfx_move_down_invoke; + ot->exec = shaderfx_move_down_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index d2a0879464b..96b540251b4 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -38,6 +38,7 @@ #include "DNA_lamp_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lattice_types.h" @@ -59,6 +60,7 @@ #include "BKE_armature.h" #include "BKE_lattice.h" #include "BKE_tracking.h" +#include "BKE_gpencil.h" #include "DEG_depsgraph.h" @@ -73,6 +75,7 @@ #include "ED_mesh.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "MEM_guardedalloc.h" @@ -434,7 +437,7 @@ static int apply_objects_internal( /* first check if we can execute */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (ELEM(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT, OB_GPENCIL)) { ID *obdata = ob->data; if (ID_REAL_USERS(obdata) > 1) { BKE_reportf(reports, RPT_ERROR, @@ -480,6 +483,37 @@ static int apply_objects_internal( } } + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + if (gpd) { + if (gpd->layers.first) { + /* Unsupported configuration */ + bool has_unparented_layers = false; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* Parented layers aren't supported as we can't easily re-evaluate the scene to sample parent movement */ + if (gpl->parent == NULL) { + has_unparented_layers = true; + break; + } + } + + if (has_unparented_layers == false) { + BKE_reportf(reports, RPT_ERROR, + "Can't apply to a GP datablock where all layers are parented: Object \"%s\", %s \"%s\", aborting", + ob->id.name + 2, BKE_idcode_to_name(ID_GD), gpd->id.name + 2); + changed = false; + } + } + else { + /* No layers/data */ + BKE_reportf(reports, RPT_ERROR, + "Can't apply to GP datablock with no layers: Object \"%s\", %s \"%s\", aborting", + ob->id.name + 2, BKE_idcode_to_name(ID_GD), gpd->id.name + 2); + } + } + } + if (ob->type == OB_LAMP) { Lamp *la = ob->data; if (la->type == LA_AREA) { @@ -587,6 +621,10 @@ static int apply_objects_internal( cu->fsize *= scale; } } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + BKE_gpencil_transform(gpd, mat); + } else if (ob->type == OB_CAMERA) { MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); @@ -1056,6 +1094,69 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) lt->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + float gpcenter[3]; + if (gpd) { + if (centermode == ORIGIN_TO_GEOMETRY) { + zero_v3(gpcenter); + BKE_gpencil_centroid_3D(gpd, gpcenter); + add_v3_v3(gpcenter, ob->obmat[3]); + } + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(gpcenter, cursor); + } + if ((centermode == ORIGIN_TO_GEOMETRY) || (centermode == ORIGIN_TO_CURSOR)) { + bGPDspoint *pt; + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + int i; + + sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all strokes (all layers are considered without evaluating lock attributtes) */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, 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) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); + } + } + } + } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + tot_change++; + if (centermode == ORIGIN_TO_GEOMETRY) { + copy_v3_v3(ob->loc, gpcenter); + } + ob->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else { + BKE_report(op->reports, RPT_WARNING, "Grease Pencil Object does not support this set origin option"); + } + } + } /* offset other selected objects */ if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 22000bd2a03..ed7950f3993 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -329,7 +329,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R GPU_matrix_translate_2f(sizex / 2, sizey / 2); G.f |= G_RENDER_OGL; - ED_gpencil_draw_ex(scene, gpd, sizex, sizey, scene->r.cfra, SPACE_SEQ); + ED_gpencil_draw_ex(rv3d, scene, gpd, sizex, sizey, scene->r.cfra, SPACE_SEQ); G.f &= ~G_RENDER_OGL; gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect"); @@ -417,7 +417,7 @@ static void screen_opengl_render_write(OGLRender *oglrender) else printf("OpenGL Render failed to write '%s'\n", name); } -static void addAlphaOverFloat(float dest[4], const float source[4]) +static void UNUSED_FUNCTION(addAlphaOverFloat)(float dest[4], const float source[4]) { /* d = s + (1-alpha_s)d*/ float mul; @@ -431,91 +431,6 @@ static void addAlphaOverFloat(float dest[4], const float source[4]) } -/* add renderlayer and renderpass for each grease pencil layer for using in composition */ -static void add_gpencil_renderpass(const bContext *C, OGLRender *oglrender, RenderResult *rr, RenderView *rv) -{ - bGPdata *gpd = oglrender->scene->gpd; - Scene *scene = oglrender->scene; - - /* sanity checks */ - if (gpd == NULL) { - return; - } - if (scene == NULL) { - return; - } - if (BLI_listbase_is_empty(&gpd->layers)) { - return; - } - if (oglrender->v3d != NULL && (oglrender->v3d->flag2 & V3D_SHOW_GPENCIL) == 0) { - return; - } - - /* save old alpha mode */ - short oldalphamode = scene->r.alphamode; - /* set alpha transparent for gp */ - scene->r.alphamode = R_ALPHAPREMUL; - - /* saves layer status */ - short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag"); - int i = 0; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - oldsts[i] = gpl->flag; - ++i; - } - /* loop all layers to create separate render */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* dont draw layer if hidden */ - if (gpl->flag & GP_LAYER_HIDE) - continue; - /* hide all layer except current */ - for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { - if (gpl != gph) { - gph->flag |= GP_LAYER_HIDE; - } - } - - /* render this gp layer */ - screen_opengl_render_doit(C, oglrender, rr); - - /* add RendePass composite */ - RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name); - - /* copy image data from rectf */ - // XXX: Needs conversion. - unsigned char *src = (unsigned char *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32; - if (src != NULL) { - float *dest = rp->rect; - - int x, y, rectx, recty; - rectx = rr->rectx; - recty = rr->recty; - for (y = 0; y < recty; y++) { - for (x = 0; x < rectx; x++) { - unsigned char *pixSrc = src + 4 * (rectx * y + x); - if (pixSrc[3] > 0) { - float *pixDest = dest + 4 * (rectx * y + x); - float float_src[4]; - srgb_to_linearrgb_uchar4(float_src, pixSrc); - addAlphaOverFloat(pixDest, float_src); - } - } - } - } - /* back layer status */ - i = 0; - for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { - gph->flag = oldsts[i]; - ++i; - } - } - /* free memory */ - MEM_freeN(oldsts); - - /* back default alpha mode */ - scene->r.alphamode = oldalphamode; -} - static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) { RenderResult *rr; @@ -550,11 +465,6 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) BLI_assert(view_id < oglrender->views_len); RE_SetActiveRenderView(oglrender->re, rv->name); oglrender->view_id = view_id; - /* add grease pencil passes. For sequencer, the render does not include renderpasses - * TODO: The sequencer render of grease pencil should be rethought */ - if (!oglrender->is_sequencer) { - add_gpencil_renderpass(C, oglrender, rr, rv); - } /* render composite */ screen_opengl_render_doit(C, oglrender, rr); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 3423eedf7ca..069611be35d 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -198,6 +198,7 @@ typedef struct IconPreview { static Main *G_pr_main = NULL; static Main *G_pr_main_cycles = NULL; +static Main *G_pr_main_grease_pencil = NULL; #ifndef WITH_HEADLESS static Main *load_main_from_memory(const void *blend, int blend_size) @@ -227,6 +228,7 @@ void ED_preview_ensure_dbase(void) if (!base_initialized) { G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size); G_pr_main_cycles = load_main_from_memory(datatoc_preview_cycles_blend, datatoc_preview_cycles_blend_size); + G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend, datatoc_preview_grease_pencil_blend_size); base_initialized = true; } #endif @@ -245,6 +247,9 @@ void ED_preview_free_dbase(void) if (G_pr_main_cycles) BKE_main_free(G_pr_main_cycles); + + if (G_pr_main_grease_pencil) + BKE_main_free(G_pr_main_grease_pencil); } static Scene *preview_get_scene(Main *pr_main) @@ -1102,6 +1107,7 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short sp->id_copy = ip->id_copy; sp->bmain = ip->bmain; sp->own_id_copy = false; + Material *ma = NULL; if (is_render) { BLI_assert(ip->id); @@ -1109,10 +1115,22 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short * so don't even think of using cycle's bmain for * texture icons */ - if (GS(ip->id->name) != ID_TE) - sp->pr_main = G_pr_main_cycles; - else + if (GS(ip->id->name) != ID_TE) { + /* grease pencil use its own preview file */ + if (GS(ip->id->name) == ID_MA) { + ma = (Material *)ip->id; + } + + if ((ma == NULL) || (ma->gp_style == NULL)) { + sp->pr_main = G_pr_main_cycles; + } + else { + sp->pr_main = G_pr_main_grease_pencil; + } + } + else { sp->pr_main = G_pr_main; + } } common_preview_startjob(sp, stop, do_update, progress); @@ -1274,11 +1292,23 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M sp->parent = parent; sp->slot = slot; sp->bmain = CTX_data_main(C); + Material *ma = NULL; /* hardcoded preview .blend for Eevee + Cycles, this should be solved * once with custom preview .blend path for external engines */ if ((method != PR_NODE_RENDER) && id_type != ID_TE) { - sp->pr_main = G_pr_main_cycles; + /* grease pencil use its own preview file */ + if (GS(id->name) == ID_MA) { + ma = (Material *)id; + } + + if ((ma == NULL) || (ma->gp_style == NULL)) { + sp->pr_main = G_pr_main_cycles; + } + else { + sp->pr_main = G_pr_main_grease_pencil; + } + } else { sp->pr_main = G_pr_main; diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 8077079a9b5..2dd4f328d06 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -469,6 +469,7 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) { Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); PointerRNA ptr, idptr; PropertyRNA *prop; @@ -477,7 +478,12 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_material_copy(bmain, ma); } else { - ma = BKE_material_add(bmain, DATA_("Material")); + if ((!ob) || (ob->type != OB_GPENCIL)) { + ma = BKE_material_add(bmain, DATA_("Material")); + } + else { + ma = BKE_material_add_gpencil(bmain, DATA_("Material")); + } ED_node_shader_default(C, &ma->id); ma->use_nodes = true; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 1a63bc1cd53..18bacee98b9 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1428,18 +1428,32 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand } if (flag & ED_KEYMAP_GPENCIL) { /* grease pencil */ - /* NOTE: This is now 2 keymaps - One for basic functionality, - * and one that only applies when "Edit Mode" is enabled - * for strokes. + /* NOTE: This is now 4 keymaps - One for basic functionality, + * and others for special stroke modes (edit, paint and sculpt). * - * For now, it's easier to just include both, - * since you hardly want one without the other. + * For now, it's easier to just include all, + * since you hardly want one without the others. */ wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0); - wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0); - WM_event_add_keymap_handler(handlers, keymap_general); + + wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0); WM_event_add_keymap_handler(handlers, keymap_edit); + + wmKeyMap *keymap_paint = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint); + + wmKeyMap *keymap_paint_draw = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_draw); + + wmKeyMap *keymap_paint_erase = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_erase); + + wmKeyMap *keymap_paint_fill = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_fill); + + wmKeyMap *keymap_sculpt = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt); } if (flag & ED_KEYMAP_HEADER) { /* standard keymap for headers regions */ diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 17b1af29010..ecfc9f2cca0 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -34,6 +34,7 @@ #include "DNA_object_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" @@ -44,10 +45,13 @@ #include "BLI_utildefines.h" +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_object.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_paint.h" +#include "BKE_main.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_screen.h" @@ -80,8 +84,7 @@ const char *screen_context_dir[] = { "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", - "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette", - "active_gpencil_palettecolor", "active_gpencil_brush", + "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_brush", "active_operator", "selected_editable_fcurves", NULL}; @@ -467,7 +470,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult * (as outlined above - see Campbell's #ifdefs). That causes the get_active function to fail when * called from context. For that reason, we end up using an alternative where we pass everything in! */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { CTX_data_id_pointer_set(result, &gpd->id); @@ -482,7 +485,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult PointerRNA ptr; /* get pointer to Grease Pencil Data */ - gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, scene, sa, obact, &ptr); + gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, sa, scene, obact, &ptr); if (gpd_ptr) { CTX_data_pointer_set(result, ptr.id.data, ptr.type, ptr.data); @@ -491,7 +494,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "active_gpencil_layer")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -502,47 +505,17 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } - else if (CTX_data_equals(member, "active_gpencil_palette")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); - - if (gpd) { - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - - if (palette) { - CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette); - return 1; - } - } - } - else if (CTX_data_equals(member, "active_gpencil_palettecolor")) { - /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); - - if (gpd) { - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - - if (palette) { - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - if (palcolor) { - CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor); - return 1; - } - } - } - } else if (CTX_data_equals(member, "active_gpencil_brush")) { - /* XXX: see comment for gpencil_data case... */ - bGPDbrush *brush = BKE_gpencil_brush_getactive(scene->toolsettings); + Brush *brush = BKE_brush_getactive_gpencil(scene->toolsettings); if (brush) { - CTX_data_pointer_set(result, &scene->id, &RNA_GPencilBrush, brush); + CTX_data_pointer_set(result, &scene->id, &RNA_Brush, brush); return 1; } } else if (CTX_data_equals(member, "active_gpencil_frame")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -555,7 +528,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "visible_gpencil_layers")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl; @@ -571,7 +544,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_gpencil_layers")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl; @@ -587,24 +560,37 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_gpencil_strokes")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd) { bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { if (gpencil_layer_is_editable(gpl) && (gpl->actframe)) { - bGPDframe *gpf = gpl->actframe; + bGPDframe *gpf; bGPDstroke *gps; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - for (gps = gpf->strokes.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use_direct(sa, gps)) { - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { - continue; + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use_direct(sa, gps)) { + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { + continue; + } + + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + } } - - CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; } } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index a837b32b0bb..df909794353 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2608,11 +2608,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) /* populate tree with keyframe nodes */ scene_to_keylist(&ads, scene, &keys, NULL); - gpencil_to_keylist(&ads, scene->gpd, &keys); if (ob) { ob_to_keylist(&ads, ob, &keys, NULL); - gpencil_to_keylist(&ads, ob->gpd, &keys); + + if (ob->type == OB_GPENCIL) { + const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY); + gpencil_to_keylist(&ads, ob->data, &keys, active); + } } { diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 0f796020d9e..86d36ade477 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -29,20 +29,27 @@ #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_math_color.h" #include "DNA_customdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" #include "ED_paint.h" #include "ED_screen.h" #include "ED_image.h" +#include "ED_gpencil.h" #include "UI_resources.h" #include "WM_api.h" @@ -96,6 +103,43 @@ static void BRUSH_OT_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /*int type = RNA_enum_get(op->ptr, "type");*/ + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = &ts->gp_paint->paint; + Brush *br = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + // ePaintMode mode = ePaintGpencil; + + if (br) { + br = BKE_brush_copy(bmain, br); + } + else { + br = BKE_brush_add(bmain, "Brush", OB_MODE_GPENCIL_PAINT); + id_us_min(&br->id); /* fake user only */ + } + + BKE_paint_brush_set(paint, br); + + /* TODO init grease pencil specific data */ + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_add_gpencil(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Drawing Brush"; + ot->description = "Add brush for grease pencil"; + ot->idname = "BRUSH_OT_add_gpencil"; + + /* api callbacks */ + ot->exec = brush_add_gpencil_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} static int brush_scale_size_exec(bContext *C, wmOperator *op) { @@ -232,7 +276,6 @@ static void PALETTE_OT_color_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1031,6 +1074,7 @@ void ED_operatortypes_paint(void) /* brush */ WM_operatortype_append(BRUSH_OT_add); + WM_operatortype_append(BRUSH_OT_add_gpencil); WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); WM_operatortype_append(BRUSH_OT_reset); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index c46d0fdb035..831c461538a 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -51,6 +51,7 @@ #include "BKE_fcurve.h" #include "BKE_nla.h" #include "BKE_context.h" +#include "BKE_gpencil.h" #include "UI_view2d.h" @@ -134,15 +135,20 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) /* Now set the flags */ for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frame_select_set(ale->data, sel); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frame_select_set(ale->data, sel); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL); + } } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -283,12 +289,16 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { ED_gplayer_frames_select_border(gpl, rectf.xmin, rectf.xmax, selectmode); } + ale->update |= ANIM_UPDATE_DEPS; break; } #endif case ANIMTYPE_GPLAYER: + { ED_gplayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode); + ale->update |= ANIM_UPDATE_DEPS; break; + } case ANIMTYPE_MASKDATABLOCK: { Mask *mask = ale->data; @@ -312,6 +322,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s } /* cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -493,6 +504,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, case ANIMTYPE_GPLAYER: { ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode); + ale->update |= ANIM_UPDATE_DEPS; break; } case ANIMTYPE_MASKDATABLOCK: @@ -520,6 +532,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, } /* cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -707,6 +720,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } else if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frames_select_border(ale->data, min, max, SELECT_ADD); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frames_select_border(ale->data, min, max, SELECT_ADD); @@ -717,6 +731,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -796,17 +811,23 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) ked.f1 = ce->cfra; /* select elements with frame number matching cfraelem */ - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, ce->cfra, SELECT_ADD); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, ce->cfra, SELECT_ADD); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } } /* free elements */ BLI_freelistN(&ked.list); + + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1081,12 +1102,16 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } - else if (ale->type == ANIMTYPE_GPLAYER) + else if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } /* Sync marker support */ @@ -1111,6 +1136,7 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1227,6 +1253,7 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s /* select the nominated keyframe on the given frame */ if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, selx, select_mode); @@ -1244,12 +1271,14 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, selx, select_mode); } } + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } else { @@ -1294,16 +1323,22 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se ked.f1 = selx; /* select elements with frame number matching cfra */ - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->key_data, selx, select_mode); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->key_data, selx, select_mode); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } /* free elements */ BLI_freelistN(&ked.list); + + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1318,6 +1353,7 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s /* select all keyframes in this channel */ if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frames(ale->data, select_mode); + ale->update = ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frames(ale->data, select_mode); @@ -1335,12 +1371,14 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frames(ale->data, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frames(ale->data, select_mode); } } + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } else { @@ -1473,6 +1511,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ /* remove active channel from list of channels for separate treatment (since it's needed later on) */ BLI_remlink(&anim_data, ale); + ale->next = ale->prev = NULL; /* cleanup temporary lists */ BLI_dlrbTree_free(&anim_keys); @@ -1557,6 +1596,12 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ } } + /* flush tagged updates + * NOTE: We temporarily add this channel back to the list so that this can happen + */ + anim_data.first = anim_data.last = ale; + ANIM_animdata_update(ac, &anim_data); + /* free this channel */ MEM_freeN(ale); } diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index faee9c2b7ac..67632f6a53a 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -237,6 +237,7 @@ static int buttons_context_path_data(ButsContextPath *path, int type) else if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) return 1; + else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) return 1; /* try to get an object in the path, no pinning supported here */ else if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; @@ -260,7 +261,21 @@ static int buttons_context_path_modifier(ButsContextPath *path) if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; - if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE)) + if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE, OB_GPENCIL)) + return 1; + } + + return 0; +} + +static int buttons_context_path_shaderfx(ButsContextPath *path) +{ + Object *ob; + + if (buttons_context_path_object(path)) { + ob = path->ptr[path->len - 1].data; + + if (ob && ELEM(ob->type, OB_GPENCIL)) return 1; } @@ -485,6 +500,7 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma WorkSpace *workspace = CTX_wm_workspace(C); ID *id; int found; + Object *ob = CTX_data_active_object(C); memset(path, 0, sizeof(*path)); path->flag = flag; @@ -546,6 +562,9 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma case BCONTEXT_MODIFIER: found = buttons_context_path_modifier(path); break; + case BCONTEXT_SHADERFX: + found = buttons_context_path_shaderfx(path); + break; case BCONTEXT_DATA: found = buttons_context_path_data(path, -1); break; @@ -553,7 +572,14 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma found = buttons_context_path_particle(path); break; case BCONTEXT_MATERIAL: - found = buttons_context_path_material(path); + /* NOTE: Grease Pencil materials use different panels... */ + if (ob && ob->type == OB_GPENCIL) { + /* XXX: Why path_data? */ + found = buttons_context_path_data(path, -1); + } + else { + found = buttons_context_path_material(path); + } break; case BCONTEXT_TEXTURE: found = buttons_context_path_texture(C, path, sbuts->texuser); @@ -626,10 +652,18 @@ void buttons_context_compute(const bContext *C, SpaceButs *sbuts) if (a == BCONTEXT_DATA) { ptr = &path->ptr[path->len - 1]; - if (ptr->type) + if (ptr->type) { sbuts->dataicon = RNA_struct_ui_icon(ptr->type); - else - sbuts->dataicon = ICON_EMPTY_DATA; + } + else { + Object *ob = CTX_data_active_object(C); + if (ob->type == OB_GPENCIL) { + sbuts->dataicon = ICON_GREASEPENCIL; + } + else { + sbuts->dataicon = ICON_EMPTY_DATA; + } + } } } } diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 66684de18ac..de422565abd 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -55,6 +55,7 @@ #include "BKE_layer.h" #include "BKE_linestyle.h" #include "BKE_modifier.h" +#include "BKE_gpencil_modifier.h" #include "BKE_node.h" #include "BKE_paint.h" #include "BKE_particle.h" @@ -152,6 +153,19 @@ static void buttons_texture_modifier_foreach(void *userData, Object *ob, Modifie N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); } +static void buttons_texture_modifier_gpencil_foreach(void *userData, Object *ob, GpencilModifierData *md, const char *propname) +{ + PointerRNA ptr; + PropertyRNA *prop; + ListBase *users = userData; + + RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr); + prop = RNA_struct_find_property(&ptr, propname); + + buttons_texture_user_property_add(users, &ob->id, ptr, prop, + N_("Grease Pencil Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); +} + static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts) { Scene *scene = NULL; @@ -203,6 +217,9 @@ static void buttons_texture_users_from_context(ListBase *users, const bContext * /* modifiers */ modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users); + /* grease pencil modifiers */ + BKE_gpencil_modifiers_foreachTexLink(ob, buttons_texture_modifier_gpencil_foreach, users); + /* particle systems */ if (psys && !limited_mode) { for (a = 0; a < MAX_MTEX; a++) { diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 6f7a4ca971a..62115aea11d 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -180,6 +180,9 @@ static void buttons_main_region_layout_properties(const bContext *C, SpaceButs * case BCONTEXT_MODIFIER: contexts[0] = "modifier"; break; + case BCONTEXT_SHADERFX: + contexts[0] = "shaderfx"; + break; case BCONTEXT_CONSTRAINT: contexts[0] = "constraint"; break; @@ -200,8 +203,9 @@ static void buttons_main_region_layout_tool(const bContext *C, ARegion *ar) const char *contexts[3] = {NULL}; const WorkSpace *workspace = CTX_wm_workspace(C); + const int mode = CTX_data_mode_enum(C); + if (workspace->tools_space_type == SPACE_VIEW3D) { - const int mode = CTX_data_mode_enum(C); switch (mode) { case CTX_MODE_EDIT_MESH: ARRAY_SET_ITEMS(contexts, ".mesh_edit"); @@ -245,12 +249,39 @@ static void buttons_main_region_layout_tool(const bContext *C, ARegion *ar) case CTX_MODE_OBJECT: ARRAY_SET_ITEMS(contexts, ".objectmode"); break; + case CTX_MODE_GPENCIL_PAINT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_GPENCIL_SCULPT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_GPENCIL_WEIGHT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; } } else if (workspace->tools_space_type == SPACE_IMAGE) { /* TODO */ } + /* for grease pencil we don't use tool system yet, so we need check outside + * workspace->tools_space_type because this value is not available + */ + switch (mode) { + case CTX_MODE_GPENCIL_PAINT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_GPENCIL_SCULPT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_GPENCIL_WEIGHT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; + case CTX_MODE_GPENCIL_EDIT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_edit"); + break; + } + const bool vertical = true; ED_region_panels_layout_ex(C, ar, contexts, -1, vertical); } @@ -495,6 +526,14 @@ static void buttons_area_listener( break; } break; + case NC_GPENCIL: + switch(wmn->data) { + case ND_DATA: + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) + ED_area_tag_redraw(sa); + break; + } + break; case NC_NODE: if (wmn->action == NA_SELECTED) { ED_area_tag_redraw(sa); diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index db5f6c2451c..6953b7cfb71 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -107,7 +107,7 @@ void uiTemplateMovieClip(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiLayoutSetContextPointer(layout, "edit_movieclip", &clipptr); if (!compact) - uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (clip) { uiLayout *col; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 2b98ff43c5f..725c2b7fa6d 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -236,7 +236,7 @@ static SpaceLink *clip_new(const ScrArea *sa, const Scene *scene) sc = MEM_callocN(sizeof(SpaceClip), "initclip"); sc->spacetype = SPACE_CLIP; sc->flag = SC_SHOW_MARKER_PATTERN | SC_SHOW_TRACK_PATH | - SC_SHOW_GRAPH_TRACKS_MOTION | SC_SHOW_GRAPH_FRAMES | SC_SHOW_GPENCIL; + SC_SHOW_GRAPH_TRACKS_MOTION | SC_SHOW_GRAPH_FRAMES | SC_SHOW_ANNOTATION; sc->zoom = 1.0f; sc->path_length = 20; sc->scopes.track_preview_height = 120; @@ -1196,7 +1196,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *ar) clip_draw_cache_and_notes(C, sc, ar); - if (sc->flag & SC_SHOW_GPENCIL) { + if (sc->flag & SC_SHOW_ANNOTATION) { /* Grease Pencil */ clip_draw_grease_pencil((bContext *)C, true); } @@ -1204,7 +1204,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *ar) /* reset view matrix */ UI_view2d_view_restore(C); - if (sc->flag & SC_SHOW_GPENCIL) { + if (sc->flag & SC_SHOW_ANNOTATION) { /* draw Grease Pencil - screen space only */ clip_draw_grease_pencil((bContext *)C, false); } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 1d8c6721b64..afcae7a27ab 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -876,7 +876,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (!compact) { uiTemplateID( layout, C, ptr, propname, - ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } if (ima) { diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 8aa37bb5e16..fdf9d6df374 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -32,6 +32,7 @@ #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -55,6 +56,7 @@ #include "BKE_particle.h" #include "BKE_editmesh.h" #include "BKE_object.h" +#include "BKE_gpencil.h" #include "ED_info.h" #include "ED_armature.h" @@ -72,6 +74,7 @@ typedef struct SceneStats { int totobj, totobjsel; int totlamp, totlampsel; int tottri; + int totgplayer, totgpframe, totgpstroke, totgppoint; char infostr[MAX_INFO_LEN]; } SceneStats; @@ -85,6 +88,8 @@ typedef struct SceneStatsFmt { char totobj[MAX_INFO_NUM_LEN], totobjsel[MAX_INFO_NUM_LEN]; char totlamp[MAX_INFO_NUM_LEN], totlampsel[MAX_INFO_NUM_LEN]; char tottri[MAX_INFO_NUM_LEN]; + char totgplayer[MAX_INFO_NUM_LEN], totgpframe[MAX_INFO_NUM_LEN]; + char totgpstroke[MAX_INFO_NUM_LEN], totgppoint[MAX_INFO_NUM_LEN]; } SceneStatsFmt; static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) @@ -144,6 +149,20 @@ static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) } break; } + case OB_GPENCIL: + { + bGPdata *gpd = (bGPdata *)ob->data; + /* GPXX Review if we can move to other place when object change + * maybe to depsgraph evaluation + */ + BKE_gpencil_stats_update(gpd); + + stats->totgplayer = gpd->totlayer; + stats->totgpframe = gpd->totframe; + stats->totgpstroke = gpd->totstroke; + stats->totgppoint = gpd->totpoint; + break; + } } } @@ -442,6 +461,11 @@ static void stats_string(ViewLayer *view_layer) SCENE_STATS_FMT_INT(tottri); + SCENE_STATS_FMT_INT(totgplayer); + SCENE_STATS_FMT_INT(totgpframe); + SCENE_STATS_FMT_INT(totgpstroke); + SCENE_STATS_FMT_INT(totgppoint); + #undef SCENE_STATS_FMT_INT @@ -501,6 +525,14 @@ static void stats_string(ViewLayer *view_layer) ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s%s"), stats_fmt.totbonesel, stats_fmt.totbone, memstr, gpumemstr); } + else if ((ob) && (ob->type == OB_GPENCIL)) { + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, + IFACE_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), + stats_fmt.totgplayer, stats_fmt.totgpframe, stats_fmt.totgpstroke, stats_fmt.totgppoint); + + ofs += BLI_strncpy_rlen(s + ofs, memstr, MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(s + ofs, gpumemstr, MAX_INFO_LEN - ofs); + } else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s%s"), stats_fmt.totvert, stats_fmt.tottri, gpumemstr); diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 57464cbf092..40caf919848 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -145,6 +145,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: case ANIMTYPE_DSGPENCIL: + case ANIMTYPE_PALETTE: { /* for these channels, we only do AnimData */ if (ale->adt && adt_ptr) { @@ -287,7 +288,7 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) row = uiLayoutRow(layout, true); uiTemplateID( row, (bContext *)C, &adt_ptr, "action", - "ACTION_OT_new", NULL, "NLA_OT_action_unlink", UI_TEMPLATE_ID_FILTER_ALL); + "ACTION_OT_new", NULL, "NLA_OT_action_unlink", UI_TEMPLATE_ID_FILTER_ALL, false); /* extrapolation */ row = uiLayoutRow(layout, true); diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 3368ad4fe8d..51177a77f0d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -182,6 +182,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: case ANIMTYPE_DSGPENCIL: + case ANIMTYPE_PALETTE: { /* sanity checking... */ if (ale->adt) { diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index d1ad8cb396c..f284fa015b8 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -744,7 +744,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE); uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE); uiItemR(layout, ptr, "projection", 0, "", ICON_NONE); @@ -775,7 +775,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false); @@ -793,7 +793,7 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!ima) return; @@ -1274,7 +1274,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; imaptr = RNA_pointer_get(ptr, "image"); @@ -1304,7 +1304,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer const char *layer_name; char scene_name[MAX_ID_NAME - 2]; - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -1418,7 +1418,7 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "use_preview", 0, NULL, ICON_NONE); - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "use_zbuffer", 0, NULL, ICON_NONE); @@ -1985,7 +1985,7 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1993,7 +1993,7 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point bNode *node = ptr->data; PointerRNA clipptr; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2007,7 +2007,7 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2031,7 +2031,7 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2339,7 +2339,7 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); uiItemR(layout, ptr, "use_antialiasing", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "use_feather", 0, NULL, ICON_NONE); @@ -2361,7 +2361,7 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2397,7 +2397,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2437,7 +2437,7 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P bNode *node = ptr->data; NodePlaneTrackDeformData *data = node->storage; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2838,7 +2838,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 435e0018ac0..feab82a59c8 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -32,6 +32,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_lightprobe_types.h" @@ -50,6 +51,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_fcurve.h" +#include "BKE_gpencil.h" #include "BKE_global.h" #include "BKE_idcode.h" #include "BKE_layer.h" @@ -438,12 +440,16 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) } case TSE_GP_LAYER: { - bGPdata *gpd = (bGPdata *)tselem->id; // id = GP Datablock + bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */ bGPDlayer *gpl = te->directdata; + /* always make layer active */ + BKE_gpencil_layer_setactive(gpd, gpl); + // XXX: name needs translation stuff BLI_uniquename(&gpd->layers, gpl, "GP Layer", '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, gpd); break; } @@ -872,39 +878,6 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } -static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) -{ - /* restrict column clip - skip it for now... */ - if (arg->x >= arg->xmax) { - /* pass */ - } - else { - PointerRNA ptr; - const float eps = 0.001f; - const bool is_stroke_visible = (gpl->color[3] > eps); - const bool is_fill_visible = (gpl->fill[3] > eps); - float w = 0.5f * UI_UNIT_X; - float h = 0.85f * UI_UNIT_Y; - - RNA_pointer_create(id, &RNA_GPencilLayer, gpl, &ptr); - - UI_block_align_begin(arg->block); - - UI_block_emboss_set(arg->block, is_stroke_visible ? UI_EMBOSS : UI_EMBOSS_NONE); - uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb, arg->yb, w, h, - &ptr, "color", -1, - 0, 0, 0, 0, NULL); - - UI_block_emboss_set(arg->block, is_fill_visible ? UI_EMBOSS : UI_EMBOSS_NONE); - uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb + w, arg->yb, w, h, - &ptr, "fill_color", -1, - 0, 0, 0, 0, NULL); - - UI_block_emboss_set(arg->block, UI_EMBOSS_NONE); - UI_block_align_end(arg->block); - } -} - static void tselem_draw_icon( uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te, float alpha, const bool is_clickable) @@ -969,156 +942,212 @@ static void tselem_draw_icon( case TSE_MODIFIER: { Object *ob = (Object *)tselem->id; - ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr); - switch ((ModifierType)md->type) { - case eModifierType_Subsurf: - ICON_DRAW(ICON_MOD_SUBSURF); - break; - case eModifierType_Armature: - ICON_DRAW(ICON_MOD_ARMATURE); - break; - case eModifierType_Lattice: - ICON_DRAW(ICON_MOD_LATTICE); - break; - case eModifierType_Curve: - ICON_DRAW(ICON_MOD_CURVE); - break; - case eModifierType_Build: - ICON_DRAW(ICON_MOD_BUILD); - break; - case eModifierType_Mirror: - ICON_DRAW(ICON_MOD_MIRROR); - break; - case eModifierType_Decimate: - ICON_DRAW(ICON_MOD_DECIM); - break; - case eModifierType_Wave: - ICON_DRAW(ICON_MOD_WAVE); - break; - case eModifierType_Hook: - ICON_DRAW(ICON_HOOK); - break; - case eModifierType_Softbody: - ICON_DRAW(ICON_MOD_SOFT); - break; - case eModifierType_Boolean: - ICON_DRAW(ICON_MOD_BOOLEAN); - break; - case eModifierType_ParticleSystem: - ICON_DRAW(ICON_MOD_PARTICLES); - break; - case eModifierType_ParticleInstance: - ICON_DRAW(ICON_MOD_PARTICLES); - break; - case eModifierType_EdgeSplit: - ICON_DRAW(ICON_MOD_EDGESPLIT); - break; - case eModifierType_Array: - ICON_DRAW(ICON_MOD_ARRAY); - break; - case eModifierType_UVProject: - case eModifierType_UVWarp: /* TODO, get own icon */ - ICON_DRAW(ICON_MOD_UVPROJECT); - break; - case eModifierType_Displace: - ICON_DRAW(ICON_MOD_DISPLACE); - break; - case eModifierType_Shrinkwrap: - ICON_DRAW(ICON_MOD_SHRINKWRAP); - break; - case eModifierType_Cast: - ICON_DRAW(ICON_MOD_CAST); - break; - case eModifierType_MeshDeform: - case eModifierType_SurfaceDeform: - ICON_DRAW(ICON_MOD_MESHDEFORM); - break; - case eModifierType_Bevel: - ICON_DRAW(ICON_MOD_BEVEL); - break; - case eModifierType_Smooth: - case eModifierType_LaplacianSmooth: - case eModifierType_CorrectiveSmooth: - ICON_DRAW(ICON_MOD_SMOOTH); - break; - case eModifierType_SimpleDeform: - ICON_DRAW(ICON_MOD_SIMPLEDEFORM); - break; - case eModifierType_Mask: - ICON_DRAW(ICON_MOD_MASK); - break; - case eModifierType_Cloth: - ICON_DRAW(ICON_MOD_CLOTH); - break; - case eModifierType_Explode: - ICON_DRAW(ICON_MOD_EXPLODE); - break; - case eModifierType_Collision: - case eModifierType_Surface: - ICON_DRAW(ICON_MOD_PHYSICS); - break; - case eModifierType_Fluidsim: - ICON_DRAW(ICON_MOD_FLUIDSIM); - break; - case eModifierType_Multires: - ICON_DRAW(ICON_MOD_MULTIRES); - break; - case eModifierType_Smoke: - ICON_DRAW(ICON_MOD_SMOKE); - break; - case eModifierType_Solidify: - ICON_DRAW(ICON_MOD_SOLIDIFY); - break; - case eModifierType_Screw: - ICON_DRAW(ICON_MOD_SCREW); - break; - case eModifierType_Remesh: - ICON_DRAW(ICON_MOD_REMESH); - break; - case eModifierType_WeightVGEdit: - case eModifierType_WeightVGMix: - case eModifierType_WeightVGProximity: - ICON_DRAW(ICON_MOD_VERTEX_WEIGHT); - break; - case eModifierType_DynamicPaint: - ICON_DRAW(ICON_MOD_DYNAMICPAINT); - break; - case eModifierType_Ocean: - ICON_DRAW(ICON_MOD_OCEAN); - break; - case eModifierType_Warp: - ICON_DRAW(ICON_MOD_WARP); - break; - case eModifierType_Skin: - ICON_DRAW(ICON_MOD_SKIN); - break; - case eModifierType_Triangulate: - ICON_DRAW(ICON_MOD_TRIANGULATE); - break; - case eModifierType_MeshCache: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_MeshSequenceCache: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_Wireframe: - ICON_DRAW(ICON_MOD_WIREFRAME); - break; - case eModifierType_LaplacianDeform: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_DataTransfer: - ICON_DRAW(ICON_MOD_DATA_TRANSFER); - break; - case eModifierType_NormalEdit: - ICON_DRAW(ICON_MOD_NORMALEDIT); - break; - /* Default */ - case eModifierType_None: - case eModifierType_ShapeKey: - case NUM_MODIFIER_TYPES: - ICON_DRAW(ICON_DOT); - break; + if (ob->type != OB_GPENCIL) { + ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr); + switch ((ModifierType)md->type) { + case eModifierType_Subsurf: + ICON_DRAW(ICON_MOD_SUBSURF); + break; + case eModifierType_Armature: + ICON_DRAW(ICON_MOD_ARMATURE); + break; + case eModifierType_Lattice: + ICON_DRAW(ICON_MOD_LATTICE); + break; + case eModifierType_Curve: + ICON_DRAW(ICON_MOD_CURVE); + break; + case eModifierType_Build: + ICON_DRAW(ICON_MOD_BUILD); + break; + case eModifierType_Mirror: + ICON_DRAW(ICON_MOD_MIRROR); + break; + case eModifierType_Decimate: + ICON_DRAW(ICON_MOD_DECIM); + break; + case eModifierType_Wave: + ICON_DRAW(ICON_MOD_WAVE); + break; + case eModifierType_Hook: + ICON_DRAW(ICON_HOOK); + break; + case eModifierType_Softbody: + ICON_DRAW(ICON_MOD_SOFT); + break; + case eModifierType_Boolean: + ICON_DRAW(ICON_MOD_BOOLEAN); + break; + case eModifierType_ParticleSystem: + ICON_DRAW(ICON_MOD_PARTICLES); + break; + case eModifierType_ParticleInstance: + ICON_DRAW(ICON_MOD_PARTICLES); + break; + case eModifierType_EdgeSplit: + ICON_DRAW(ICON_MOD_EDGESPLIT); + break; + case eModifierType_Array: + ICON_DRAW(ICON_MOD_ARRAY); + break; + case eModifierType_UVProject: + case eModifierType_UVWarp: /* TODO, get own icon */ + ICON_DRAW(ICON_MOD_UVPROJECT); + break; + case eModifierType_Displace: + ICON_DRAW(ICON_MOD_DISPLACE); + break; + case eModifierType_Shrinkwrap: + ICON_DRAW(ICON_MOD_SHRINKWRAP); + break; + case eModifierType_Cast: + ICON_DRAW(ICON_MOD_CAST); + break; + case eModifierType_MeshDeform: + case eModifierType_SurfaceDeform: + ICON_DRAW(ICON_MOD_MESHDEFORM); + break; + case eModifierType_Bevel: + ICON_DRAW(ICON_MOD_BEVEL); + break; + case eModifierType_Smooth: + case eModifierType_LaplacianSmooth: + case eModifierType_CorrectiveSmooth: + ICON_DRAW(ICON_MOD_SMOOTH); + break; + case eModifierType_SimpleDeform: + ICON_DRAW(ICON_MOD_SIMPLEDEFORM); + break; + case eModifierType_Mask: + ICON_DRAW(ICON_MOD_MASK); + break; + case eModifierType_Cloth: + ICON_DRAW(ICON_MOD_CLOTH); + break; + case eModifierType_Explode: + ICON_DRAW(ICON_MOD_EXPLODE); + break; + case eModifierType_Collision: + case eModifierType_Surface: + ICON_DRAW(ICON_MOD_PHYSICS); + break; + case eModifierType_Fluidsim: + ICON_DRAW(ICON_MOD_FLUIDSIM); + break; + case eModifierType_Multires: + ICON_DRAW(ICON_MOD_MULTIRES); + break; + case eModifierType_Smoke: + ICON_DRAW(ICON_MOD_SMOKE); + break; + case eModifierType_Solidify: + ICON_DRAW(ICON_MOD_SOLIDIFY); + break; + case eModifierType_Screw: + ICON_DRAW(ICON_MOD_SCREW); + break; + case eModifierType_Remesh: + ICON_DRAW(ICON_MOD_REMESH); + break; + case eModifierType_WeightVGEdit: + case eModifierType_WeightVGMix: + case eModifierType_WeightVGProximity: + ICON_DRAW(ICON_MOD_VERTEX_WEIGHT); + break; + case eModifierType_DynamicPaint: + ICON_DRAW(ICON_MOD_DYNAMICPAINT); + break; + case eModifierType_Ocean: + ICON_DRAW(ICON_MOD_OCEAN); + break; + case eModifierType_Warp: + ICON_DRAW(ICON_MOD_WARP); + break; + case eModifierType_Skin: + ICON_DRAW(ICON_MOD_SKIN); + break; + case eModifierType_Triangulate: + ICON_DRAW(ICON_MOD_TRIANGULATE); + break; + case eModifierType_MeshCache: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_MeshSequenceCache: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_Wireframe: + ICON_DRAW(ICON_MOD_WIREFRAME); + break; + case eModifierType_LaplacianDeform: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_DataTransfer: + ICON_DRAW(ICON_MOD_DATA_TRANSFER); + break; + case eModifierType_NormalEdit: + ICON_DRAW(ICON_MOD_NORMALEDIT); + break; + /* Default */ + case eModifierType_None: + case eModifierType_ShapeKey: + + case NUM_MODIFIER_TYPES: + ICON_DRAW(ICON_DOT); + break; + } + } + else { + /* grease pencil modifiers */ + GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr); + switch ((GpencilModifierType)md->type) { + case eGpencilModifierType_Noise: + ICON_DRAW(ICON_RNDCURVE); + break; + case eGpencilModifierType_Subdiv: + ICON_DRAW(ICON_MOD_SUBSURF); + break; + case eGpencilModifierType_Thick: + ICON_DRAW(ICON_MAN_ROT); + break; + case eGpencilModifierType_Tint: + ICON_DRAW(ICON_COLOR); + break; + case eGpencilModifierType_Instance: + ICON_DRAW(ICON_MOD_ARRAY); + break; + case eGpencilModifierType_Build: + ICON_DRAW(ICON_MOD_BUILD); + break; + case eGpencilModifierType_Opacity: + ICON_DRAW(ICON_MOD_MASK); + break; + case eGpencilModifierType_Color: + ICON_DRAW(ICON_GROUP_VCOL); + break; + case eGpencilModifierType_Lattice: + ICON_DRAW(ICON_MOD_LATTICE); + break; + case eGpencilModifierType_Mirror: + ICON_DRAW(ICON_MOD_MIRROR); + break; + case eGpencilModifierType_Simplify: + ICON_DRAW(ICON_MOD_DECIM); + break; + case eGpencilModifierType_Smooth: + ICON_DRAW(ICON_MOD_SMOOTH); + break; + case eGpencilModifierType_Hook: + ICON_DRAW(ICON_HOOK); + break; + case eGpencilModifierType_Offset: + ICON_DRAW(ICON_MOD_DISPLACE); + break; + + /* Default */ + default: + ICON_DRAW(ICON_DOT); + break; + } } break; } @@ -1185,11 +1214,18 @@ static void tselem_draw_icon( ICON_DRAW(ICON_GROUP); break; /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */ -#if 0 case TSE_GP_LAYER: - tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata); + { + /* indicate whether layer is active */ + bGPDlayer *gpl = te->directdata; + if (gpl->flag & GP_LAYER_ACTIVE) { + ICON_DRAW(ICON_GREASEPENCIL); + } + else { + ICON_DRAW(ICON_DOT); + } break; -#endif + } default: ICON_DRAW(ICON_DOT); break; @@ -1229,6 +1265,9 @@ static void tselem_draw_icon( ICON_CLICK_DRAW(ICON_OUTLINER_OB_EMPTY); } break; + case OB_GPENCIL: + ICON_CLICK_DRAW(ICON_OUTLINER_OB_GREASEPENCIL); break; + break; } } else { @@ -1304,7 +1343,7 @@ static void tselem_draw_icon( case ID_LS: tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; case ID_GD: - tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_GREASEPENCIL); break; case ID_LP: { LightProbe * lp = (LightProbe *)tselem->id; diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 7ab13f36953..ec5e11520a6 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -39,6 +39,7 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_world_types.h" +#include "DNA_gpencil_types.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" @@ -46,9 +47,11 @@ #include "BKE_armature.h" #include "BKE_collection.h" #include "BKE_context.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_workspace.h" @@ -60,6 +63,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_undo.h" +#include "ED_gpencil.h" #include "WM_api.h" #include "WM_types.h" @@ -470,6 +474,28 @@ static eOLDrawState tree_element_active_defgroup( return OL_DRAWSEL_NONE; } +static eOLDrawState UNUSED_FUNCTION(tree_element_active_gplayer)( + bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) +{ + bGPdata *gpd = (bGPdata *)tselem->id; + bGPDlayer *gpl = te->directdata; + + /* We can only have a single "active" layer at a time + * and there must always be an active layer... + */ + if (set != OL_SETSEL_NONE) { + if (gpl) { + BKE_gpencil_layer_setactive(gpd, gpl); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd); + } + } + else { + return OL_DRAWSEL_NORMAL; + } + + return OL_DRAWSEL_NONE; +} + static eOLDrawState tree_element_active_posegroup( bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) { @@ -1006,6 +1032,10 @@ static void do_outliner_item_activate_tree_element( } } } + else if (ELEM(te->idcode, ID_GD)) { + /* set grease pencil to object mode */ + WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); + } else { // rest of types tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 28890e42139..539df3aa085 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -319,8 +319,6 @@ static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *s if (outliner_animdata_test(sce->adt)) outliner_add_element(soops, lb, sce, te, TSE_ANIM_DATA, 0); - /* Grease Pencil */ - outliner_add_element(soops, lb, sce->gpd, te, 0, 0); } TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata) diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index e45159124e8..6113922c02e 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -168,6 +168,10 @@ static void topbar_main_region_listener(wmWindow *UNUSED(win), ScrArea *UNUSED(s if (wmn->data == ND_SPACE_VIEW3D) ED_region_tag_redraw(ar); break; + case NC_GPENCIL: + if (wmn->data == ND_DATA) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index c0abbe636c3..3649c6f6dbb 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -80,6 +80,7 @@ #include "BKE_subsurf.h" #include "BKE_unit.h" #include "BKE_tracking.h" +#include "BKE_gpencil.h" #include "BKE_editmesh.h" diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 2577077002e..c1776ef18e7 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -36,6 +36,7 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" @@ -335,12 +336,18 @@ static SpaceLink *view3d_new(const ScrArea *UNUSED(sa), const Scene *scene) v3d->gridflag = V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_FLOOR; v3d->flag = V3D_SELECT_OUTLINE; - v3d->flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_GPENCIL; + v3d->flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_ANNOTATION; v3d->lens = 50.0f; v3d->near = 0.01f; v3d->far = 1000.0f; + v3d->overlay.gpencil_grid_scale = 1.0; // Scales + v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES; // NUmber of Lines + v3d->overlay.gpencil_paper_opacity = 0.5f; + v3d->overlay.gpencil_grid_axis = V3D_GP_GRID_AXIS_Y; + v3d->overlay.gpencil_grid_opacity = 0.9f; + v3d->bundle_size = 0.2f; v3d->bundle_drawtype = OB_PLAINAXES; @@ -350,6 +357,10 @@ static SpaceLink *view3d_new(const ScrArea *UNUSED(sa), const Scene *scene) v3d->stereo3d_convergence_alpha = 0.15f; v3d->stereo3d_volume_alpha = 0.05f; + /* grease pencil settings */ + v3d->vertex_opacity = 1.0f; + v3d->flag3 |= V3D_GP_SHOW_EDIT_LINES; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for view3d"); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 941f9262694..0157bc567ca 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1593,7 +1593,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple( v3d.flag2 = V3D_RENDER_OVERRIDE; if (draw_flags & V3D_OFSDRAW_USE_GPENCIL) { - v3d.flag2 |= V3D_SHOW_GPENCIL; + v3d.flag2 |= V3D_SHOW_ANNOTATION; } if (draw_flags & V3D_OFSDRAW_USE_SOLID_TEX) { v3d.flag2 |= V3D_SOLID_TEX; diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index 94cd4dfc73d..45e4c4b4676 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -881,7 +881,7 @@ void ED_view3d_draw_depth_gpencil( GPU_depth_test(true); - if (v3d->flag2 & V3D_SHOW_GPENCIL) { + if (v3d->flag2 & V3D_SHOW_ANNOTATION) { ED_gpencil_draw_view3d(NULL, scene, view_layer, depsgraph, v3d, ar, true); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index e94d3a13225..468b33ea9a6 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -52,6 +52,7 @@ #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_font.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" @@ -77,7 +78,6 @@ #include "ED_screen.h" #include "ED_transform.h" #include "ED_mesh.h" -#include "ED_gpencil.h" #include "ED_view3d.h" #include "ED_transform_snap_object_context.h" @@ -2811,7 +2811,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) Depsgraph *depsgraph = CTX_data_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); bGPdata *gpd = CTX_data_gpencil_data(C); - const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); const bool is_face_map = ((is_gp_edit == false) && ar->gizmo_map && WM_gizmomap_is_any_selected(ar->gizmo_map)); Object *ob_eval = OBACT(view_layer_eval); @@ -2850,9 +2850,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) { /* we're only interested in selected points here... */ if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { - if (ED_gpencil_stroke_minmax(gps, true, min, max)) { - ok = true; - } + ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); } } CTX_DATA_END; diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index c716692eb9b..d5ef7cdf441 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -36,13 +36,17 @@ #include "BKE_object.h" #include "BKE_unit.h" +#include "BKE_material.h" +#include "BKE_main.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_gpencil_types.h" #include "DNA_view3d_types.h" #include "BIF_gl.h" +#include "ED_gpencil.h" #include "ED_screen.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" @@ -385,37 +389,28 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) // RulerInfo *ruler_info = gzgroup->customdata; Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + + bGPdata *gpd; bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; if (scene->gpd == NULL) { - scene->gpd = BKE_gpencil_data_addnew(bmain, "GPencil"); + scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); } + gpd = scene->gpd; - gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - /* try to get active palette or create a new one */ - palette = BKE_gpencil_palette_getactive(scene->gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true); - } - /* try to get color with the ruler name or create a new one */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name); - if (palcolor == NULL) { - palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true); - } - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); BKE_gpencil_free_strokes(gpf); @@ -428,6 +423,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) if (ruler_item->flag & RULERITEM_USE_ANGLE) { gps->totpoints = 3; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -438,6 +434,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) else { gps->totpoints = 2; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -447,9 +444,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) } gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; - /* assign color to stroke */ - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); - gps->palcolor = palcolor; + BLI_addtail(&gpf->strokes, gps); changed = true; } diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 690fc5e3bdb..0475159712b 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -26,9 +26,11 @@ /* defines VIEW3D_OT_ruler modal operator */ +#include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "MEM_guardedalloc.h" @@ -40,6 +42,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_unit.h" #include "BIF_gl.h" @@ -51,6 +54,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_gpencil.h" #include "ED_screen.h" #include "ED_view3d.h" #include "ED_transform_snap_object_context.h" @@ -300,37 +304,28 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; + /* FIXME: This needs to be reviewed. Should it keep being done like this? */ if (scene->gpd == NULL) { - scene->gpd = BKE_gpencil_data_addnew(bmain, "GPencil"); + scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); } + bGPdata *gpd = scene->gpd; - gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - /* try to get active palette or create a new one */ - palette = BKE_gpencil_palette_getactive(scene->gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true); - } - /* try to get color with the ruler name or create a new one */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name); - if (palcolor == NULL) { - palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true); - } - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); BKE_gpencil_free_strokes(gpf); @@ -343,6 +338,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) if (ruler_item->flag & RULERITEM_USE_ANGLE) { gps->totpoints = 3; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -353,6 +349,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) else { gps->totpoints = 2; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -362,9 +359,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) } gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; - /* assign color to stroke */ - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); - gps->palcolor = palcolor; + BLI_addtail(&gpf->strokes, gps); changed = true; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 7da69c5b2d5..afff5eb7f66 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -44,6 +44,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_tracking_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" @@ -76,6 +77,7 @@ #include "BKE_editmesh.h" #include "BKE_scene.h" #include "BKE_tracking.h" +#include "BKE_workspace.h" #include "DEG_depsgraph.h" @@ -95,6 +97,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_mball.h" +#include "ED_gpencil.h" #include "UI_interface.h" @@ -1675,6 +1678,27 @@ static bool ed_object_select_pick( if ((oldbasact != basact) && (is_obedit == false)) { ED_object_base_activate(C, basact); /* adds notifier */ } + + /* Set special modes for grease pencil + The grease pencil modes are not real modes, but a hack to make the interface + consistent, so need some tricks to keep UI synchronized */ + // XXX: This stuff neeeds reviewing (Aligorith) +#if 0 + if (((oldbasact) && oldbasact->object->type == OB_GPENCIL) || (basact->object->type == OB_GPENCIL)) { + /* set cursor */ + if (ELEM(basact->object->mode == OB_MODE_GPENCIL_PAINT, + OB_MODE_GPENCIL_SCULPT, + OB_MODE_GPENCIL_WEIGHT)) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + else { + /* TODO: maybe is better use restore */ + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + /* set workspace mode */ + BKE_workspace_object_mode_set(CTX_wm_workspace(C), scene, basact->object->mode); + } +#endif } DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 07ef6b9a819..9ad80f2ab12 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -44,6 +44,7 @@ #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" /* PET modes */ #include "DNA_workspace_types.h" +#include "DNA_gpencil_types.h" #include "BLI_alloca.h" #include "BLI_utildefines.h" @@ -85,6 +86,7 @@ #include "ED_mesh.h" #include "ED_clip.h" #include "ED_node.h" +#include "ED_gpencil.h" #include "WM_types.h" #include "WM_api.h" @@ -101,6 +103,8 @@ #include "transform.h" +#include "DEG_depsgraph.h" + /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */ // #define USE_NUM_NO_ZERO @@ -571,6 +575,10 @@ void removeAspectRatio(TransInfo *t, float vec[2]) static void viewRedrawForce(const bContext *C, TransInfo *t) { if (t->options & CTX_GPENCIL_STROKES) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + } WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } else if (t->spacetype == SPACE_VIEW3D) { @@ -1800,7 +1808,23 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) (float)t->mval[1], }; + +#if 0 /* XXX: Fix from 1c9690e7607bc990cc4a3e6ba839949bb83a78af cannot be used anymore */ + if ((t->flag & T_POINTS) && (t->options & CTX_GPENCIL_STROKES)) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->obedit; + float vecrot[3]; + copy_v3_v3(vecrot, t->center); + mul_m4_v3(ob->obmat, vecrot); + projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO); + } + } + else { + projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); + } +#else projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); +#endif /* Offset the values for the area region. */ const float offset[2] = { @@ -3551,7 +3575,25 @@ static void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, f else sub_v3_v3(vec, td->center); - mul_v3_fl(vec, td->factor); + /* grease pencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff); + + /* scale stroke thickness */ + if (td->val) { + snapGridIncrement(t, t->values); + applyNumInput(&t->num, t->values); + + float ratio = t->values[0]; + *td->val = td->ival * ratio * gps->runtime.multi_frame_falloff; + CLAMP_MIN(*td->val, 0.001f); + } + + } + else { + mul_v3_fl(vec, td->factor); + } if (t->flag & (T_OBJECT | T_POSE)) { mul_m3_v3(td->smtx, vec); @@ -3905,6 +3947,20 @@ static void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData * mul_m3_m3m3(totmat, mat, td->mtx); mul_m3_m3m3(smat, td->smtx, totmat); + /* apply gpencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + float sx = smat[0][0]; + float sy = smat[1][1]; + float sz = smat[2][2]; + + mul_m3_fl(smat, gps->runtime.multi_frame_falloff); + /* fix scale */ + smat[0][0] = sx; + smat[1][1] = sy; + smat[2][2] = sz; + } + sub_v3_v3v3(vec, td->iloc, center); mul_m3_v3(smat, vec); @@ -4578,7 +4634,16 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) } mul_m3_v3(td->smtx, tvec); - mul_v3_fl(tvec, td->factor); + + if (t->options & CTX_GPENCIL_STROKES) { + /* grease pencil multiframe falloff */ + bGPDstroke *gps = (bGPDstroke *)td->extra; + mul_v3_fl(tvec, td->factor * gps->runtime.multi_frame_falloff); + } + else { + /* proportional editing falloff */ + mul_v3_fl(tvec, td->factor); + } protectedTransBits(td->protectflag, tvec); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 675441189b0..d3b7417c4dd 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -90,6 +90,7 @@ #include "BKE_editmesh.h" #include "BKE_tracking.h" #include "BKE_mask.h" +#include "BKE_colortools.h" #include "BIK_api.h" @@ -3609,6 +3610,8 @@ static void posttrans_gpd_clean(bGPdata *gpd) } #endif } + /* set cache flag to dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); } static void posttrans_mask_clean(Mask *mask) @@ -8086,7 +8089,14 @@ void flushTransPaintCurve(TransInfo *t) static void createTransGPencil(bContext *C, TransInfo *t) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0; + + Object *obact = CTX_data_active_object(C); bGPDlayer *gpl; TransData *td = NULL; float mtx[3][3], smtx[3][3]; @@ -8110,50 +8120,67 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (gpd == NULL) return; + /* initialize falloff curve */ + if (is_multiedit) { + curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + /* First Pass: Count the number of datapoints required for the strokes, * (and additional info about the configuration - e.g. 2D/3D?) */ 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; + bGPDframe *gpf; bGPDstroke *gps; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - for (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(gpl, gps) == false) { - continue; - } + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + for (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(obact, gpl, gps) == false) { + continue; + } - if (is_prop_edit) { - /* Proportional Editing... */ - if (is_prop_edit_connected) { - /* connected only - so only if selected */ - if (gps->flag & GP_STROKE_SELECT) - tc->data_len += gps->totpoints; - } - else { - /* everything goes - connection status doesn't matter */ - tc->data_len += gps->totpoints; - } - } - else { - /* only selected stroke points are considered */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; + if (is_prop_edit) { + /* Proportional Editing... */ + if (is_prop_edit_connected) { + /* connected only - so only if selected */ + if (gps->flag & GP_STROKE_SELECT) + tc->data_len += gps->totpoints; + } + else { + /* everything goes - connection status doesn't matter */ + tc->data_len += gps->totpoints; + } + } + else { + /* only selected stroke points are considered */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; - // TODO: 2D vs 3D? - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) - tc->data_len++; + // TODO: 2D vs 3D? + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) + tc->data_len++; + } + } } } } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; + } } } } @@ -8180,153 +8207,166 @@ static void createTransGPencil(bContext *C, TransInfo *t) float diff_mat[4][4]; float inverse_diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - /* undo matrix */ - invert_m4_m4(inverse_diff_mat, diff_mat); + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; } + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (use_multiframe_falloff) { + BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + } + + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); /* 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) { + // XXX: should this be allowed when framelock is enabled? + if ((gpf->framenum != cfra) && (!is_multiedit)) { gpf = BKE_gpencil_frame_addcopy(gpl, cfra); /* in some weird situations (framelock enabled) return NULL */ if (gpf == NULL) { continue; } + if (!is_multiedit) { + init_gpf = gpf; + } } /* Loop over strokes, adding TransData for points as needed... */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - TransData *head = td; - TransData *tail = td; - bool stroke_ok; + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - /* 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(gpl, gps) == false) { - continue; - } - /* What we need to include depends on proportional editing settings... */ - if (is_prop_edit) { - if (is_prop_edit_connected) { - /* A) "Connected" - Only those in selected strokes */ - stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; + /* if multiframe and falloff, recalculate and save value */ + float falloff = 1.0f; /* by default no falloff */ + if ((is_multiedit) && (use_multiframe_falloff)) { + /* Faloff depends on distance to active frame (relative to the overall frame range) */ + falloff = BKE_gpencil_multiframe_falloff_calc(gpf, gpl->actframe->framenum, + f_init, f_end, ts->gp_sculpt.cur_falloff); } - else { - /* B) All points, always */ - stroke_ok = true; - } - } - else { - /* C) Only selected points in selected strokes */ - stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; - } - /* Do stroke... */ - if (stroke_ok && gps->totpoints) { - bGPDspoint *pt; - int i; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + TransData *head = td; + TransData *tail = td; + bool stroke_ok; -#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or is_prop_edit breaks */ - const float ninv = 1.0f / gps->totpoints; - float center[3] = {0.0f}; - - /* compute midpoint of stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - madd_v3_v3v3fl(center, center, &pt->x, ninv); - } -#endif - - /* add all necessary points... */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - bool point_ok; - - /* include point? */ + /* 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(obact, gpl, gps) == false) { + continue; + } + /* What we need to include depends on proportional editing settings... */ if (is_prop_edit) { - /* Always all points in strokes that get included */ - point_ok = true; - } - else { - /* Only selected points in selected strokes */ - point_ok = (pt->flag & GP_SPOINT_SELECT) != 0; - } - - /* do point... */ - if (point_ok) { - copy_v3_v3(td->iloc, &pt->x); - copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local? - - td->loc = &pt->x; - - td->flag = 0; - - if (pt->flag & GP_SPOINT_SELECT) - td->flag |= TD_SELECTED; - - /* for other transform modes (e.g. shrink-fatten), need to additional data */ - if (t->mode == TFM_GPENCIL_SHRINKFATTEN) { - td->val = &pt->pressure; - td->ival = pt->pressure; - } - - /* screenspace needs special matrices... */ - if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) == 0) { - /* screenspace */ - td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - - /* apply parent transformations */ - if (gpl->parent == NULL) { - copy_m3_m4(td->smtx, t->persmat); - copy_m3_m4(td->mtx, t->persinv); - unit_m3(td->axismtx); - } - else { - /* apply matrix transformation relative to parent */ - copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ - copy_m3_m4(td->mtx, diff_mat); /* display position */ - copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ - } + if (is_prop_edit_connected) { + /* A) "Connected" - Only those in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; } else { - /* configure 2D dataspace points so that they don't play up... */ - if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) { - td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - // XXX: matrices may need to be different? - } + /* B) All points, always */ + stroke_ok = true; + } + } + else { + /* C) Only selected points in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; + } - /* apply parent transformations */ - if (gpl->parent == NULL) { - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - unit_m3(td->axismtx); // XXX? + /* Do stroke... */ + if (stroke_ok && gps->totpoints) { + bGPDspoint *pt; + int i; + + /* save falloff factor */ + gps->runtime.multi_frame_falloff = falloff; + + /* add all necessary points... */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + bool point_ok; + + /* include point? */ + if (is_prop_edit) { + /* Always all points in strokes that get included */ + point_ok = true; } else { - /* apply matrix transformation relative to parent */ - copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ - copy_m3_m4(td->mtx, diff_mat); /* display position */ - copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + /* Only selected points in selected strokes */ + point_ok = (pt->flag & GP_SPOINT_SELECT) != 0; + } + + /* do point... */ + if (point_ok) { + copy_v3_v3(td->iloc, &pt->x); + copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local? + + td->loc = &pt->x; + + td->flag = 0; + + if (pt->flag & GP_SPOINT_SELECT) + td->flag |= TD_SELECTED; + + /* for other transform modes (e.g. shrink-fatten), need to additional data + * but never for scale or mirror + */ + if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) { + td->val = &pt->pressure; + td->ival = pt->pressure; + } + + /* screenspace needs special matrices... */ + if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) == 0) { + /* screenspace */ + td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } + else { + /* configure 2D dataspace points so that they don't play up... */ + if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) { + td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + // XXX: matrices may need to be different? + } + + /* apply parent transformations */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } + /* Triangulation must be calculated again, so save the stroke for recalc function */ + td->extra = gps; + + /* save pointer to object */ + td->ob = obact; + + td++; + tail++; } } - /* Triangulation must be calculated again, so save the stroke for recalc function */ - td->extra = gps; - td++; - tail++; + /* March over these points, and calculate the proportional editing distances */ + if (is_prop_edit && (head != tail)) { + /* XXX: for now, we are similar enough that this works... */ + calc_distanceCurveVerts(head, tail - 1); + } } } - - /* March over these points, and calculate the proportional editing distances */ - if (is_prop_edit && (head != tail)) { - /* XXX: for now, we are similar enough that this works... */ - calc_distanceCurveVerts(head, tail - 1); - } + } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; } } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 3618d57b3ed..a2377166dff 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -74,6 +74,7 @@ #include "BKE_armature.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_gpencil.h" #include "BKE_lattice.h" #include "BKE_library.h" #include "BKE_main.h" @@ -105,6 +106,7 @@ #include "ED_curve.h" /* for curve_editnurbs */ #include "ED_clip.h" #include "ED_screen.h" +#include "ED_gpencil.h" #include "WM_types.h" #include "WM_api.h" @@ -379,7 +381,8 @@ static void recalcData_actedit(TransInfo *t) /* flush transform values back to actual coordinates */ flushTransIntFrameActionData(t); } - else { + + if (ac.datatype != ANIMCONT_MASK) { /* get animdata blocks visible in editor, assuming that these will be the ones where things changed */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1311,7 +1314,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } /* GPencil editing context */ - if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { + if (GPENCIL_ANY_MODE(gpd)) { t->options |= CTX_GPENCIL_STROKES; } @@ -1823,6 +1826,21 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) } r_center[2] = 0.0f; } + else if (t->options & CTX_GPENCIL_STROKES) { + /* move cursor in local space */ + TransData *td = NULL; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + float mat[3][3], imat[3][3]; + + td = tc->data; + Object *ob = td->ob; + + sub_v3_v3v3(r_center, r_center, ob->obmat[3]); + copy_m3_m4(mat, ob->obmat); + invert_m3_m3(imat, mat); + mul_m3_v3(imat, r_center); + } + } } void calculateCenterCursor2D(TransInfo *t, float r_center[2]) diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index cbc2b312512..3b5d7d5871a 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -604,6 +604,7 @@ int ED_transform_calc_gizmo_stats( ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); View3D *v3d = sa->spacedata.first; @@ -611,7 +612,7 @@ int ED_transform_calc_gizmo_stats( Base *base; Object *ob = OBACT(view_layer); bGPdata *gpd = CTX_data_gpencil_data(C); - const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); int a, totsel = 0; const int pivot_point = scene->toolsettings->transform_pivot_point; @@ -728,10 +729,8 @@ int ED_transform_calc_gizmo_stats( /* only editable and visible layers are considered */ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, ob, gpd, 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/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index a6e857c4a60..bc35e6e6b89 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -2280,7 +2280,12 @@ static short snapObject( dist_px, r_loc, r_no, r_index); break; - + case OB_GPENCIL: + retval = snapEmpty( + snapdata, ob, obmat, + dist_px, + r_loc, r_no, r_index); + break; case OB_CAMERA: retval = snapCamera( sctx, snapdata, ob, obmat, diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 14592149579..7c1dc148dde 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -51,9 +51,12 @@ #include "BKE_screen.h" #include "BKE_layer.h" #include "BKE_undo_system.h" +#include "BKE_workspace.h" +#include "BKE_paint.h" #include "ED_gpencil.h" #include "ED_render.h" +#include "ED_object.h" #include "ED_screen.h" #include "ED_undo.h" @@ -110,6 +113,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); /* undo during jobs are running can easily lead to freeing data using by jobs, * or they can just lead to freezing job in some other cases */ @@ -122,6 +126,12 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) if (ED_gpencil_session_active()) { return ED_undo_gpencil_step(C, step, undoname); } + if (sa && (sa->spacetype == SPACE_VIEW3D)) { + Object *obact = CTX_data_active_object(C); + if (obact && (obact->type == OB_GPENCIL)) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + } UndoStep *step_data_from_name = NULL; int step_for_callback = step; @@ -156,6 +166,23 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) else { BKE_undosys_step_undo_compat_only(wm->undo_stack, C, step); } + + /* Set special modes for grease pencil */ + if (sa && (sa->spacetype == SPACE_VIEW3D)) { + Object *obact = CTX_data_active_object(C); + if (obact && (obact->type == OB_GPENCIL)) { + /* set cursor */ + if (ELEM(obact->mode, OB_MODE_GPENCIL_PAINT, OB_MODE_GPENCIL_SCULPT, OB_MODE_GPENCIL_WEIGHT)) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + else { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + /* set workspace mode */ + Base *basact = CTX_data_active_base(C); + ED_object_base_activate(C, basact); + } + } } /* App-Handlers (post). */ diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt new file mode 100644 index 00000000000..5ad91d4e01b --- /dev/null +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -0,0 +1,70 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2018, Blender Foundation +# All rights reserved. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + ../blenkernel + ../blenlib + ../blenfont + ../depsgraph + ../makesdna + ../makesrna + ../bmesh + ../render/extern/include + ../../../intern/elbeem/extern + ../../../intern/guardedalloc + ../../../intern/eigen +) + +set(INC_SYS + ${ZLIB_INCLUDE_DIRS} +) + +set(SRC + intern/MOD_gpencil_util.h + + intern/MOD_gpencil_util.c + intern/MOD_gpencilnoise.c + intern/MOD_gpencilsubdiv.c + intern/MOD_gpencilsimplify.c + intern/MOD_gpencilthick.c + intern/MOD_gpenciltint.c + intern/MOD_gpencilcolor.c + intern/MOD_gpencilinstance.c + intern/MOD_gpencilbuild.c + intern/MOD_gpencilopacity.c + intern/MOD_gpencillattice.c + intern/MOD_gpencilmirror.c + intern/MOD_gpencilsmooth.c + intern/MOD_gpencilhook.c + intern/MOD_gpenciloffset.c + + MOD_gpencil_modifiertypes.h +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +add_definitions(${GL_DEFINITIONS}) + +blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h new file mode 100644 index 00000000000..ca941017ff9 --- /dev/null +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -0,0 +1,53 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Ben Batt + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file MOD_modifiertypes.h + * \ingroup modifiers + */ + +#ifndef __MOD_GP_MODIFIERTYPES_H__ +#define __MOD_GP_MODIFIERTYPES_H__ + +#include "BKE_gpencil_modifier.h" + +/* ****************** Type structures for all modifiers ****************** */ + +extern GpencilModifierTypeInfo modifierType_Gpencil_None; +extern GpencilModifierTypeInfo modifierType_Gpencil_Noise; +extern GpencilModifierTypeInfo modifierType_Gpencil_Subdiv; +extern GpencilModifierTypeInfo modifierType_Gpencil_Simplify; +extern GpencilModifierTypeInfo modifierType_Gpencil_Thick; +extern GpencilModifierTypeInfo modifierType_Gpencil_Tint; +extern GpencilModifierTypeInfo modifierType_Gpencil_Color; +extern GpencilModifierTypeInfo modifierType_Gpencil_Instance; +extern GpencilModifierTypeInfo modifierType_Gpencil_Build; +extern GpencilModifierTypeInfo modifierType_Gpencil_Opacity; +extern GpencilModifierTypeInfo modifierType_Gpencil_Lattice; +extern GpencilModifierTypeInfo modifierType_Gpencil_Mirror; +extern GpencilModifierTypeInfo modifierType_Gpencil_Smooth; +extern GpencilModifierTypeInfo modifierType_Gpencil_Hook; +extern GpencilModifierTypeInfo modifierType_Gpencil_Offset; + +/* MOD_gpencil_util.c */ +void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); + +#endif /* __MOD_GP_MODIFIERTYPES_H__ */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c new file mode 100644 index 00000000000..97d28863095 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -0,0 +1,142 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_gpencil_util.c + * \ingroup bke + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_math_color.h" +#include "BLI_rand.h" + +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_lattice.h" +#include "BKE_material.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_colortools.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_util.h" + +void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) +{ +#define INIT_GP_TYPE(typeName) (types[eGpencilModifierType_##typeName] = &modifierType_Gpencil_##typeName) + INIT_GP_TYPE(Noise); + INIT_GP_TYPE(Subdiv); + INIT_GP_TYPE(Simplify); + INIT_GP_TYPE(Thick); + INIT_GP_TYPE(Tint); + INIT_GP_TYPE(Color); + INIT_GP_TYPE(Instance); + INIT_GP_TYPE(Build); + INIT_GP_TYPE(Opacity); + INIT_GP_TYPE(Lattice); + INIT_GP_TYPE(Mirror); + INIT_GP_TYPE(Smooth); + INIT_GP_TYPE(Hook); + INIT_GP_TYPE(Offset); +#undef INIT_GP_TYPE +} + +/* verify if valid layer and pass index */ +bool is_stroke_affected_by_modifier( + Object *ob, char *mlayername, int mpassindex, int minpoints, + bGPDlayer *gpl, bGPDstroke *gps, bool inv1, bool inv2) +{ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + /* omit if filter by layer */ + if (mlayername[0] != '\0') { + if (inv1 == false) { + if (!STREQ(mlayername, gpl->info)) { + return false; + } + } + else { + if (STREQ(mlayername, gpl->info)) { + return false; + } + } + } + /* verify pass */ + if (mpassindex > 0) { + if (inv2 == false) { + if (gp_style->index != mpassindex) { + return false; + } + } + else { + if (gp_style->index == mpassindex) { + return false; + } + } + } + /* need to have a minimum number of points */ + if ((minpoints > 0) && (gps->totpoints < minpoints)) { + return false; + } + + return true; +} + +/* verify if valid vertex group *and return weight */ +float get_modifier_point_weight(MDeformVert *dvert, int inverse, int vindex) +{ + float weight = 1.0f; + + if (vindex >= 0) { + weight = BKE_gpencil_vgroup_use_index(dvert, vindex); + if ((weight >= 0.0f) && (inverse == 1)) { + return -1.0f; + } + + if ((weight < 0.0f) && (inverse == 0)) { + return -1.0f; + } + + /* if inverse, weight is always 1 */ + if ((weight < 0.0f) && (inverse == 1)) { + return 1.0f; + } + + } + + return weight; +} diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h new file mode 100644 index 00000000000..50ac557042d --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -0,0 +1,47 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_gpencil_util.h + * \ingroup modifiers + */ + + +#ifndef __MOD_GPENCIL_UTIL_H__ +#define __MOD_GPENCIL_UTIL_H__ + +struct Object; +struct bGPDlayer; +struct bGPDstroke; +struct MDeformVert; + +bool is_stroke_affected_by_modifier( + struct Object *ob, char *mlayername, int mpassindex, int minpoints, + bGPDlayer *gpl, bGPDstroke *gps, bool inv1, bool inv2); + +float get_modifier_point_weight(struct MDeformVert *dvert, int inverse, int vindex); + +#endif /* __MOD_GPENCIL_UTIL_H__ */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c new file mode 100644 index 00000000000..6b959659a60 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -0,0 +1,558 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilbuild.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + BuildGpencilModifierData *gpmd = (BuildGpencilModifierData *)md; + + /* We deliberately set this range to the half the default + * frame-range to have an immediate effect ot suggest use-cases + */ + gpmd->start_frame = 1; + gpmd->end_frame = 125; + + /* Init default length of each build effect - Nothing special */ + gpmd->start_delay = 0.0f; + gpmd->length = 100.0f; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static bool dependsOnTime(GpencilModifierData *UNUSED(md)) +{ + return true; +} + +/* ******************************************** */ +/* Build Modifier - Stroke generation logic + * + * There are two modes for how the strokes are sequenced (at a macro-level): + * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a time. + * - Concurrent Mode - Multiple strokes appear/disappear at once. + * + * Assumptions: + * - Stroke points are generally equally spaced. This implies that we can just add/remove points, + * without worrying about distances between them / adding extra interpolated points between + * an visible point and one about to be added/removed (or any similar tapering effects). + + * - All strokes present are fully visible (i.e. we don't have to ignore any) + */ + +/* Remove a particular stroke */ +static void clear_stroke(bGPDframe *gpf, bGPDstroke *gps) +{ + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); +} + +/* Clear all strokes in frame */ +static void gpf_clear_all_strokes(bGPDframe *gpf) +{ + bGPDstroke *gps, *gps_next; + for (gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + clear_stroke(gpf, gps); + } + BLI_listbase_clear(&gpf->strokes); +} + +/* Reduce the number of points in the stroke + * + * Note: This won't be called if all points are present/removed + * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions) + */ +static void reduce_stroke_points(bGPDstroke *gps, const int num_points, const eBuildGpencil_Transition transition) +{ + bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, __func__); + MDeformVert *new_dvert = MEM_callocN(sizeof(MDeformVert) * num_points, __func__); + + /* Which end should points be removed from */ + // TODO: free stroke weights + switch (transition) { + case GP_BUILD_TRANSITION_GROW: /* Show in forward order = Remove ungrown-points from end of stroke */ + case GP_BUILD_TRANSITION_SHRINK: /* Hide in reverse order = Remove dead-points from end of stroke */ + { + /* copy over point data */ + memcpy(new_points, gps->points, sizeof(bGPDspoint) * num_points); + memcpy(new_dvert, gps->dvert, sizeof(MDeformVert) * num_points); + + /* free unused point weights */ + for (int i = num_points; i < gps->totpoints; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } + + break; + } + + /* Hide in forward order = Remove points from start of stroke */ + case GP_BUILD_TRANSITION_FADE: + { + /* num_points is the number of points left after reducing. + * We need to know how many to remove + */ + const int offset = gps->totpoints - num_points; + + /* copy over point data */ + memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * num_points); + memcpy(new_dvert, gps->dvert + offset, sizeof(MDeformVert) * num_points); + + /* free unused weights */ + for (int i = 0; i < offset; i++) { + MDeformVert *dvert = &gps->dvert[i]; + BKE_gpencil_free_point_weights(dvert); + } + + break; + } + + default: + printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__); + break; + } + + /* replace stroke geometry */ + MEM_SAFE_FREE(gps->points); + MEM_SAFE_FREE(gps->dvert); + gps->points = new_points; + gps->dvert = new_dvert; + gps->totpoints = num_points; + + /* mark stroke as needing to have its geometry caches rebuilt */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; + MEM_SAFE_FREE(gps->triangles); +} + +/* --------------------------------------------- */ + +/* Stroke Data Table Entry - This represents one stroke being generated */ +typedef struct tStrokeBuildDetails { + bGPDstroke *gps; + + /* Indices - first/last indices for the stroke's points (overall) */ + size_t start_idx, end_idx; + + /* Number of points - Cache for more convenient access */ + int totpoints; +} tStrokeBuildDetails; + + +/* Sequential - Show strokes one after the other */ +static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac) +{ + const size_t tot_strokes = BLI_listbase_count(&gpf->strokes); + bGPDstroke *gps; + size_t i; + + /* 1) Compute proportion of time each stroke should occupy */ + /* NOTE: This assumes that the total number of points won't overflow! */ + tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__); + size_t totpoints = 0; + + /* 1.1) First pass - Tally up points */ + for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) { + tStrokeBuildDetails *cell = &table[i]; + + cell->gps = gps; + cell->totpoints = gps->totpoints; + + totpoints += cell->totpoints; + } + + /* 1.2) Second pass - Compute the overall indices for points */ + for (i = 0; i < tot_strokes; i++) { + tStrokeBuildDetails *cell = &table[i]; + + if (i == 0) { + cell->start_idx = 0; + } + else { + cell->start_idx = (cell - 1)->end_idx; + } + cell->end_idx = cell->start_idx + cell->totpoints - 1; + } + + + /* 2) Determine the global indices for points that should be visible */ + size_t first_visible = 0; + size_t last_visible = 0; + + switch (mmd->transition) { + /* Show in forward order + * - As fac increases, the number of visible points increases + */ + case GP_BUILD_TRANSITION_GROW: + first_visible = 0; /* always visible */ + last_visible = (size_t)roundf(totpoints * fac); + break; + + /* Hide in reverse order + * - As fac increases, the number of points visible at the end decreases + */ + case GP_BUILD_TRANSITION_SHRINK: + first_visible = 0; /* always visible (until last point removed) */ + last_visible = (size_t)(totpoints * (1.0f - fac)); + break; + + /* Hide in forward order + * - As fac increases, the early points start getting hidden + */ + case GP_BUILD_TRANSITION_FADE: + first_visible = (size_t)(totpoints * fac); + last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */ + break; + } + + + /* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */ + for (i = 0; i < tot_strokes; i++) { + tStrokeBuildDetails *cell = &table[i]; + + /* Determine what portion of the stroke is visible */ + if ((cell->end_idx < first_visible) || (cell->start_idx > last_visible)) { + /* Not visible at all - Either ended before */ + clear_stroke(gpf, cell->gps); + } + else { + /* Some proportion of stroke is visible */ + /* XXX: Will the transition settings still be valid now? */ + if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) { + /* Do nothing - whole stroke is visible */ + } + else if (first_visible > cell->start_idx) { + /* Starts partway through this stroke */ + int num_points = cell->end_idx - first_visible; + reduce_stroke_points(cell->gps, num_points, mmd->transition); + } + else { + /* Ends partway through this stroke */ + int num_points = last_visible - cell->start_idx; + reduce_stroke_points(cell->gps, num_points, mmd->transition); + } + } + } + + /* Free table */ + MEM_freeN(table); +} + +/* --------------------------------------------- */ + +/* Concurrent - Show multiple strokes at once */ +// TODO: Allow random offsets to start times +// TODO: Allow varying speeds? Scaling of progress? +static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac) +{ + bGPDstroke *gps, *gps_next; + int max_points = 0; + + const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW); + + /* 1) Determine the longest stroke, to figure out when short strokes should start */ + /* FIXME: A *really* long stroke here could dwarf everything else, causing bad timings */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->totpoints > max_points) { + max_points = gps->totpoints; + } + } + if (max_points == 0) { + printf("ERROR: Strokes are all empty (GP Build Modifier: %s)\n", __func__); + return; + } + + /* 2) For each stroke, determine how it should be handled */ + for (gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + + /* Relative Length of Stroke - Relative to the longest stroke, + * what proportion of the available time should this stroke use + */ + const float relative_len = (float)gps->totpoints / (float)max_points; + + /* Determine how many points should be left in the stroke */ + int num_points = 0; + + switch (mmd->time_alignment) { + case GP_BUILD_TIMEALIGN_START: /* all start on frame 1 */ + { + /* Build effect occurs over when fac = 0, to fac = relative_len */ + if (fac <= relative_len) { + /* Scale fac to fit relative_len */ + /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ + const float scaled_fac = fac / relative_len; + + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); + } + else { + num_points = (int)roundf(scaled_fac * gps->totpoints); + } + } + else { + /* Build effect has ended */ + if (reverse) { + num_points = 0; + } + else { + num_points = gps->totpoints; + } + } + + break; + } + case GP_BUILD_TIMEALIGN_END: /* all end on same frame */ + { + /* Build effect occurs over 1.0 - relative_len, to 1.0 (i.e. over the end of the range) */ + const float start_fac = 1.0f - relative_len; + + if (fac >= start_fac) { + /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ + const float scaled_fac = (fac - start_fac) / relative_len; + + if (reverse) { + num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); + } + else { + num_points = (int)roundf(scaled_fac * gps->totpoints); + } + } + else { + /* Build effect hasn't started */ + if (reverse) { + num_points = gps->totpoints; + } + else { + num_points = 0; + } + } + + break; + } + + /* TODO... */ + } + + /* Modify the stroke geometry */ + if (num_points <= 0) { + /* Nothing Left - Delete the stroke */ + clear_stroke(gpf, gps); + } + else if (num_points < gps->totpoints) { + /* Remove some points */ + reduce_stroke_points(gps, num_points, mmd->transition); + } + } +} + +/* --------------------------------------------- */ + +/* Entry-point for Build Modifier */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *UNUSED(ob), 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) { + return; + } + + /* Omit layer if filter by layer */ + if (mmd->layername[0] != '\0') { + if ((mmd->flag & GP_BUILD_INVERT_LAYER) == 0) { + if (!STREQ(mmd->layername, gpl->info)) { + return; + } + } + else { + if (STREQ(mmd->layername, gpl->info)) { + return; + } + } + } + + /* Early exit if outside of the frame range for this modifier + * (e.g. to have one forward, and one backwards modifier) + */ + if (mmd->flag & GP_BUILD_RESTRICT_TIME) { + if ((ctime < mmd->start_frame) || (ctime > mmd->end_frame)) { + return; + } + } + + /* Compute start and end frames for the animation effect + * 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; + + if (gpf->next) { + /* Use the next frame or upper bound as end frame, whichever is lower/closer */ + end_frame = MIN2(end_frame, gpf->next->framenum); + } + + + /* Early exit if current frame is outside start/end bounds */ + /* NOTE: If we're beyond the next/prev frames (if existent), then we wouldn't have this problem anyway... */ + if (ctime < start_frame) { + /* Before Start - Animation hasn't started. Display initial state. */ + if (reverse) { + /* 1) Reverse = Start with all, end with nothing. + * ==> Do nothing (everything already present) + */ + } + else { + /* 2) Forward Order = Start with nothing, end with the full frame. + * ==> Free all strokes, and return an empty frame + */ + gpf_clear_all_strokes(gpf); + } + + /* Early exit */ + return; + } + else if (ctime >= end_frame) { + /* Past End - Animation finished. Display final result. */ + if (reverse) { + /* 1) Reverse = Start with all, end with nothing. + * ==> Free all strokes, and return an empty frame + */ + gpf_clear_all_strokes(gpf); + } + else { + /* 2) Forward Order = Start with nothing, end with the full frame. + * ==> Do Nothing (everything already present) + */ + } + + /* Early exit */ + return; + } + + + /* 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) { + case GP_BUILD_MODE_SEQUENTIAL: + build_sequential(mmd, gpf, fac); + break; + + case GP_BUILD_MODE_CONCURRENT: + build_concurrent(mmd, gpf, fac); + break; + + default: + printf("Unsupported build mode (%d) for GP Build Modifier: '%s'\n", mmd->mode, mmd->modifier.name); + break; + } +} + +/* ******************************************** */ + +/* FIXME: Baking the Build Modifier is currently unsupported. + * Adding support for this is more complicated than for other + * modifiers, as to implement this, we'd have to add more frames, + * which would in turn break how the modifier functions. + */ +#if 0 +static void bakeModifier( + Main *bmain, const Depsgraph *UNUSED(depsgraph), + 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) { + + } + } +} +#endif + +/* ******************************************** */ + +GpencilModifierTypeInfo modifierType_Gpencil_Build = { + /* name */ "Build", + /* structName */ "BuildGpencilModifierData", + /* structSize */ sizeof(BuildGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ NULL, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ dependsOnTime, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c new file mode 100644 index 00000000000..af00b24715f --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c @@ -0,0 +1,178 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilcolor.c + * \ingroup modifiers + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math_color.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "BKE_global.h" +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + ColorGpencilModifierData *gpmd = (ColorGpencilModifierData *)md; + gpmd->pass_index = 0; + ARRAY_SET_ITEMS(gpmd->hsv, 1.0f, 1.0f, 1.0f); + gpmd->layername[0] = '\0'; + gpmd->flag |= GP_COLOR_CREATE_COLORS; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* color correction strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + + ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md; + float hsv[3], factor[3]; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_COLOR_INVERT_LAYER, mmd->flag & GP_COLOR_INVERT_PASS)) + { + return; + } + + copy_v3_v3(factor, mmd->hsv); + add_v3_fl(factor, -1.0f); + + 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); + + 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); +} + +static void bakeModifier( + Main *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 = give_current_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); + + /* look for color */ + if (mmd->flag & GP_TINT_CREATE_COLORS) { + 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); + assign_material(bmain, ob, newmat, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + 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); + } + /* reasign color index */ + int idx = BKE_object_material_slot_find_index(ob, newmat); + gps->mat_nr = idx - 1; + } + else { + /* reuse existing color */ + copy_v4_v4(gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + } + + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + /* free hash buffers */ + if (gh_color) { + BLI_ghash_free(gh_color, NULL, NULL); + gh_color = NULL; + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Color = { + /* name */ "Hue/Saturation", + /* structName */ "ColorGpencilModifierData", + /* structSize */ sizeof(ColorGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c new file mode 100644 index 00000000000..e036b6b78be --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c @@ -0,0 +1,355 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilhook.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_math.h" + +#include "BLI_utildefines.h" + +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_colortools.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.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" + +/* temp struct to hold data */ +struct GPHookData_cb { + struct CurveMapping *curfalloff; + + char falloff_type; + float falloff; + float falloff_sq; + float fac_orig; + + unsigned int use_falloff : 1; + unsigned int use_uniform : 1; + + float cent[3]; + + float mat_uniform[3][3]; + float mat[4][4]; +}; + +static void initData(GpencilModifierData *md) +{ + HookGpencilModifierData *gpmd = (HookGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->object = NULL; + gpmd->force = 0.5f; + gpmd->falloff_type = eGPHook_Falloff_Smooth; + gpmd->curfalloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curfalloff) { + curvemapping_initialize(gpmd->curfalloff); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + HookGpencilModifierData *gmd = (HookGpencilModifierData *)md; + HookGpencilModifierData *tgmd = (HookGpencilModifierData *)target; + + if (tgmd->curfalloff != NULL) { + curvemapping_free(tgmd->curfalloff); + tgmd->curfalloff = NULL; + } + + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curfalloff = curvemapping_copy(gmd->curfalloff); +} + +/* calculate factor of fallof */ +static float gp_hook_falloff(const struct GPHookData_cb *tData, const float len_sq) +{ + BLI_assert(tData->falloff_sq); + if (len_sq > tData->falloff_sq) { + return 0.0f; + } + else if (len_sq > 0.0f) { + float fac; + + if (tData->falloff_type == eGPHook_Falloff_Const) { + fac = 1.0f; + goto finally; + } + else if (tData->falloff_type == eGPHook_Falloff_InvSquare) { + /* avoid sqrt below */ + fac = 1.0f - (len_sq / tData->falloff_sq); + goto finally; + } + + fac = 1.0f - (sqrtf(len_sq) / tData->falloff); + + switch (tData->falloff_type) { + case eGPHook_Falloff_Curve: + fac = curvemapping_evaluateF(tData->curfalloff, 0, fac); + break; + case eGPHook_Falloff_Sharp: + fac = fac * fac; + break; + case eGPHook_Falloff_Smooth: + fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; + break; + case eGPHook_Falloff_Root: + fac = sqrtf(fac); + break; + case eGPHook_Falloff_Linear: + /* pass */ + break; + case eGPHook_Falloff_Sphere: + fac = sqrtf(2 * fac - fac * fac); + break; + default: + fac = fac; + break; + } + + finally: + return fac * tData->fac_orig; + } + else { + return tData->fac_orig; + } +} + +/* apply point deformation */ +static void gp_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoint *pt) +{ + float fac; + + if (tData->use_falloff) { + float len_sq; + + if (tData->use_uniform) { + float co_uniform[3]; + mul_v3_m3v3(co_uniform, tData->mat_uniform, &pt->x); + len_sq = len_squared_v3v3(tData->cent, co_uniform); + } + else { + len_sq = len_squared_v3v3(tData->cent, &pt->x); + } + + fac = gp_hook_falloff(tData, len_sq); + } + else { + fac = tData->fac_orig; + } + + if (fac) { + float co_tmp[3]; + mul_v3_m4v3(co_tmp, tData->mat, &pt->x); + interp_v3_v3v3(&pt->x, &pt->x, co_tmp, fac * weight); + } +} + +/* deform stroke */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + if (!mmd->object) { + return; + } + + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + bPoseChannel *pchan = BKE_pose_channel_find_name(mmd->object->pose, mmd->subtarget); + float dmat[4][4]; + struct GPHookData_cb tData; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_HOOK_INVERT_LAYER, mmd->flag & GP_HOOK_INVERT_PASS)) + { + return; + } + + /* init struct */ + tData.curfalloff = mmd->curfalloff; + tData.falloff_type = mmd->falloff_type; + tData.falloff = (mmd->falloff_type == eHook_Falloff_None) ? 0.0f : mmd->falloff; + tData.falloff_sq = SQUARE(tData.falloff); + tData.fac_orig = mmd->force; + tData.use_falloff = (tData.falloff_sq != 0.0f); + tData.use_uniform = (mmd->flag & GP_HOOK_UNIFORM_SPACE) != 0; + + if (tData.use_uniform) { + copy_m3_m4(tData.mat_uniform, mmd->parentinv); + mul_v3_m3v3(tData.cent, tData.mat_uniform, mmd->cent); + } + else { + unit_m3(tData.mat_uniform); + copy_v3_v3(tData.cent, mmd->cent); + } + + /* get world-space matrix of target, corrected for the space the verts are in */ + if (mmd->subtarget[0] && pchan) { + /* bone target if there's a matching pose-channel */ + mul_m4_m4m4(dmat, mmd->object->obmat, pchan->pose_mat); + } + else { + /* just object target */ + copy_m4_m4(dmat, mmd->object->obmat); + } + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_series(tData.mat, ob->imat, dmat, mmd->parentinv); + + /* loop points and apply deform */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_HOOK_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + gp_hook_co_apply(&tData, weight, pt); + } +} + +/* 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) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* apply hook effects on this frame + * NOTE: this assumes that we don't want hook animation on non-keyframed frames + */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* compute hook effects on this frame */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static void freeData(GpencilModifierData *md) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + if (mmd->curfalloff) { + curvemapping_free(mmd->curfalloff); + } +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + return !mmd->object; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + HookGpencilModifierData *lmd = (HookGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Hook Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + HookGpencilModifierData *mmd = (HookGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Hook = { + /* name */ "Hook", + /* structName */ "HookGpencilModifierData", + /* structSize */ sizeof(HookGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c new file mode 100644 index 00000000000..64f3fbc4a95 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilinstance.c @@ -0,0 +1,360 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilinstance.c + * \ingroup modifiers + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_blenlib.h" +#include "BLI_rand.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_main.h" +#include "BKE_scene.h" +#include "BKE_layer.h" +#include "BKE_collection.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + InstanceGpencilModifierData *gpmd = (InstanceGpencilModifierData *)md; + gpmd->count[0] = 1; + gpmd->count[1] = 1; + gpmd->count[2] = 1; + gpmd->offset[0] = 1.0f; + gpmd->offset[1] = 1.0f; + gpmd->offset[2] = 1.0f; + gpmd->shift[0] = 0.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; + gpmd->lock_axis |= GP_LOCKAXIS_X; + gpmd->flag |= GP_INSTANCE_MAKE_OBJECTS; + + /* fill random values */ + BLI_array_frand(gpmd->rnd, 20, 1); + gpmd->rnd[0] = 1; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* -------------------------------- */ + +/* array modifier - generate geometry callback (for viewport/rendering) */ +/* TODO: How to skip this for the simplify options? --> !GP_SIMPLIFY_MODIF(ts, playing) */ +static void generate_geometry( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)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; + + bool *valid_strokes = MEM_callocN(sizeof(bool) * num_strokes, __func__); + + 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->pass_index, 1, gpl, gps, + mmd->flag & GP_INSTANCE_INVERT_LAYER, mmd->flag & GP_INSTANCE_INVERT_PASS)) + { + valid_strokes[idx] = true; + num_valid++; + } + } + + /* 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"); + } + + MEM_SAFE_FREE(valid_strokes); + return; + } + + + /* Generate new instances of all existing strokes, + * keeping each instance together so they maintain + * the correct ordering relative to each other + */ + for (int x = 0; x < mmd->count[0]; x++) { + for (int y = 0; y < mmd->count[1]; y++) { + for (int z = 0; z < mmd->count[2]; z++) { + /* original strokes are at index = 0,0,0 */ + if ((x == 0) && (y == 0) && (z == 0)) { + continue; + } + + /* Compute transforms for this instance */ + const int elem_idx[3] = {x, y, z}; + float mat[4][4]; + + BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat); + + /* apply shift */ + int sh = x; + if (mmd->lock_axis == GP_LOCKAXIS_Y) { + sh = y; + } + if (mmd->lock_axis == GP_LOCKAXIS_Z) { + sh = z; + } + madd_v3_v3fl(mat[3], mmd->shift, sh); + + /* 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]) { + /* Duplicate stroke */ + bGPDstroke *gps_dst = MEM_dupallocN(gps); + gps_dst->points = MEM_dupallocN(gps->points); + gps_dst->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gps_dst); + + gps_dst->triangles = MEM_dupallocN(gps->triangles); + + /* Move points */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps_dst->points[i]; + mul_m4_v3(mat, &pt->x); + } + + /* Add new stroke to cache, to be added to the frame once + * all duplicates have been made + */ + BLI_addtail(&stroke_cache, gps_dst); + } + } + } + } + } + + /* merge newly created stroke instances back into the main stroke list */ + BLI_movelisttolist(&gpf->strokes, &stroke_cache); + + /* free temp data */ + MEM_SAFE_FREE(valid_strokes); +} + +/* bakeModifier - "Bake to Data" Mode */ +static void bakeModifierGP_strokes( + Depsgraph *depsgraph, + 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); + } + } +} + +/* -------------------------------- */ + +/* helper to create a new object */ +static Object *array_instance_add_ob_copy(Main *bmain, Scene *scene, Object *from_ob) +{ + Object *ob; + + ob = BKE_object_copy(bmain, from_ob); + BKE_collection_object_add_from(bmain, scene, from_ob, ob); + + zero_v3(ob->loc); + zero_v3(ob->rot); + + DEG_id_type_tag(bmain, ID_OB); + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, 0); + + return ob; +} + +/* bakeModifier - "Make Objects" Mode */ +static void bakeModifierGP_objects(Main *bmain, Depsgraph *depsgraph, GpencilModifierData *md, Object *ob) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + /* reset random */ + mmd->rnd[0] = 1; + + /* generate instances as objects */ + for (int x = 0; x < mmd->count[0]; x++) { + for (int y = 0; y < mmd->count[1]; y++) { + for (int z = 0; z < mmd->count[2]; z++) { + Object *newob; + GpencilModifierData *fmd; + + const int elem_idx[3] = {x, y, z}; + float mat[4][4], finalmat[4][4]; + int sh; + + /* original strokes are at index = 0,0,0 */ + if ((x == 0) && (y == 0) && (z == 0)) { + continue; + } + + /* compute transform for instance */ + BKE_gpencil_instance_modifier_instance_tfm(mmd, elem_idx, mat); + mul_m4_m4m4(finalmat, ob->obmat, mat); + + /* moves to new origin */ + sh = x; + if (mmd->lock_axis == GP_LOCKAXIS_Y) { + sh = y; + } + if (mmd->lock_axis == GP_LOCKAXIS_Z) { + sh = z; + } + madd_v3_v3fl(finalmat[3], mmd->shift, sh); + + /* Create a new object + * + * NOTE: Copies share the same original GP datablock + * Artists can later user make_single_user on these + * to make them unique (if necessary), without too + * much extra memory usage. + */ + newob = array_instance_add_ob_copy(bmain, scene, ob); + + /* remove array on destination object */ + fmd = (GpencilModifierData *)BLI_findstring(&newob->greasepencil_modifiers, md->name, offsetof(GpencilModifierData, name)); + if (fmd) { + BLI_remlink(&newob->greasepencil_modifiers, fmd); + BKE_gpencil_modifier_free(fmd); + } + + /* copy transforms to destination object */ + copy_m4_m4(newob->obmat, finalmat); + + copy_v3_v3(newob->loc, finalmat[3]); + mat4_to_eul(newob->rot, finalmat); + mat4_to_size(newob->size, finalmat); + } + } + } +} + +/* -------------------------------- */ + +/* Generic "generateStrokes" callback */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + + /* When the "make_objects" flag is set, this modifier is handled as part of the + * draw engine instead. The main benefit is that the instances won't suffer from + * z-ordering problems. + * + * FIXME: Ultimately, the draw-engine hack here shouldn't be necessary, but until + * we find a better fix to the z-ordering problems, it's better to have + * working functionality + */ + if ((mmd->flag & GP_INSTANCE_MAKE_OBJECTS) == 0) { + generate_geometry(md, depsgraph, ob, gpl, gpf); + } +} + +/* Generic "bakeModifier" callback */ +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + InstanceGpencilModifierData *mmd = (InstanceGpencilModifierData *)md; + + /* Create new objects or add all to current datablock. + * Sometimes it's useful to have the option to do either of these... + */ + if (mmd->flag & GP_INSTANCE_MAKE_OBJECTS) { + bakeModifierGP_objects(bmain, depsgraph, md, ob); + } + else { + bakeModifierGP_strokes(depsgraph, md, ob); + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Instance = { + /* name */ "Instance", + /* structName */ "InstanceGpencilModifierData", + /* structSize */ sizeof(InstanceGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c new file mode 100644 index 00000000000..944e787020e --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c @@ -0,0 +1,213 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencillattice.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_lattice.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.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) +{ + LatticeGpencilModifierData *gpmd = (LatticeGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->object = NULL; + gpmd->cache_data = NULL; + gpmd->strength = 1.0f; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_LATTICE_INVERT_LAYER, mmd->flag & GP_LATTICE_INVERT_PASS)) + { + return; + } + + if (mmd->cache_data == NULL) { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_LATTICE_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + calc_latt_deform((struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight); + } +} + +/* 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) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + struct LatticeDeformData *ldata = NULL; + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* apply lattice effects on this frame + * NOTE: this assumes that we don't want lattice animation on non-keyframed frames + */ + CFRA = gpf->framenum; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); + + /* recalculate lattice data */ + BKE_gpencil_lattice_init(ob); + + /* compute lattice effects on this frame */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + + /* free lingering data */ + ldata = (struct LatticeDeformData *)mmd->cache_data; + if (ldata) { + end_latt_deform(ldata); + mmd->cache_data = NULL; + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static void freeData(GpencilModifierData *md) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + struct LatticeDeformData *ldata = (struct LatticeDeformData *)mmd->cache_data; + /* free deform data */ + if (ldata) { + end_latt_deform(ldata); + } +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + + return !mmd->object; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Lattice Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Lattice = { + /* name */ "Lattice", + /* structName */ "LatticeGpencilModifierData", + /* structSize */ sizeof(LatticeGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_Single | eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c new file mode 100644 index 00000000000..9b5186755d6 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -0,0 +1,239 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilmirror.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_modifier.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" +#include "BKE_main.h" +#include "BKE_layer.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) +{ + MirrorGpencilModifierData *gpmd = (MirrorGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->object = NULL; + gpmd->flag |= GP_MIRROR_AXIS_X; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void clip_stroke(MirrorGpencilModifierData *mmd, bGPDstroke *gps) +{ + int i; + bGPDspoint *pt; + float fpt[3]; + if ((mmd->flag & GP_MIRROR_CLIPPING) == 0) { + return; + } + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + copy_v3_v3(fpt, &pt->x); + for (int xi = 0; xi < 3; ++xi) { + if (mmd->flag & (GP_MIRROR_AXIS_X << xi)) { + if (fpt[xi] >= 0.0f) { + fpt[xi] = 0.0f; + } + } + } + copy_v3_v3(&pt->x, fpt); + } + +} + +static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstroke *gps, int axis) +{ + int i; + bGPDspoint *pt; + float factor[3] = { 1.0f, 1.0f, 1.0f }; + factor[axis] = -1.0f; + + float clear[3] = { 0.0f, 0.0f, 0.0f }; + clear[axis] = 1.0f; + + float origin[3]; + float mirror_origin[3]; + + copy_v3_v3(origin, ob->loc); + /* only works with current axis */ + mul_v3_v3(origin, clear); + zero_v3(mirror_origin); + + if (mmd->object) { + copy_v3_v3(mirror_origin, mmd->object->loc); + mul_v3_v3(mirror_origin, clear); + sub_v3_v3(origin, mirror_origin); + } + /* clear other axis */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(&pt->x, origin); + mul_v3_v3(&pt->x, factor); + add_v3_v3(&pt->x, mirror_origin); + } + +} + +/* Generic "generateStrokes" callback */ +static void generateStrokes( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + bGPDstroke *gps, *gps_new = NULL; + int tot_strokes; + int i; + + /* count strokes to avoid infinite loop after adding new strokes to tail of listbase */ + tot_strokes = BLI_listbase_count(&gpf->strokes); + + for (i = 0, gps = gpf->strokes.first; i < tot_strokes; i++, gps = gps->next) { + if (is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_MIRROR_INVERT_LAYER, mmd->flag & GP_MIRROR_INVERT_PASS)) + { + /* check each axis for mirroring */ + for (int xi = 0; xi < 3; ++xi) { + if (mmd->flag & (GP_MIRROR_AXIS_X << xi)) { + /* clip before duplicate */ + clip_stroke(mmd, gps); + + gps_new = BKE_gpencil_stroke_duplicate(gps); + update_position(ob, mmd, gps_new, xi); + BLI_addtail(&gpf->strokes, gps_new); + } + } + } + } +} + +static void bakeModifier( + Main *bmain, Depsgraph *depsgraph, + GpencilModifierData *md, Object *ob) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + Scene *scene = DEG_get_evaluated_scene(depsgraph); + bGPdata *gpd = ob->data; + int oldframe = (int)DEG_get_ctime(depsgraph); + + if (mmd->object == NULL) + return; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* 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); + } + } + + /* return frame state and DB to original state */ + CFRA = oldframe; + BKE_scene_graph_update_for_newframe(depsgraph, bmain); +} + +static bool isDisabled(GpencilModifierData *UNUSED(md), int UNUSED(userRenderParams)) +{ + //MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + + return false; +} + +static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + MirrorGpencilModifierData *lmd = (MirrorGpencilModifierData *)md; + if (lmd->object != NULL) { + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Mirror Modifier"); + DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); +} + +static void foreachObjectLink( + GpencilModifierData *md, Object *ob, + ObjectWalkFunc walk, void *userData) +{ + MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Mirror = { + /* name */ "Mirror", + /* structName */ "MirrorGpencilModifierData", + /* structSize */ sizeof(MirrorGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c new file mode 100644 index 00000000000..37c8bf0b0f0 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -0,0 +1,285 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilnoise.c + * \ingroup modifiers + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" +#include "BLI_rand.h" + +#include "PIL_time.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_context.h" +#include "BKE_global.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + NoiseGpencilModifierData *gpmd = (NoiseGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->flag |= GP_NOISE_MOD_LOCATION; + gpmd->flag |= GP_NOISE_FULL_STROKE; + gpmd->flag |= GP_NOISE_USE_RANDOM; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->step = 1; + gpmd->scene_frame = -999999; + gpmd->gp_frame = -999999; + + gpmd->vrand1 = 1.0; + gpmd->vrand2 = 1.0; +} + +static void freeData(GpencilModifierData *md) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + + if (mmd->rng != NULL) { + BLI_rng_free(mmd->rng); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static bool dependsOnTime(GpencilModifierData *md) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + return (mmd->flag & GP_NOISE_USE_RANDOM) != 0; +} + +/* aply noise effect based on stroke direction */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *depsgraph, + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md; + bGPDspoint *pt0, *pt1; + MDeformVert *dvert; + float shift, vran, vdir; + float normal[3]; + float vec1[3], vec2[3]; +#if 0 + Scene *scene = DEG_get_evaluated_scene(depsgraph); +#endif + int sc_frame = 0; + int sc_diff = 0; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + + /* Random generator, only init once. */ + if (mmd->rng == NULL) { + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= GET_UINT_FROM_POINTER(mmd); + mmd->rng = BLI_rng_new(rng_seed); + } + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_NOISE_INVERT_LAYER, mmd->flag & GP_NOISE_INVERT_PASS)) + { + return; + } + + sc_frame = (int)DEG_get_ctime(depsgraph); + + zero_v3(vec2); + + /* calculate stroke normal*/ + BKE_gpencil_stroke_normal(gps, normal); + + /* 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; + } + + /* last point is special */ + if (i == gps->totpoints) { + dvert = &gps->dvert[i - 2]; + pt0 = &gps->points[i - 2]; + pt1 = &gps->points[i - 1]; + } + else { + dvert = &gps->dvert[i - 1]; + pt0 = &gps->points[i - 1]; + pt1 = &gps->points[i]; + + } + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_NOISE_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + /* initial vector (p0 -> p1) */ + sub_v3_v3v3(vec1, &pt1->x, &pt0->x); + 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) { + sc_diff = abs(mmd->scene_frame - sc_frame); + /* only recalc if the gp frame change or the number of scene frames is bigger than step */ + if ((!gpl->actframe) || (mmd->gp_frame != gpl->actframe->framenum) || + (sc_diff >= mmd->step)) + { + vran = mmd->vrand1 = BLI_rng_get_float(mmd->rng); + vdir = mmd->vrand2 = BLI_rng_get_float(mmd->rng); + mmd->gp_frame = gpl->actframe->framenum; + mmd->scene_frame = sc_frame; + } + else { + vran = mmd->vrand1; + if (mmd->flag & GP_NOISE_FULL_STROKE) { + vdir = mmd->vrand2; + } + else { + int f = (mmd->vrand2 * 10.0f) + i; + vdir = f % 2; + } + } + } + else { + vran = 1.0f; + if (mmd->flag & GP_NOISE_FULL_STROKE) { + vdir = gps->totpoints % 2; + } + else { + vdir = i % 2; + } + mmd->gp_frame = -999999; + } + + /* 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); + } + + /* apply randomness to thickness */ + if (mmd->flag & GP_NOISE_MOD_THICKNESS) { + if (vdir > 0.5f) { + pt1->pressure -= pt1->pressure * vran * mmd->factor; + } + else { + pt1->pressure += pt1->pressure * vran * mmd->factor; + } + CLAMP_MIN(pt1->pressure, GPENCIL_STRENGTH_MIN); + } + + /* apply randomness to color strength */ + if (mmd->flag & GP_NOISE_MOD_STRENGTH) { + if (vdir > 0.5f) { + pt1->strength -= pt1->strength * vran * mmd->factor; + } + else { + pt1->strength += pt1->strength * vran * mmd->factor; + } + 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; + } + else { + pt1->uv_rot += pt1->uv_rot * vran * mmd->factor; + } + CLAMP(pt1->uv_rot, -M_PI_2, M_PI_2); + } + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Noise = { + /* name */ "Noise", + /* structName */ "NoiseGpencilModifierData", + /* structSize */ sizeof(NoiseGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ dependsOnTime, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c new file mode 100644 index 00000000000..8a96c705f08 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c @@ -0,0 +1,143 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpenciloffset.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + OffsetGpencilModifierData *gpmd = (OffsetGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + ARRAY_SET_ITEMS(gpmd->loc, 0.0f, 0.0f, 0.0f); + ARRAY_SET_ITEMS(gpmd->rot, 0.0f, 0.0f, 0.0f); + ARRAY_SET_ITEMS(gpmd->scale, 0.0f, 0.0f, 0.0f); +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* change stroke offsetness */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + + float mat[4][4]; + float loc[3], rot[3], scale[3]; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_OFFSET_INVERT_LAYER, mmd->flag & GP_OFFSET_INVERT_PASS)) + { + return; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_OFFSET_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + /* calculate matrix */ + mul_v3_v3fl(loc, mmd->loc, weight); + mul_v3_v3fl(rot, mmd->rot, weight); + mul_v3_v3fl(scale, mmd->scale, weight); + add_v3_fl(scale, 1.0); + loc_eul_size_to_mat4(mat, loc, rot, scale); + + mul_m4_v3(mat, &pt->x); + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Offset = { + /* name */ "Offset", + /* structName */ "OffsetGpencilModifierData", + /* structSize */ sizeof(OffsetGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c new file mode 100644 index 00000000000..bdd651a69fc --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -0,0 +1,171 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilopacity.c + * \ingroup modifiers + */ + +#include + +#include "BLI_blenlib.h" +#include "BLI_utildefines.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_context.h" +#include "BKE_deform.h" +#include "BKE_material.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + OpacityGpencilModifierData *gpmd = (OpacityGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->factor = 1.0f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* opacity strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md; + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + int vindex = defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier( + ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_OPACITY_INVERT_LAYER, mmd->flag & GP_OPACITY_INVERT_PASS)) + { + return; + } + + gp_style->fill_rgba[3]*= mmd->factor; + + /* if factor is > 1, then force opacity */ + if (mmd->factor > 1.0f) { + gp_style->stroke_rgba[3] += mmd->factor - 1.0f; + if (gp_style->fill_rgba[3] > 1e-5) { + gp_style->fill_rgba[3] += mmd->factor - 1.0f; + } + } + + CLAMP(gp_style->stroke_rgba[3], 0.0f, 1.0f); + CLAMP(gp_style->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]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (!(mmd->flag & GP_OPACITY_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + pt->strength += mmd->factor - 1.0f; + } + else { + pt->strength += (mmd->factor - 1.0f) * weight; + } + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + else { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + if (mmd->vgname == NULL) { + pt->strength *= mmd->factor; + } + else { + float weight = get_modifier_point_weight(dvert, (!(mmd->flag & GP_OPACITY_INVERT_VGROUP) == 0), vindex); + if (weight >= 0) { + pt->strength *= mmd->factor * weight; + } + } + CLAMP(pt->strength, 0.0f, 1.0f); + } + } + +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Opacity = { + /* name */ "Opacity", + /* structName */ "OpacityGpencilModifierData", + /* structSize */ sizeof(OpacityGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c new file mode 100644 index 00000000000..f0400e39b73 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -0,0 +1,123 @@ +/* ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsimplify.c + * \ingroup modifiers + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_vec_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SimplifyGpencilModifierData *gpmd = (SimplifyGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->step = 1; + gpmd->factor = 0.0f; + gpmd->layername[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SimplifyGpencilModifierData *mmd = (SimplifyGpencilModifierData *)md; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 4, gpl, gps, + mmd->flag & GP_SIMPLIFY_INVERT_LAYER, mmd->flag & GP_SIMPLIFY_INVERT_PASS)) + { + return; + } + + if (mmd->mode == GP_SIMPLIFY_FIXED) { + for (int i = 0; i < mmd->step; i++) { + BKE_gpencil_simplify_fixed(gps); + } + } + else { + /* simplify stroke using Ramer-Douglas-Peucker algorithm */ + BKE_gpencil_simplify_stroke(gps, mmd->factor); + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Simplify = { + /* name */ "Simplify", + /* structName */ "SimplifyGpencilModifierData", + /* structSize */ sizeof(SimplifyGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c new file mode 100644 index 00000000000..b83c8ed98b1 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -0,0 +1,152 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsmooth.c + * \ingroup modifiers + */ + +#include + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SmoothGpencilModifierData *gpmd = (SmoothGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->flag |= GP_SMOOTH_MOD_LOCATION; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->step = 1; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* aply smooth effect based on stroke direction */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + float weight = 1.0f; + float val; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_SMOOTH_INVERT_LAYER, mmd->flag & GP_SMOOTH_INVERT_PASS)) + { + return; + } + + /* smooth stroke */ + if (mmd->factor > 0.0f) { + for (int r = 0; r < mmd->step; r++) { + for (int i = 0; i < gps->totpoints; i++) { + // bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + /* verify vertex group */ + weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_SMOOTH_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + val = mmd->factor * weight; + /* perform smoothing */ + if (mmd->flag & GP_SMOOTH_MOD_LOCATION) { + BKE_gpencil_smooth_stroke(gps, i, val); + } + if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) { + BKE_gpencil_smooth_stroke_strength(gps, i, val); + } + if ((mmd->flag & GP_SMOOTH_MOD_THICKNESS) && (val > 0)) { + /* thickness need to repeat process several times */ + for (int r2 = 0; r2 < r * 10; r2++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, val); + } + } + if (mmd->flag & GP_SMOOTH_MOD_UV) { + BKE_gpencil_smooth_stroke_uv(gps, i, val); + } + } + } + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Smooth = { + /* name */ "Smooth", + /* structName */ "SmoothGpencilModifierData", + /* structSize */ sizeof(SmoothGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c new file mode 100644 index 00000000000..6fe9c34c06b --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -0,0 +1,193 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilsubdiv.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + SubdivGpencilModifierData *gpmd = (SubdivGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->level = 1; + gpmd->layername[0] = '\0'; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* subdivide stroke to get more control points */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md; + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + int i2; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_SUBDIV_INVERT_LAYER, mmd->flag & GP_SUBDIV_INVERT_PASS)) + { + return; + } + + /* loop as many times as levels */ + for (int s = 0; s < mmd->level; s++) { + totnewpoints = gps->totpoints - 1; + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* resize the points arrys */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* move points from last to first to new place */ + i2 = gps->totpoints - 1; + for (int i = oldtotpoints - 1; i > 0; i--) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + i2 -= 2; + } + /* interpolate mid points */ + i2 = 1; + for (int i = 0; i < oldtotpoints - 1; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + /* add a half way point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + i2 += 2; + } + + MEM_SAFE_FREE(temp_points); + + /* move points to smooth stroke (not simple flag )*/ + if ((mmd->flag & GP_SUBDIV_SIMPLE) == 0) { + /* duplicate points in a temp area with the new subdivide data */ + temp_points = MEM_dupallocN(gps->points); + + /* extreme points are not changed */ + for (int i = 0; i < gps->totpoints - 2; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i + 1]; + + /* move point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); + } + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = { + /* name */ "Subdivision", + /* structName */ "SubdivGpencilModifierData", + /* structSize */ sizeof(SubdivGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c new file mode 100644 index 00000000000..118cc33255f --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -0,0 +1,171 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpencilthick.c + * \ingroup modifiers + */ + +#include + +#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 "BLI_utildefines.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->thickness = 0; + gpmd->layername[0] = '\0'; + gpmd->vgname[0] = '\0'; + gpmd->curve_thickness = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + if (gpmd->curve_thickness) { + curvemapping_initialize(gpmd->curve_thickness); + } +} + +static void freeData(GpencilModifierData *md) +{ + ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md; + + if (gpmd->curve_thickness) { + curvemapping_free(gpmd->curve_thickness); + } +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + ThickGpencilModifierData *gmd = (ThickGpencilModifierData *)md; + ThickGpencilModifierData *tgmd = (ThickGpencilModifierData *)target; + + if (tgmd->curve_thickness != NULL) { + curvemapping_free(tgmd->curve_thickness); + tgmd->curve_thickness = NULL; + } + + BKE_gpencil_modifier_copyData_generic(md, target); + + tgmd->curve_thickness = curvemapping_copy(gmd->curve_thickness); +} + +/* change stroke thickness */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md; + int vindex = defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 3, gpl, gps, + mmd->flag & GP_THICK_INVERT_LAYER, mmd->flag & GP_THICK_INVERT_PASS)) + { + return; + } + + /* if normalize, set stroke thickness */ + if (mmd->flag & GP_THICK_NORMALIZE) { + gps->thickness = mmd->thickness; + } + + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + float curvef = 1.0f; + /* verify vertex group */ + float weight = get_modifier_point_weight(dvert, (int)(!(mmd->flag & GP_THICK_INVERT_VGROUP) == 0), vindex); + if (weight < 0) { + continue; + } + + if (mmd->flag & GP_THICK_NORMALIZE) { + pt->pressure = 1.0f; + } + else { + if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) { + /* normalize value to evaluate curve */ + float value = (float)i / (gps->totpoints - 1); + curvef = curvemapping_evaluateF(mmd->curve_thickness, 0, value); + } + + pt->pressure += mmd->thickness * weight * curvef; + CLAMP(pt->strength, 0.0f, 1.0f); + } + } +} + +static void bakeModifier( + struct Main *UNUSED(bmain), Depsgraph *depsgraph, + 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) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Thick = { + /* name */ "Thickness", + /* structName */ "ThickGpencilModifierData", + /* structSize */ sizeof(ThickGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c new file mode 100644 index 00000000000..9d1e9eccb8c --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -0,0 +1,186 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/modifiers/intern/MOD_gpenciltint.c + * \ingroup modifiers + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_material.h" +#include "BKE_main.h" + +#include "DEG_depsgraph.h" + +#include "MOD_gpencil_util.h" +#include "MOD_gpencil_modifiertypes.h" + +static void initData(GpencilModifierData *md) +{ + TintGpencilModifierData *gpmd = (TintGpencilModifierData *)md; + gpmd->pass_index = 0; + gpmd->factor = 0.5f; + gpmd->layername[0] = '\0'; + ARRAY_SET_ITEMS(gpmd->rgb, 1.0f, 1.0f, 1.0f); + gpmd->flag |= GP_TINT_CREATE_COLORS; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copyData_generic(md, target); +} + +/* tint strokes */ +static void deformStroke( + GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), + Object *ob, bGPDlayer *gpl, bGPDstroke *gps) +{ + TintGpencilModifierData *mmd = (TintGpencilModifierData *)md; + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, mmd->pass_index, 1, gpl, gps, + mmd->flag & GP_TINT_INVERT_LAYER, mmd->flag & GP_TINT_INVERT_PASS)) + { + return; + } + + interp_v3_v3v3(gps->runtime.tmp_stroke_rgba, gps->runtime.tmp_stroke_rgba, mmd->rgb, mmd->factor); + 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_stroke_rgba[3] += mmd->factor - 1.0f; + if (gps->runtime.tmp_fill_rgba[3] > 1e-5) { + gps->runtime.tmp_fill_rgba[3] += mmd->factor - 1.0f; + } + } + + CLAMP4(gps->runtime.tmp_stroke_rgba, 0.0f, 1.0f); + CLAMP4(gps->runtime.tmp_fill_rgba, 0.0f, 1.0f); + + /* 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); + } + } +} + +static void bakeModifier( + Main *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 = give_current_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); + + /* look for color */ + if (mmd->flag & GP_TINT_CREATE_COLORS) { + Material *newmat = (Material *)BLI_ghash_lookup(gh_color, mat->id.name); + if (newmat == NULL) { + BKE_object_material_slot_add(bmain, ob); + newmat = BKE_material_copy(bmain, mat); + assign_material(bmain, ob, newmat, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + 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); + } + /* reasign color index */ + int idx = BKE_object_material_slot_find_index(ob, newmat); + gps->mat_nr = idx - 1; + } + else { + /* reuse existing color */ + copy_v4_v4(gp_style->stroke_rgba, gps->runtime.tmp_stroke_rgba); + copy_v4_v4(gp_style->fill_rgba, gps->runtime.tmp_fill_rgba); + } + + deformStroke(md, depsgraph, ob, gpl, gps); + } + } + } + /* free hash buffers */ + if (gh_color) { + BLI_ghash_free(gh_color, NULL, NULL); + gh_color = NULL; + } +} + +GpencilModifierTypeInfo modifierType_Gpencil_Tint = { + /* name */ "Tint", + /* structName */ "TintGpencilModifierData", + /* structSize */ sizeof(TintGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 8273f3f1992..b9cc1de447f 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -260,6 +260,14 @@ data_to_c_simple(shaders/gpu_shader_vertex_world.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_vsm_store_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_gpencil_stroke_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_gpencil_stroke_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_gpencil_stroke_geom.glsl SRC) + +data_to_c_simple(shaders/gpu_shader_gpencil_fill_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_gpencil_fill_frag.glsl SRC) + + if(WITH_MOD_SMOKE) add_definitions(-DWITH_SMOKE) endif() diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 7f334cec21f..704abdb13a1 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -346,7 +346,10 @@ typedef enum GPUBuiltinShader { GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE, /* Uniformly scaled */ GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SCALE, GPU_SHADER_INSTANCE_EDGES_VARIYING_COLOR, - /* specialized for UI drawing */ + /* grease pencil drawing */ + GPU_SHADER_GPENCIL_STROKE, + GPU_SHADER_GPENCIL_FILL, + /* specialized for widget drawing */ GPU_SHADER_2D_WIDGET_BASE, GPU_SHADER_2D_WIDGET_BASE_INST, GPU_SHADER_2D_WIDGET_SHADOW, diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index 3543c73f71d..0d5183d82ab 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -162,6 +162,13 @@ extern char datatoc_gpu_shader_vsm_store_frag_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_vert_glsl[]; extern char datatoc_gpu_shader_sep_gaussian_blur_frag_glsl[]; +extern char datatoc_gpu_shader_gpencil_stroke_vert_glsl[]; +extern char datatoc_gpu_shader_gpencil_stroke_frag_glsl[]; +extern char datatoc_gpu_shader_gpencil_stroke_geom_glsl[]; + +extern char datatoc_gpu_shader_gpencil_fill_vert_glsl[]; +extern char datatoc_gpu_shader_gpencil_fill_frag_glsl[]; + /* cache of built-in shaders (each is created on first use) */ static GPUShader *builtin_shaders[GPU_NUM_BUILTIN_SHADERS] = { NULL }; @@ -868,6 +875,13 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) datatoc_gpu_shader_2D_nodelink_frag_glsl }, [GPU_SHADER_2D_NODELINK_INST] = { datatoc_gpu_shader_2D_nodelink_vert_glsl, datatoc_gpu_shader_2D_nodelink_frag_glsl }, + + [GPU_SHADER_GPENCIL_STROKE] = { datatoc_gpu_shader_gpencil_stroke_vert_glsl, + datatoc_gpu_shader_gpencil_stroke_frag_glsl, + datatoc_gpu_shader_gpencil_stroke_geom_glsl }, + + [GPU_SHADER_GPENCIL_FILL] = { datatoc_gpu_shader_gpencil_fill_vert_glsl, + datatoc_gpu_shader_gpencil_fill_frag_glsl }, }; if (builtin_shaders[shader] == NULL) { diff --git a/source/blender/gpu/shaders/gpu_shader_gpencil_fill_frag.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_fill_frag.glsl new file mode 100644 index 00000000000..328fbbe26a1 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_fill_frag.glsl @@ -0,0 +1,166 @@ +uniform vec4 color; +uniform vec4 color2; +uniform int fill_type; +uniform float mix_factor; + +uniform float g_angle; +uniform float g_radius; +uniform float g_boxsize; +uniform vec2 g_scale; +uniform vec2 g_shift; + +uniform float t_angle; +uniform vec2 t_scale; +uniform vec2 t_offset; +uniform int t_mix; +uniform int t_flip; +uniform float t_opacity; + +uniform sampler2D myTexture; + +/* keep this list synchronized with list in DNA_brush_types.h */ +#define SOLID 0 +#define GRADIENT 1 +#define RADIAL 2 +#define CHESS 3 +#define TEXTURE 4 + +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) { + if (flip == 0) { + ocolor = color; + } + else { + ocolor = tcolor; + } + } + else { + if (flip == 0) { + ocolor = color; + } + else { + ocolor = color2; + } + } + } + /* full color B */ + else if (mixv == 0.0) { + if (tmix == 1) { + if (flip == 0) { + ocolor = tcolor; + } + else { + ocolor = color; + } + } + else { + if (flip == 0) { + ocolor = color2; + } + else { + ocolor = color; + } + } + } + /* mix of colors */ + else { + if (tmix == 1) { + if (flip == 0) { + ocolor = mix(color, tcolor, factor); + } + else { + ocolor = mix(tcolor, color, factor); + } + } + else { + if (flip == 0) { + ocolor = mix(color, color2, factor); + } + else { + ocolor = mix(color2, color, factor); + } + } + } +} + +void main() +{ + vec2 t_center = vec2(0.5, 0.5); + mat2 matrot_tex = mat2(cos(t_angle), -sin(t_angle), sin(t_angle), cos(t_angle)); + vec2 rot_tex = (matrot_tex * (texCoord_interp - t_center)) + t_center + t_offset; + vec4 tmp_color = texture2D(myTexture, rot_tex * t_scale); + vec4 text_color = vec4(tmp_color[0], tmp_color[1], tmp_color[2], tmp_color[3] * t_opacity); + vec4 chesscolor; + + /* solid fill */ + if (fill_type == SOLID) { + if (t_mix == 1) { + fragColor = mix(color, text_color, mix_factor); + } + else { + fragColor = color; + } + } + else { + vec2 center = vec2(0.5, 0.5) + g_shift; + mat2 matrot = mat2(cos(g_angle), -sin(g_angle), sin(g_angle), cos(g_angle)); + vec2 rot = (((matrot * (texCoord_interp - center)) + center) * g_scale) + g_shift; + /* gradient */ + if (fill_type == GRADIENT) { + set_color(color, color2, text_color, mix_factor, rot.x - mix_factor + 0.5, t_mix, t_flip, fragColor); + } + /* radial gradient */ + if (fill_type == RADIAL) { + float in_rad = g_radius * mix_factor; + float ex_rad = g_radius - in_rad; + float intensity = 0; + float distance = length((center - texCoord_interp) * g_scale); + if (distance > g_radius) { + discard; + } + if (distance > in_rad) { + intensity = clamp(((distance - in_rad) / ex_rad), 0.0, 1.0); + } + set_color(color, color2, text_color, mix_factor, intensity, t_mix, t_flip, fragColor); + } + /* chessboard */ + if (fill_type == CHESS) { + vec2 pos = rot / g_boxsize; + if ((fract(pos.x) < 0.5 && fract(pos.y) < 0.5) || (fract(pos.x) > 0.5 && fract(pos.y) > 0.5)) { + if (t_flip == 0) { + chesscolor = color; + } + else { + chesscolor = color2; + } + } + else { + if (t_flip == 0) { + chesscolor = color2; + } + else { + chesscolor = color; + } + } + /* mix with texture */ + if (t_mix == 1) { + fragColor = mix(chesscolor, text_color, mix_factor); + } + else { + fragColor = chesscolor; + } + } + /* texture */ + if (fill_type == TEXTURE) { + fragColor = text_color; + } + } +} diff --git a/source/blender/gpu/shaders/gpu_shader_gpencil_fill_vert.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_fill_vert.glsl new file mode 100644 index 00000000000..2bc381a3689 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_fill_vert.glsl @@ -0,0 +1,11 @@ +uniform mat4 ModelViewProjectionMatrix; + +in vec3 pos; +in vec2 texCoord; +out vec2 texCoord_interp; + +void main(void) +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + texCoord_interp = texCoord; +} diff --git a/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_frag.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_frag.glsl new file mode 100644 index 00000000000..7bb7693d202 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_frag.glsl @@ -0,0 +1,20 @@ +in vec4 mColor; +in vec2 mTexCoord; + +out vec4 fragColor; + +void main() +{ + const vec2 center = vec2(0, 0.5); + vec4 tColor = vec4(mColor); + /* if alpha < 0, then encap */ + if (mColor.a < 0) { + tColor.a = tColor.a * -1.0; + float dist = length(mTexCoord - center); + if (dist > 0.25) { + discard; + } + } + /* Solid */ + fragColor = tColor; +} diff --git a/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl new file mode 100644 index 00000000000..3de1bd838b3 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl @@ -0,0 +1,196 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform vec2 Viewport; +uniform int xraymode; + +layout(lines_adjacency) in; +layout(triangle_strip, max_vertices = 13) out; + +in vec4 finalColor[4]; +in float finalThickness[4]; + +out vec4 mColor; +out vec2 mTexCoord; + +#define GP_XRAY_FRONT 0 +#define GP_XRAY_3DSPACE 1 +#define GP_XRAY_BACK 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 0.0; + } + if (xraymode == GP_XRAY_3DSPACE) { + return (point.z / point.w); + } + if (xraymode == GP_XRAY_BACK) { + return 1.0; + } + + /* in front by default */ + return 0.0; +} +void main(void) +{ + float MiterLimit = 0.75; + + /* 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; + + /* 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 (alpha < 0 used as endcap flag)*/ + if (P0 == P2) { + mTexCoord = vec2(1, 0.5); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + vec2 svn1 = normalize(sp1 - sp2) * length_a * 4.0; + gl_Position = vec4((sp1 + svn1) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 0); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + gl_Position = vec4((sp1 - (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 1); + mColor = vec4(finalColor[1].rgb, finalColor[1].a * -1.0) ; + gl_Position = vec4((sp1 + (length_a * 2.0) * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + } + + /* generate the triangle strip */ + mTexCoord = vec2(0, 0); + mColor = finalColor[1]; + gl_Position = vec4((sp1 + length_a * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 1); + mColor = finalColor[1]; + gl_Position = vec4((sp1 - length_a * miter_a) / Viewport, getZdepth(P1), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 0); + mColor = finalColor[2]; + gl_Position = vec4((sp2 + length_b * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 1); + mColor = finalColor[2]; + gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + /* generate the end endcap (alpha < 0 used as endcap flag)*/ + if (P1 == P3) { + mTexCoord = vec2(0, 1); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ; + gl_Position = vec4((sp2 + (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + mTexCoord = vec2(0, 0); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0) ; + gl_Position = vec4((sp2 - (length_b * 2.0) * miter_b) / Viewport, getZdepth(P2), 1.0); + EmitVertex(); + + mTexCoord = vec2(1, 0.5); + mColor = vec4(finalColor[2].rgb, finalColor[2].a * -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/gpu/shaders/gpu_shader_gpencil_stroke_vert.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_vert.glsl new file mode 100644 index 00000000000..5f4f56dcca1 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_vert.glsl @@ -0,0 +1,33 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ProjectionMatrix; + +uniform float pixsize; /* rv3d->pixsize */ +uniform float pixelsize; /* U.pixelsize */ +uniform int keep_size; +uniform float objscale; +uniform int pixfactor; + +in vec3 pos; +in vec4 color; +in float thickness; + +out vec4 finalColor; +out float finalThickness; + +#define TRUE 1 + +float defaultpixsize = pixsize * pixelsize * (1000.0 / pixfactor); + +void main(void) +{ + gl_Position = ModelViewProjectionMatrix * vec4( pos, 1.0 ); + 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); + } +} diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index ec71f28cb23..634819b33ce 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -564,6 +564,7 @@ enum { INDEX_ID_IP, INDEX_ID_AC, INDEX_ID_KE, + INDEX_ID_PAL, INDEX_ID_GD, INDEX_ID_NT, INDEX_ID_IM, @@ -581,7 +582,6 @@ enum { INDEX_ID_TXT, INDEX_ID_SO, INDEX_ID_GR, - INDEX_ID_PAL, INDEX_ID_PC, INDEX_ID_BR, INDEX_ID_PA, diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index fc3b4afe18d..dcb7fbd344b 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -44,6 +44,7 @@ struct CurveMapping; struct MTex; struct Image; +struct Material; typedef struct BrushClone { struct Image *image; /* image for clone tool */ @@ -51,6 +52,109 @@ typedef struct BrushClone { float alpha, pad; /* transparency for drawing of clone image */ } BrushClone; + +typedef struct BrushGpencilSettings { + float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ + float draw_sensitivity; /* amount of sensivity to apply to newly created strokes */ + float draw_strength; /* amount of alpha strength to apply to newly created strokes */ + float draw_jitter; /* amount of jitter to apply to newly created strokes */ + float draw_angle; /* angle when the brush has full thickness */ + float draw_angle_factor; /* factor to apply when angle change (only 90 degrees) */ + float draw_random_press; /* factor of randomness for pressure */ + float draw_random_strength; /* factor of strength for strength */ + float draw_random_sub; /* factor of randomness for subdivision */ + short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ + short draw_subdivide; /* number of times to subdivide new strokes */ + short flag; /* internal grease pencil drawing flags */ + + short thick_smoothlvl; /* number of times to apply thickness smooth factor to new strokes */ + float thick_smoothfac; /* amount of thickness smoothing to apply to newly created strokes */ + + float fill_threshold; /* factor for transparency */ + short fill_leak; /* number of pixel to consider the leak is too small (x 2) */ + char pad_1[6]; + + int fill_simplylvl; /* number of simplify steps */ + int fill_draw_mode; /* type of control lines drawing mode */ + int icon_id; /* icon identifier */ + + int input_samples; /* maximum distance before generate new point for very fast mouse movements */ + float uv_random; /* random factor for UV rotation */ + + int brush_type; /* type of brush (draw, fill, erase, etc..) */ + int eraser_mode; /* soft, hard or stroke */ + float active_smooth; /* smooth while drawing factor */ + char pad_2[4]; + + struct CurveMapping *curve_sensitivity; + struct CurveMapping *curve_strength; + struct CurveMapping *curve_jitter; + + /* optional link of material to replace default in context */ + struct Material *material; /* material */ +} BrushGpencilSettings; + +/* BrushGpencilSettings->gp_flag */ +typedef enum eGPDbrush_Flag { + /* brush use pressure */ + GP_BRUSH_USE_PRESSURE = (1 << 0), + /* brush use pressure for alpha factor */ + 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 */ + GP_BRUSH_FILL_SHOW_HELPLINES = (1 << 7), + /* lazy mouse */ + GP_BRUSH_STABILIZE_MOUSE = (1 << 8), + /* lazy mouse override (internal only) */ + GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9), + /* default eraser brush for quick switch */ + GP_BRUSH_DEFAULT_ERASER = (1 << 10), + /* settings group */ + GP_BRUSH_GROUP_SETTINGS = (1 << 11), + /* Random settings group */ + GP_BRUSH_GROUP_RANDOM = (1 << 12) +} eGPDbrush_Flag; + +/* BrushGpencilSettings->gp_fill_draw_mode */ +typedef enum eGP_FillDrawModes { + GP_FILL_DMODE_BOTH = 0, + GP_FILL_DMODE_STROKE = 1, + GP_FILL_DMODE_CONTROL = 2, +} eGP_FillDrawModes; + +/* BrushGpencilSettings->brush type */ +typedef enum eGP_BrushType { + GP_BRUSH_TYPE_DRAW = 0, + GP_BRUSH_TYPE_FILL = 1, + GP_BRUSH_TYPE_ERASE = 2, +} eGP_BrushType; + +/* BrushGpencilSettings->gp_eraser_mode */ +typedef enum eGP_BrushEraserMode { + GP_BRUSH_ERASER_SOFT = 0, + GP_BRUSH_ERASER_HARD = 1, + GP_BRUSH_ERASER_STROKE = 2, +} eGP_BrushEraserMode; + +/* BrushGpencilSettings default brush icons */ +typedef enum eGP_BrushIcons { + GP_BRUSH_ICON_PENCIL = 1, + GP_BRUSH_ICON_PEN = 2, + GP_BRUSH_ICON_INK = 3, + GP_BRUSH_ICON_INKNOISE = 4, + GP_BRUSH_ICON_BLOCK = 5, + GP_BRUSH_ICON_MARKER = 6, + GP_BRUSH_ICON_FILL = 7, + GP_BRUSH_ICON_ERASE_SOFT = 8, + GP_BRUSH_ICON_ERASE_HARD = 9, + GP_BRUSH_ICON_ERASE_STROKE = 10 +} eGP_BrushIcons; + typedef struct Brush { ID id; @@ -139,8 +243,10 @@ typedef struct Brush { float mask_stencil_pos[2]; float mask_stencil_dimension[2]; -} Brush; + struct BrushGpencilSettings *gpencil_settings; + +} Brush; typedef struct PaletteColor { struct PaletteColor *next, *prev; /* two values, one to store rgb, other to store values for sculpt/weight */ @@ -355,5 +461,6 @@ enum { }; #define MAX_BRUSH_PIXEL_RADIUS 500 +#define GP_MAX_BRUSH_PIXEL_RADIUS 1000 #endif /* __DNA_BRUSH_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index 8ed38b0b05d..4f860e16b88 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -99,6 +99,7 @@ typedef enum eCurveMappingPreset { CURVE_PRESET_MID9 = 4, CURVE_PRESET_ROUND = 5, CURVE_PRESET_ROOT = 6, + CURVE_PRESET_GAUSS = 7, } eCurveMappingPreset; /* histogram->mode */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h new file mode 100644 index 00000000000..150b4a2d9f1 --- /dev/null +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -0,0 +1,404 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_greasepencil_modifier_types.h + * \ingroup DNA + */ + +#ifndef __DNA_GREASEPENCIL_TYPES_H__ +#define __DNA_GREASEPENCIL_TYPES_H__ + +#include "DNA_defs.h" +#include "DNA_listBase.h" + +/* WARNING ALERT! TYPEDEF VALUES ARE WRITTEN IN FILES! SO DO NOT CHANGE! + * (ONLY ADD NEW ITEMS AT THE END) + */ + +struct RNG; + +typedef enum GpencilModifierType { + eGpencilModifierType_None = 0, + eGpencilModifierType_Noise = 1, + eGpencilModifierType_Subdiv = 2, + eGpencilModifierType_Thick = 3, + eGpencilModifierType_Tint = 4, + eGpencilModifierType_Instance = 5, + eGpencilModifierType_Build = 6, + eGpencilModifierType_Opacity = 7, + eGpencilModifierType_Color = 8, + eGpencilModifierType_Lattice = 9, + eGpencilModifierType_Simplify = 10, + eGpencilModifierType_Smooth = 11, + eGpencilModifierType_Hook = 12, + eGpencilModifierType_Offset = 13, + eGpencilModifierType_Mirror = 14, + NUM_GREASEPENCIL_MODIFIER_TYPES +} GpencilModifierType; + +typedef enum GpencilModifierMode { + eGpencilModifierMode_Realtime = (1 << 0), + eGpencilModifierMode_Render = (1 << 1), + eGpencilModifierMode_Editmode = (1 << 2), + eGpencilModifierMode_Expanded = (1 << 3), +} GpencilModifierMode; + +typedef enum { + /* This modifier has been inserted in local override, and hence can be fully edited. */ + eGpencilModifierFlag_StaticOverride_Local = (1 << 0), +} GpencilModifierFlag; + +typedef struct GpencilModifierData { + struct GpencilModifierData *next, *prev; + + int type, mode; + int stackindex; + short flag; + short pad; + char name[64]; /* MAX_NAME */ + + char *error; +} GpencilModifierData; + +typedef struct NoiseGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* several flags */ + float factor; /* factor of noise */ + int step; /* how many frames before recalculate randoms */ + int gp_frame; /* last gp frame used */ + int scene_frame; /* last scene frame used */ + float vrand1, vrand2; /* random values */ + struct RNG *rng; +} NoiseGpencilModifierData; + +typedef enum eNoiseGpencil_Flag { + GP_NOISE_USE_RANDOM = (1 << 0), + GP_NOISE_MOD_LOCATION = (1 << 1), + 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_INVERT_LAYER = (1 << 6), + GP_NOISE_INVERT_PASS = (1 << 7), + GP_NOISE_INVERT_VGROUP = (1 << 8), + GP_NOISE_MOD_UV = (1 << 9), +} eNoiseGpencil_Flag; + +typedef struct SubdivGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + int level; /* factor of subdivision */ + char pad[4]; +} SubdivGpencilModifierData; + +typedef enum eSubdivGpencil_Flag { + GP_SUBDIV_SIMPLE = (1 << 0), + GP_SUBDIV_INVERT_LAYER = (1 << 1), + GP_SUBDIV_INVERT_PASS = (1 << 2), +} eSubdivGpencil_Flag; + +typedef struct ThickGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + int thickness; /* Thickness change */ + char pad[4]; + struct CurveMapping *curve_thickness; +} ThickGpencilModifierData; + +typedef enum eThickGpencil_Flag { + GP_THICK_INVERT_LAYER = (1 << 0), + GP_THICK_INVERT_PASS = (1 << 1), + GP_THICK_INVERT_VGROUP = (1 << 2), + GP_THICK_CUSTOM_CURVE = (1 << 3), + GP_THICK_NORMALIZE = (1 << 4), +} eThickGpencil_Flag; + +typedef struct TintGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float rgb[3]; /* Tint color */ + float factor; /* Mix factor */ +} TintGpencilModifierData; + +typedef enum eTintGpencil_Flag { + GP_TINT_CREATE_COLORS = (1 << 0), + GP_TINT_INVERT_LAYER = (1 << 1), + GP_TINT_INVERT_PASS = (1 << 2), +} eTintGpencil_Flag; + +typedef struct ColorGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float hsv[3]; /* hsv factors */ + char pad[4]; +} ColorGpencilModifierData; + +typedef enum eColorGpencil_Flag { + GP_COLOR_CREATE_COLORS = (1 << 0), + GP_COLOR_INVERT_LAYER = (1 << 1), + GP_COLOR_INVERT_PASS = (1 << 2), +} eColorGpencil_Flag; + +typedef struct OpacityGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float factor; /* Main Opacity factor */ + char pad[4]; +} OpacityGpencilModifierData; + +typedef enum eOpacityGpencil_Flag { + GP_OPACITY_INVERT_LAYER = (1 << 0), + GP_OPACITY_INVERT_PASS = (1 << 1), + GP_OPACITY_INVERT_VGROUP = (1 << 2), +} eOpacityGpencil_Flag; + +typedef struct InstanceGpencilModifierData { + GpencilModifierData modifier; + int count[3]; /* number of elements in array */ + int flag; /* several flags */ + float offset[3]; /* Location increments */ + float shift[3]; /* shift increment */ + float rnd_size; /* random size factor */ + float rnd_rot; /* random size factor */ + float rot[3]; /* Rotation changes */ + float scale[3]; /* Scale changes */ + float rnd[20]; /* (first element is the index) random values */ + int lock_axis; /* lock shift to one axis */ + + int pass_index; /* custom index for passes */ + char layername[64]; /* layer name */ +} InstanceGpencilModifierData; + +typedef enum eInstanceGpencil_Flag { + GP_INSTANCE_RANDOM_SIZE = (1 << 0), + GP_INSTANCE_RANDOM_ROT = (1 << 1), + GP_INSTANCE_INVERT_LAYER = (1 << 2), + GP_INSTANCE_INVERT_PASS = (1 << 3), + GP_INSTANCE_MAKE_OBJECTS = (1 << 4), +} eInstanceGpencil_Flag; + +typedef struct BuildGpencilModifierData { + GpencilModifierData modifier; + + char layername[64]; /* if set, restrict modifier to operating on this layer */ + int pass_index; + + int pad; + + float start_frame; /* If GP_BUILD_RESTRICT_TIME is set, the defines the frame range where GP frames are considered */ + float end_frame; + + float start_delay; /* For each pair of gp keys, number of frames before strokes start appearing */ + float length; /* For each pair of gp keys, number of frames that build effect must be completed within */ + + short flag; /* (eGpencilBuild_Flag) Options for controlling modifier behaviour */ + + short mode; /* (eGpencilBuild_Mode) How are strokes ordered */ + short transition; /* (eGpencilBuild_Transition) In what order do stroke points appear/disappear */ + + short time_alignment; /* (eGpencilBuild_TimeAlignment) For the "Concurrent" mode, when should "shorter" strips start/end */ +} BuildGpencilModifierData; + +typedef enum eBuildGpencil_Mode { + /* Strokes are shown one by one until all have appeared */ + GP_BUILD_MODE_SEQUENTIAL = 0, + /* All strokes start at the same time */ + GP_BUILD_MODE_CONCURRENT = 1, +} eBuildGpencil_Mode; + +typedef enum eBuildGpencil_Transition { + /* Show in forward order */ + GP_BUILD_TRANSITION_GROW = 0, + /* Hide in reverse order */ + GP_BUILD_TRANSITION_SHRINK = 1, + /* Hide in forward order */ + GP_BUILD_TRANSITION_FADE = 2, +} eBuildGpencil_Transition; + +typedef enum eBuildGpencil_TimeAlignment { + /* All strokes start at same time */ + GP_BUILD_TIMEALIGN_START = 0, + /* All strokes end at same time */ + GP_BUILD_TIMEALIGN_END = 1, + + /* TODO: Random Offsets, Stretch-to-Fill */ +} eBuildGpencil_TimeAlignment; + +typedef enum eBuildGpencil_Flag { + /* Restrict modifier to particular layer/passes? */ + GP_BUILD_INVERT_LAYER = (1 << 0), + GP_BUILD_INVERT_PASS = (1 << 1), + + /* Restrict modifier to only operating between the nominated frames */ + GP_BUILD_RESTRICT_TIME = (1 << 2), +} eBuildGpencil_Flag; + +typedef struct LatticeGpencilModifierData { + GpencilModifierData modifier; + struct Object *object; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float strength; + char pad[4]; + void *cache_data; /* runtime only (LatticeDeformData) */ +} LatticeGpencilModifierData; + +typedef enum eLatticeGpencil_Flag { + GP_LATTICE_INVERT_LAYER = (1 << 0), + GP_LATTICE_INVERT_PASS = (1 << 1), + GP_LATTICE_INVERT_VGROUP = (1 << 2), +} eLatticeGpencil_Flag; + +typedef struct MirrorGpencilModifierData { + GpencilModifierData modifier; + struct Object *object; + char layername[64]; /* layer name */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ +} MirrorGpencilModifierData; + +typedef enum eMirrorGpencil_Flag { + GP_MIRROR_INVERT_LAYER = (1 << 0), + GP_MIRROR_INVERT_PASS = (1 << 1), + GP_MIRROR_CLIPPING = (1 << 2), + GP_MIRROR_AXIS_X = (1 << 3), + GP_MIRROR_AXIS_Y = (1 << 4), + GP_MIRROR_AXIS_Z = (1 << 5), +} eMirrorGpencil_Flag; + +typedef struct HookGpencilModifierData { + GpencilModifierData modifier; + + struct Object *object; + char subtarget[64]; /* optional name of bone target, MAX_ID_NAME-2 */ + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + + int flag; + char falloff_type; /* use enums from WarpGpencilModifier (exact same functionality) */ + char pad[3]; + float parentinv[4][4]; /* matrix making current transform unmodified */ + float cent[3]; /* visualization of hook */ + float falloff; /* if not zero, falloff is distance where influence zero */ + float force; + struct CurveMapping *curfalloff; +} HookGpencilModifierData; + +typedef enum eHookGpencil_Flag { + GP_HOOK_INVERT_LAYER = (1 << 0), + GP_HOOK_INVERT_PASS = (1 << 1), + GP_HOOK_INVERT_VGROUP = (1 << 2), + GP_HOOK_UNIFORM_SPACE = (1 << 3), +} eHookGpencil_Flag; + +typedef enum eHookGpencil_Falloff { + eGPHook_Falloff_None = 0, + eGPHook_Falloff_Curve = 1, + eGPHook_Falloff_Sharp = 2, + eGPHook_Falloff_Smooth = 3, + eGPHook_Falloff_Root = 4, + eGPHook_Falloff_Linear = 5, + eGPHook_Falloff_Const = 6, + eGPHook_Falloff_Sphere = 7, + eGPHook_Falloff_InvSquare = 8, +} eHookGpencil_Falloff; + +typedef struct SimplifyGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float factor; /* factor of simplify */ + short mode; /* type of simplify */ + short step; /* every n vertex to keep */ +} SimplifyGpencilModifierData; + +typedef enum eSimplifyGpencil_Flag { + GP_SIMPLIFY_INVERT_LAYER = (1 << 0), + GP_SIMPLIFY_INVERT_PASS = (1 << 1), +} eSimplifyGpencil_Flag; + +typedef enum eSimplifyGpencil_Mode { + /* Keep only one vertex every n vertices */ + GP_SIMPLIFY_FIXED = 0, + /* Use RDP algorithm */ + GP_SIMPLIFY_ADAPTATIVE = 1, +} eSimplifyGpencil_Mode; + +typedef struct OffsetGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* flags */ + float loc[3]; + float rot[3]; + float scale[3]; + char pad[4]; +} OffsetGpencilModifierData; + +typedef enum eOffsetGpencil_Flag { + GP_OFFSET_INVERT_LAYER = (1 << 0), + GP_OFFSET_INVERT_PASS = (1 << 1), + GP_OFFSET_INVERT_VGROUP = (1 << 2) +} eOffsetGpencil_Flag; + +typedef struct SmoothGpencilModifierData { + GpencilModifierData modifier; + char layername[64]; /* layer name */ + char vgname[64]; /* optional vertexgroup name, MAX_VGROUP_NAME */ + int pass_index; /* custom index for passes */ + int flag; /* several flags */ + float factor; /* factor of noise */ + int step; /* how many times apply smooth */ +} SmoothGpencilModifierData; + +typedef enum eSmoothGpencil_Flag { + GP_SMOOTH_MOD_LOCATION = (1 << 0), + GP_SMOOTH_MOD_STRENGTH = (1 << 1), + GP_SMOOTH_MOD_THICKNESS = (1 << 2), + GP_SMOOTH_INVERT_LAYER = (1 << 3), + GP_SMOOTH_INVERT_PASS = (1 << 4), + GP_SMOOTH_INVERT_VGROUP = (1 << 5), + GP_SMOOTH_MOD_UV = (1 << 6), +} eSmoothGpencil_Flag; + +#define MOD_MESHSEQ_READ_ALL \ + (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR) + +#endif /* __DNA_GREASEPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index e2ee561de7f..8febfbc8ffc 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -36,6 +36,17 @@ struct AnimData; struct CurveMapping; +struct GHash; +struct MDeformVert; + +/* TODO: add size as userprefs parameter */ +#define GP_OBGPENCIL_DEFAULT_SIZE 0.2f +#define GP_DEFAULT_PIX_FACTOR 1.0f +#define GP_DEFAULT_GRID_LINES 4 +#define GP_MAX_INPUT_SAMPLES 10 + +/* ***************************************** */ +/* GP Stroke Points */ /* Grease-Pencil Annotations - 'Stroke Point' * -> Coordinates may either be 2d or 3d depending on settings at the time @@ -47,7 +58,10 @@ typedef struct bGPDspoint { float pressure; /* pressure of input device (from 0 to 1) at this point */ float strength; /* color strength (used for alpha factor) */ float time; /* seconds since start of stroke */ - int flag; /* additional options (NOTE: can shrink this field down later if needed) */ + int flag; /* additional options */ + + float uv_fac; /* factor of uv along the stroke */ + float uv_rot; /* uv rotation for dot mode */ } bGPDspoint; /* bGPDspoint->flag */ @@ -59,54 +73,24 @@ typedef enum eGPDspoint_Flag { GP_SPOINT_TAG = (1 << 1), } eGPSPoint_Flag; +/* ***************************************** */ +/* GP Fill - Triangle Tesselation Data */ + /* Grease-Pencil Annotations - 'Triangle' - * A triangle contains the index of three vertices for filling the stroke - * This is only used if high quality fill is enabled. - * (not saved to blend file). + * -> A triangle contains the index of three vertices for filling the stroke + * This is only used if high quality fill is enabled */ typedef struct bGPDtriangle { /* indices for tesselated triangle used for GP Fill */ unsigned int verts[3]; + /* texture coordinates for verts */ + float uv[3][2]; } bGPDtriangle; -/* GP brush (used for new strokes) */ -typedef struct bGPDbrush { - struct bGPDbrush *next, *prev; +/* ***************************************** */ - char info[64]; /* Brush name. Must be unique. */ - short thickness; /* thickness to apply to strokes */ - short flag; - float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ - short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ - short sublevel; /* number of times to subdivide new strokes */ - - float draw_sensitivity; /* amount of sensivity to apply to newly created strokes */ - float draw_strength; /* amount of alpha strength to apply to newly created strokes */ - float draw_jitter; /* amount of jitter to apply to newly created strokes */ - float draw_angle; /* angle when the brush has full thickness */ - float draw_angle_factor; /* factor to apply when angle change (only 90 degrees) */ - float draw_random_press; /* factor of randomness for sensitivity and strength */ - float draw_random_sub; /* factor of randomness for subdivision */ - struct CurveMapping *cur_sensitivity; - struct CurveMapping *cur_strength; - struct CurveMapping *cur_jitter; -} bGPDbrush; - -/* bGPDbrush->flag */ -typedef enum eGPDbrush_Flag { - /* brush is active */ - GP_BRUSH_ACTIVE = (1 << 0), - /* brush use pressure */ - GP_BRUSH_USE_PRESSURE = (1 << 1), - /* brush use pressure for alpha factor */ - GP_BRUSH_USE_STENGTH_PRESSURE = (1 << 2), - /* brush use pressure for alpha factor */ - GP_BRUSH_USE_JITTER_PRESSURE = (1 << 3), - /* brush use random for pressure */ - GP_BRUSH_USE_RANDOM_PRESSURE = (1 << 4), - /* brush use random for strength */ - GP_BRUSH_USE_RANDOM_STRENGTH = (1 << 5) -} eGPDbrush_Flag; +/* ***************************************** */ +/* GP Palettes (Deprecated - 2.78 - 2.79 only) */ /* color of palettes */ typedef struct bGPDpalettecolor { @@ -129,9 +113,7 @@ typedef enum eGPDpalettecolor_Flag { /* do onion skinning */ PC_COLOR_ONIONSKIN = (1 << 3), /* "volumetric" strokes */ - PC_COLOR_VOLUMETRIC = (1 << 4), - /* Use High quality fill */ - PC_COLOR_HQ_FILL = (1 << 5) + PC_COLOR_VOLUMETRIC = (1 << 4) } eGPDpalettecolor_Flag; /* palette of colors */ @@ -152,6 +134,21 @@ typedef enum eGPDpalette_Flag { PL_PALETTE_ACTIVE = (1 << 0) } eGPDpalette_Flag; +/* ***************************************** */ +/* GP Strokes */ + +/* Runtime temp data for bGPDstroke */ +typedef struct bGPDstroke_runtime { + /* runtime final colors (result of original colors and modifiers) */ + float tmp_stroke_rgba[4]; + 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]; + + float multi_frame_falloff; /* runtime falloff factor (only for transform) */ +} bGPDstroke_runtime; + /* Grease-Pencil Annotations - 'Stroke' * -> A stroke represents a (simplified version) of the curve * drawn by the user in one 'mousedown'->'mouseup' operation @@ -168,14 +165,16 @@ typedef struct bGPDstroke { short flag, pad[2]; /* various settings about this stroke */ double inittime; /* Init time of stroke */ - /* The pointer to color is only used during drawing, but not saved - * colorname is the join with the palette, but when draw, the pointer is update if the value is NULL - * to speed up the drawing - */ - char colorname[128]; /* color name */ - bGPDpalettecolor *palcolor; /* current palette color */ - /* temporary layer name only used during copy/paste to put the stroke in the original layer */ - char tmp_layerinfo[128]; + + char colorname[128] DNA_DEPRECATED; /* color name */ + + int mat_nr; /* material index */ + char pad_[4]; + + struct MDeformVert *dvert; /* vertex weight data */ + + bGPDstroke_runtime runtime; + char pad_1[4]; } bGPDstroke; /* bGPDstroke->flag */ @@ -190,14 +189,22 @@ typedef enum eGPDstroke_Flag { GP_STROKE_SELECT = (1 << 3), /* Recalculate triangulation for high quality fill (when true, force a new recalc) */ GP_STROKE_RECALC_CACHES = (1 << 4), - /* Recalculate the color pointer using the name as index (true force a new recalc) */ - GP_STROKE_RECALC_COLOR = (1 << 5), /* 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), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15) } eGPDstroke_Flag; +/* ***************************************** */ +/* GP Frame */ + +/* Runtime temp data for bGPDframe */ +typedef struct bGPDframe_runtime { + float viewmatrix[4][4]; /* parent matrix for drawing */ +} bGPDframe_runtime; + /* Grease-Pencil Annotations - 'Frame' * -> Acts as storage for the 'image' formed by strokes */ @@ -210,6 +217,8 @@ typedef struct bGPDframe { short flag; /* temp settings */ short key_type; /* keyframe type (eBezTriple_KeyframeType) */ + + bGPDframe_runtime runtime; } bGPDframe; /* bGPDframe->flag */ @@ -220,6 +229,16 @@ typedef enum eGPDframe_Flag { GP_FRAME_SELECT = (1 << 1) } eGPDframe_Flag; +/* ***************************************** */ +/* GP Layer */ + +/* Runtime temp data for bGPDlayer */ +typedef struct bGPDlayer_runtime { + struct GHash *derived_data; /* runtime data created by modifiers */ + int icon_id; /* id for dynamic icon used to show annotation color preview for layer */ + char pad[4]; +} bGPDlayer_runtime; + /* Grease-Pencil Annotations - 'Layer' */ typedef struct bGPDlayer { struct bGPDlayer *next, *prev; @@ -228,27 +247,27 @@ typedef struct bGPDlayer { bGPDframe *actframe; /* active frame (should be the frame that is currently being displayed) */ short flag; /* settings for layer */ - short thickness; /* current thickness to apply to strokes */ + short onion_flag; /* Per-layer onion-skinning flags (eGPDlayer_OnionFlag) */ - short gstep; /* Ghosts Before: max number of ghost frames to show between active frame and the one before it (0 = only the ghost itself) */ - short gstep_next; /* Ghosts After: max number of ghost frames to show after active frame and the following it (0 = only the ghost itself) */ + float color[4]; /* Color for strokes in layers. Used for annotations, and for ruler (which uses GPencil internally) */ + float fill[4]; /* Fill color for strokes in layers. Not used anymore (was only for) */ - float gcolor_prev[3]; /* optional color for ghosts before the active frame */ - float gcolor_next[3]; /* optional color for ghosts after the active frame */ + char info[128]; /* name/reference info for this layer (i.e. "director's comments, 12/3") + * needs to be kept unique, as it's used as the layer identifier */ - float color[4]; /* Color for strokes in layers (replaced by palettecolor). Only used for ruler (which uses GPencil internally) */ - float fill[4]; /* Fill color for strokes in layers. Not used and replaced by palettecolor fill */ - - char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3") - * this is used for the name of the layer too and kept unique. */ + short thickness; /* thickness to apply to strokes (Annotations) */ + char pad_1[2]; struct Object *parent; /* parent object */ float inverse[4][4]; /* inverse matrix (only used if parented) */ char parsubstr[64]; /* String describing subobject info, MAX_ID_NAME-2 */ - short partype, pad; + short partype; + short line_change; /* Thickness adjustment */ float tintcolor[4]; /* Color used to tint layer, alpha value is used as factor */ float opacity; /* Opacity of the layer */ + + bGPDlayer_runtime runtime; } bGPDlayer; /* bGPDlayer->flag */ @@ -261,51 +280,89 @@ typedef enum eGPDlayer_Flag { GP_LAYER_ACTIVE = (1 << 2), /* draw points of stroke for debugging purposes */ GP_LAYER_DRAWDEBUG = (1 << 3), - /* do onion skinning */ - GP_LAYER_ONIONSKIN = (1 << 4), /* for editing in Action Editor */ GP_LAYER_SELECT = (1 << 5), /* current frame for layer can't be changed */ GP_LAYER_FRAMELOCK = (1 << 6), /* don't render xray (which is default) */ GP_LAYER_NO_XRAY = (1 << 7), - /* use custom color for ghosts before current frame */ - GP_LAYER_GHOST_PREVCOL = (1 << 8), - /* use custom color for ghosts after current frame */ - GP_LAYER_GHOST_NEXTCOL = (1 << 9), /* "volumetric" strokes */ GP_LAYER_VOLUMETRIC = (1 << 10), - /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ - GP_LAYER_HQ_FILL = (1 << 11), /* Unlock color */ GP_LAYER_UNLOCK_COLOR = (1 << 12), - /* always show onion skins (i.e. even during renders/animation playback) */ - GP_LAYER_GHOST_ALWAYS = (1 << 13), } eGPDlayer_Flag; +/* bGPDlayer->onion_flag */ +typedef enum eGPDlayer_OnionFlag { + /* do onion skinning */ + GP_LAYER_ONIONSKIN = (1 << 0), +} eGPDlayer_OnionFlag; + +/* ***************************************** */ +/* GP Datablock */ + +/* Runtime temp data for bGPdata */ +typedef struct bGPdata_runtime { + /* Drawing Manager cache */ + struct GHash *batch_cache_data; + void *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */ + + /* GP Object drawing */ + float scolor[4]; /* buffer stroke color */ + float sfill[4]; /* buffer fill color */ + short mode; /* settings for color */ + short bstroke_style; /* buffer style for drawing strokes (used to select shader type) */ + short bfill_style; /* buffer style for filling areas (used to select shader type) */ + + /* Stroke Buffer data (only used during paint-session) + * - buffer must be initialized before use, but freed after + * whole paint operation is over + */ + short sbuffer_size; /* number of elements currently in cache */ + short sbuffer_sflag; /* flags for stroke that cache represents */ + char pad_[6]; +} bGPdata_runtime; + /* Grease-Pencil Annotations - 'DataBlock' */ typedef struct bGPdata { ID id; /* Grease Pencil data is a datablock */ struct AnimData *adt; /* animation data - for animating draw settings */ - /* saved Grease-Pencil data */ + /* Grease-Pencil data */ ListBase layers; /* bGPDlayers */ int flag; /* settings for this datablock */ - /* not-saved stroke buffer data (only used during paint-session) - * - buffer must be initialized before use, but freed after - * whole paint operation is over - */ - short sbuffer_size; /* number of elements currently in cache */ - short sbuffer_sflag; /* flags for stroke that cache represents */ - void *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */ - float scolor[4]; /* buffer color using palettes */ - float sfill[4]; /* buffer fill color */ - char pad[6]; /* padding for compiler alignment error */ - short sflag; /* settings for palette color */ + short xray_mode; /* xray mode for strokes (eGP_DepthOrdering) */ + char pad_1[2]; - /* saved palettes */ - ListBase palettes; + /* Palettes */ + ListBase palettes DNA_DEPRECATED; /* list of bGPDpalette's - Deprecated (2.78 - 2.79 only) */ + + /* 3D Viewport/Appearance Settings */ + float pixfactor; /* factor to define pixel size conversion */ + float line_color[4]; /* color for edit line */ + + /* Onion skinning */ + float onion_factor; /* onion alpha factor change */ + int onion_mode; /* onion skinning range (eGP_OnionModes) */ + int onion_flag; /* onion skinning flags (eGPD_OnionFlag) */ + short gstep; /* Ghosts Before: max number of ghost frames to show between active frame and the one before it (0 = only the ghost itself) */ + short gstep_next; /* Ghosts After: max number of ghost frames to show after active frame and the following it (0 = only the ghost itself) */ + + float gcolor_prev[3]; /* optional color for ghosts before the active frame */ + float gcolor_next[3]; /* optional color for ghosts after the active frame */ + + char pad[4]; + struct Material **mat; /* materials array */ + short totcol; /* total materials */ + + /* stats */ + short totlayer; + short totframe; + short totstroke; + short totpoint; + char pad_2[6]; + bGPdata_runtime runtime; } bGPdata; /* bGPdata->flag */ @@ -314,8 +371,12 @@ typedef struct bGPdata { * changes made during the porting process. */ typedef enum eGPdata_Flag { - /* don't allow painting to occur at all */ - /* GP_DATA_LMBPLOCK = (1 << 0), */ + /* datablock is used for "annotations" + * NOTE: This flag used to be used in 2.4x, but should hardly ever have been set. + * We can use this freely now, as all GP datablocks from pre-2.8 will get + * set on file load (as many old use cases are for "annotations" only) + */ + GP_DATA_ANNOTATIONS = (1 << 0), /* show debugging info in viewport (i.e. status print) */ GP_DATA_DISPINFO = (1 << 1), @@ -339,10 +400,80 @@ typedef enum eGPdata_Flag { /* Stroke Editing Mode - Toggle to enable alternative keymap for easier editing of stroke points */ GP_DATA_STROKE_EDITMODE = (1 << 8), - /* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */ + /* 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) + GP_DATA_SHOW_DIRECTION = (1 << 10), + + /* Batch drawing cache need to be recalculated */ + GP_DATA_CACHE_IS_DIRTY = (1 << 11), + + /* Stroke Paint Mode - Toggle paint mode */ + GP_DATA_STROKE_PAINTMODE = (1 << 12), + /* Stroke Editing Mode - Toggle sculpt mode */ + GP_DATA_STROKE_SCULPTMODE = (1 << 13), + /* Stroke Editing Mode - Toggle weight paint mode */ + GP_DATA_STROKE_WEIGHTMODE = (1 << 14), + + /* keep stroke thickness unchanged when zoom change */ + GP_DATA_STROKE_KEEPTHICKNESS = (1 << 15), + + /* Allow edit several frames at the same time */ + GP_DATA_STROKE_MULTIEDIT = (1 << 16), } eGPdata_Flag; +/* gpd->onion_flag */ +typedef enum eGPD_OnionFlag { + /* use custom color for ghosts before current frame */ + GP_ONION_GHOST_PREVCOL = (1 << 0), + /* use custom color for ghosts after current frame */ + GP_ONION_GHOST_NEXTCOL = (1 << 1), + /* always show onion skins (i.e. even during renders/animation playback) */ + GP_ONION_GHOST_ALWAYS = (1 << 2), + /* use fade color in onion skin */ + GP_ONION_FADE = (1 << 3), + /* Loop showing first frame after last frame */ + GP_ONION_LOOP = (1 << 4), +} eGPD_OnionFlag; + +/* gpd->onion_mode */ +typedef enum eGP_OnionModes { + GP_ONION_MODE_ABSOLUTE = 0, + GP_ONION_MODE_RELATIVE = 1, + GP_ONION_MODE_SELECTED = 2, +} eGP_OnionModes; + +/* xray modes (Depth Ordering) */ +typedef enum eGP_DepthOrdering { + GP_XRAY_FRONT = 0, + GP_XRAY_3DSPACE = 1, + GP_XRAY_BACK = 2 +} eGP_DepthOrdering; + +/* ***************************************** */ +/* Mode Checking Macros */ + +/* 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_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_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_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)) +#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))) + #endif /* __DNA_GPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index eb469895fd7..50d9b890724 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -54,6 +54,59 @@ typedef struct TexPaintSlot { int pad; } TexPaintSlot; +typedef struct MaterialGPencilStyle { + struct Image *sima; /* Texture image for strokes */ + struct Image *ima; /* Texture image for filling */ + float stroke_rgba[4]; /* color for paint and strokes (alpha included) */ + float fill_rgba[4]; /* color that should be used for drawing "fills" for strokes (alpha included) */ + float mix_rgba[4]; /* secondary color used for gradients and other stuff */ + short flag; /* settings */ + short index; /* custom index for passes */ + short stroke_style; /* style for drawing strokes (used to select shader type) */ + short fill_style; /* style for filling areas (used to select shader type) */ + float mix_factor; /* factor used to define shader behavior (several uses) */ + float gradient_angle; /* angle used for gradients orientation */ + float gradient_radius; /* radius for radial gradients */ + float pattern_gridsize; /* cheesboard size */ + float gradient_scale[2]; /* uv coordinates scale */ + float gradient_shift[2]; /* factor to shift filling in 2d space */ + float texture_angle; /* angle used for texture orientation */ + float texture_scale[2]; /* texture scale (separated of uv scale) */ + float texture_offset[2]; /* factor to shift texture in 2d space */ + float texture_opacity; /* texture opacity */ + float texture_pixsize; /* pixel size for uv along the stroke */ + int mode; /* drawing mode (line or dots) */ + + int gradient_type; /* type of gradient */ + char pad[4]; +} MaterialGPencilStyle; + +/* MaterialGPencilStyle->flag */ +typedef enum eMaterialGPencilStyle_Flag { + /* Fill Texture is a pattern */ + GP_STYLE_FILL_PATTERN = (1 << 0), + /* don't display color */ + GP_STYLE_COLOR_HIDE = (1 << 1), + /* protected from further editing */ + GP_STYLE_COLOR_LOCKED = (1 << 2), + /* do onion skinning */ + GP_STYLE_COLOR_ONIONSKIN = (1 << 3), + /* clamp texture */ + GP_STYLE_COLOR_TEX_CLAMP = (1 << 4), + /* mix texture */ + GP_STYLE_COLOR_TEX_MIX = (1 << 5), + /* Flip fill colors */ + GP_STYLE_COLOR_FLIP_FILL = (1 << 6), + /* Stroke Texture is a pattern */ + GP_STYLE_STROKE_PATTERN = (1 << 7) +} eMaterialGPencilStyle_Flag; + +typedef enum eMaterialGPencilStyle_Mode { + GP_STYLE_MODE_LINE = 0, /* line */ + GP_STYLE_MODE_DOTS = 1, /* dots */ + GP_STYLE_MODE_BOX = 2, /* rectangles */ +} eMaterialGPencilStyle_Mode; + typedef struct Material { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -107,6 +160,9 @@ typedef struct Material { /* Runtime cache for GLSL materials. */ ListBase gpumaterial; + + /* grease pencil color */ + struct MaterialGPencilStyle *gp_style; } Material; /* **************** MATERIAL ********************* */ @@ -229,4 +285,24 @@ enum { MA_BS_HASHED, }; +/* Grease Pencil Stroke styles */ +enum { + GP_STYLE_STROKE_STYLE_SOLID = 0, + GP_STYLE_STROKE_STYLE_TEXTURE +}; + +/* Grease Pencil Fill styles */ +enum { + GP_STYLE_FILL_STYLE_SOLID = 0, + GP_STYLE_FILL_STYLE_GRADIENT, + GP_STYLE_FILL_STYLE_CHESSBOARD, + GP_STYLE_FILL_STYLE_TEXTURE +}; + +/* Grease Pencil Gradient Types */ +enum { + GP_STYLE_GRADIENT_LINEAR = 0, + GP_STYLE_GRADIENT_RADIAL +}; + #endif diff --git a/source/blender/makesdna/DNA_object_enums.h b/source/blender/makesdna/DNA_object_enums.h index 802ca6c7d0d..01228376174 100644 --- a/source/blender/makesdna/DNA_object_enums.h +++ b/source/blender/makesdna/DNA_object_enums.h @@ -37,7 +37,10 @@ typedef enum eObjectMode { OB_MODE_TEXTURE_PAINT = 1 << 4, OB_MODE_PARTICLE_EDIT = 1 << 5, OB_MODE_POSE = 1 << 6, - OB_MODE_GPENCIL = 1 << 7, /* NOTE: Just a dummy to make the UI nicer */ + OB_MODE_GPENCIL_EDIT = 1 << 7, + OB_MODE_GPENCIL_PAINT = 1 << 8, + OB_MODE_GPENCIL_SCULPT = 1 << 9, + OB_MODE_GPENCIL_WEIGHT = 1 << 10, } eObjectMode; /* Any mode where the brush system is used. */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index a8d50543e80..47fb2feb7f4 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -180,7 +180,9 @@ typedef struct Object { ListBase effect DNA_DEPRECATED; // XXX deprecated... keep for readfile ListBase defbase; /* list of bDeformGroup (vertex groups) names and flag only */ ListBase modifiers; /* list of ModifierData structures */ + ListBase greasepencil_modifiers; /* list of GpencilModifierData structures */ ListBase fmaps; /* list of facemaps */ + ListBase shader_fx; /* list of viewport effects. Actually only used by grease pencil */ int mode; /* Local object mode */ int restore_mode; @@ -351,6 +353,9 @@ enum { /* 23 and 24 are for life and sector (old file compat.) */ OB_ARMATURE = 25, +/* Grease Pencil object used in 3D view but not used for annotation in 2D */ + OB_GPENCIL = 26, + OB_TYPE_MAX, }; @@ -361,9 +366,9 @@ enum { /* check if the object type supports materials */ #define OB_TYPE_SUPPORT_MATERIAL(_type) \ - ((_type) >= OB_MESH && (_type) <= OB_MBALL) + (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) == OB_GPENCIL)) #define OB_TYPE_SUPPORT_VGROUP(_type) \ - (ELEM(_type, OB_MESH, OB_LATTICE)) + (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) #define OB_TYPE_SUPPORT_PARVERT(_type) \ @@ -375,10 +380,10 @@ enum { /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ - (ELEM(_id_type, ID_ME, ID_CU, ID_MB, ID_LA, ID_SPK, ID_LP, ID_CA, ID_LT, ID_AR)) + (ELEM(_id_type, ID_ME, ID_CU, ID_MB, ID_LA, ID_SPK, ID_LP, ID_CA, ID_LT, ID_GD, ID_AR)) #define OB_DATA_SUPPORT_ID_CASE \ - ID_ME: case ID_CU: case ID_MB: case ID_LA: case ID_SPK: case ID_LP: case ID_CA: case ID_LT: case ID_AR + ID_ME: case ID_CU: case ID_MB: case ID_LA: case ID_SPK: case ID_LP: case ID_CA: case ID_LT: case ID_GD: case ID_AR /* partype: first 4 bits: type */ enum { @@ -466,6 +471,12 @@ enum { OB_EMPTY_IMAGE = 8, }; +/* gpencil add types */ +enum { + GP_EMPTY = 0, + GP_MONKEY = 1 +}; + /* boundtype */ enum { OB_BOUND_BOX = 0, diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a4235a07ed5..6629eeae3fa 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -66,7 +66,6 @@ struct AnimData; struct Editing; struct SceneStats; struct bGPdata; -struct bGPDbrush; struct MovieClip; struct ColorSpace; struct SceneCollection; @@ -686,7 +685,8 @@ typedef struct RenderData { /* render simplify */ short simplify_subsurf; short simplify_subsurf_render; - short pad9, pad10; + short simplify_gpencil; + short pad10; float simplify_particles; float simplify_particles_render; @@ -905,6 +905,11 @@ typedef struct UvSculpt { Paint paint; } UvSculpt; +/* grease pencil drawing brushes */ +typedef struct GpPaint { + Paint paint; +} GpPaint; + /* ------------------------------------------- */ /* Vertex Paint */ @@ -929,15 +934,18 @@ enum { typedef enum eGP_EditBrush_Types { GP_EDITBRUSH_TYPE_SMOOTH = 0, GP_EDITBRUSH_TYPE_THICKNESS = 1, - GP_EDITBRUSH_TYPE_GRAB = 2, - GP_EDITBRUSH_TYPE_PUSH = 3, - GP_EDITBRUSH_TYPE_TWIST = 4, - GP_EDITBRUSH_TYPE_PINCH = 5, - GP_EDITBRUSH_TYPE_RANDOMIZE = 6, - GP_EDITBRUSH_TYPE_SUBDIVIDE = 7, - GP_EDITBRUSH_TYPE_SIMPLIFY = 8, - GP_EDITBRUSH_TYPE_CLONE = 9, - GP_EDITBRUSH_TYPE_STRENGTH = 10, + GP_EDITBRUSH_TYPE_STRENGTH = 2, + GP_EDITBRUSH_TYPE_GRAB = 3, + GP_EDITBRUSH_TYPE_PUSH = 4, + GP_EDITBRUSH_TYPE_TWIST = 5, + GP_EDITBRUSH_TYPE_PINCH = 6, + GP_EDITBRUSH_TYPE_RANDOMIZE = 7, + GP_EDITBRUSH_TYPE_CLONE = 8, + GP_EDITBRUSH_TYPE_SUBDIVIDE = 9, + GP_EDITBRUSH_TYPE_SIMPLIFY = 10, + /* add any sculpt brush above this value */ + GP_EDITBRUSH_TYPE_WEIGHT = 11, + /* add any weight paint brush below this value. Do no mix brushes */ /* !!! Update GP_EditBrush_Data brush[###]; below !!! */ TOT_GP_EDITBRUSH_TYPES @@ -956,6 +964,8 @@ typedef struct GP_EditBrush_Data { short size; /* radius of brush */ short flag; /* eGP_EditBrush_Flag */ float strength; /* strength of effect */ + float curcolor_add[3]; /* cursor color for add */ + float curcolor_sub[3]; /* cursor color for sub */ } GP_EditBrush_Data; /* GP_EditBrush_Data.flag */ @@ -969,20 +979,31 @@ typedef enum eGP_EditBrush_Flag { GP_EDITBRUSH_FLAG_USE_FALLOFF = (1 << 2), /* smooth brush affects pressure values as well */ - GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE = (1 << 3) + GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE = (1 << 3), + /* enable screen cursor */ + GP_EDITBRUSH_FLAG_ENABLE_CURSOR = (1 << 4), + /* temporary invert action */ + GP_EDITBRUSH_FLAG_TMP_INVERT = (1 << 5), } eGP_EditBrush_Flag; /* GPencil Stroke Sculpting Settings */ typedef struct GP_BrushEdit_Settings { - GP_EditBrush_Data brush[11]; /* TOT_GP_EDITBRUSH_TYPES */ + GP_EditBrush_Data brush[12]; /* TOT_GP_EDITBRUSH_TYPES */ void *paintcursor; /* runtime */ - int brushtype; /* eGP_EditBrush_Types */ + int brushtype; /* eGP_EditBrush_Types (sculpt) */ int flag; /* eGP_BrushEdit_SettingsFlag */ int lock_axis; /* eGP_Lockaxis_Types lock drawing to one axis */ - float alpha; /* alpha factor for selection color */ + char pad1[4]; + + /* 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. + */ + int weighttype; /* eGP_EditBrush_Types (weight paint) */ + char pad[4]; + struct CurveMapping *cur_falloff; /* multiframe edit falloff effect by frame */ } GP_BrushEdit_Settings; /* GP_BrushEdit_Settings.flag */ @@ -995,6 +1016,12 @@ typedef enum eGP_BrushEdit_SettingsFlag { GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), /* apply brush to thickness */ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3), + /* apply brush to thickness */ + GP_BRUSHEDIT_FLAG_WEIGHT_MODE = (1 << 4), + /* enable falloff for multiframe editing */ + GP_BRUSHEDIT_FLAG_FRAME_FALLOFF = (1 << 5), + /* apply brush to uv data */ + GP_BRUSHEDIT_FLAG_APPLY_UV = (1 << 6), } eGP_BrushEdit_SettingsFlag; @@ -1213,6 +1240,7 @@ typedef struct ToolSettings { VPaint *wpaint; /* weight paint */ Sculpt *sculpt; UvSculpt *uvsculpt; /* uv smooth */ + GpPaint *gp_paint; /* gpencil paint */ /* Vertex group weight - used only for editmode, not weight * paint */ @@ -1236,19 +1264,19 @@ typedef struct ToolSettings { /* Auto-IK */ short autoik_chainlen; /* runtime only */ - /* SCE_MPR_LOC/SCAL */ - char gizmo_flag; - /* Grease Pencil */ char gpencil_flags; /* flags/options for how the tool works */ - char gpencil_src; /* for main 3D view Grease Pencil, where data comes from */ char gpencil_v3d_align; /* stroke placement settings: 3D View */ char gpencil_v2d_align; /* : General 2D Editor */ char gpencil_seq_align; /* : Sequencer Preview */ char gpencil_ima_align; /* : Image Editor */ - char _pad3[3]; + /* Annotations */ + char annotate_v3d_align; /* stroke placement settings - 3D View */ + + short annotate_thickness; /* default stroke thickness for annotation strokes */ + char _pad3[2]; /* Grease Pencil Sculpt */ struct GP_BrushEdit_Settings gp_sculpt; @@ -1256,10 +1284,7 @@ typedef struct ToolSettings { /* Grease Pencil Interpolation Tool(s) */ struct GP_Interpolate_Settings gp_interpolate; - /* Grease Pencil Drawing Brushes (bGPDbrush) */ - ListBase gp_brushes; - - /* Image Paint (8 byttse aligned please!) */ + /* Image Paint (8 bytes aligned please!) */ struct ImagePaintSettings imapaint; /* Particle Editing */ @@ -1281,7 +1306,9 @@ typedef struct ToolSettings { /* Alt+RMB option */ char edge_mode; char edge_mode_live_unwrap; - char _pad1; + + /* SCE_MPR_LOC/SCAL */ + char gizmo_flag; /* Transform */ char transform_pivot_point; @@ -1503,7 +1530,7 @@ typedef struct Scene { /* Units */ struct UnitSettings unit; - /* Grease Pencil */ + /* Grease Pencil - Annotations */ struct bGPdata *gpd; /* Movie Tracking */ @@ -1515,6 +1542,7 @@ typedef struct Scene { uint64_t customdata_mask; /* XXX. runtime flag for drawing, actually belongs in the window, only used by BKE_object_handle_update() */ uint64_t customdata_mask_modal; /* XXX. same as above but for temp operator use (gl renders) */ + /* Color Management */ ColorManagedViewSettings view_settings; ColorManagedDisplaySettings display_settings; @@ -2020,19 +2048,27 @@ typedef enum eImagePaintMode { /* ToolSettings.gpencil_flags */ typedef enum eGPencil_Flags { - /* "Continuous Drawing" - The drawing operator enters a mode where multiple strokes can be drawn */ - GP_TOOL_FLAG_PAINTSESSIONS_ON = (1 << 0), /* When creating new frames, the last frame gets used as the basis for the new one */ GP_TOOL_FLAG_RETAIN_LAST = (1 << 1), /* Add the strokes below all strokes in the layer */ - GP_TOOL_FLAG_PAINT_ONBACK = (1 << 2) + GP_TOOL_FLAG_PAINT_ONBACK = (1 << 2), + /* Show compact list of colors */ + GP_TOOL_FLAG_THUMBNAIL_LIST = (1 << 3), } eGPencil_Flags; -/* ToolSettings.gpencil_src */ -typedef enum eGPencil_Source_3D { - GP_TOOL_SOURCE_SCENE = 0, - GP_TOOL_SOURCE_OBJECT = 1 -} eGPencil_Source_3d; +/* scene->r.simplify_gpencil */ +typedef enum eGPencil_SimplifyFlags { + /* Simplify */ + SIMPLIFY_GPENCIL_ENABLE = (1 << 0), + /* Simplify on play */ + SIMPLIFY_GPENCIL_ON_PLAY = (1 << 1), + /* Simplify fill on viewport */ + 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) +} eGPencil_SimplifyFlags; /* ToolSettings.gpencil_*_align - Stroke Placement mode flags */ typedef enum eGPencil_Placement_Flags { @@ -2048,6 +2084,7 @@ typedef enum eGPencil_Placement_Flags { /* "Use Endpoints" */ GP_PROJECT_DEPTH_STROKE_ENDPOINTS = (1 << 4), + GP_PROJECT_CURSOR = (1 << 5), } eGPencil_Placement_Flags; /* ToolSettings.particle flag */ diff --git a/source/blender/makesdna/DNA_shader_fx_types.h b/source/blender/makesdna/DNA_shader_fx_types.h new file mode 100644 index 00000000000..15147cf2b6c --- /dev/null +++ b/source/blender/makesdna/DNA_shader_fx_types.h @@ -0,0 +1,196 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_shader_fx_types.h + * \ingroup DNA + */ + +#ifndef __DNA_SHADERFX_TYPES_H__ +#define __DNA_SHADERFX_TYPES_H__ + +#include "DNA_defs.h" +#include "DNA_listBase.h" + +struct DRWShadingGroup; + +/* WARNING ALERT! TYPEDEF VALUES ARE WRITTEN IN FILES! SO DO NOT CHANGE! + * (ONLY ADD NEW ITEMS AT THE END) + */ + +typedef enum ShaderFxType { + eShaderFxType_None = 0, + eShaderFxType_Blur = 1, + eShaderFxType_Flip = 2, + eShaderFxType_Light = 3, + eShaderFxType_Pixel = 4, + eShaderFxType_Swirl = 5, + eShaderFxType_Wave = 6, + eShaderFxType_Rim = 7, + eShaderFxType_Colorize = 8, + NUM_SHADER_FX_TYPES +} ShaderFxType; + +typedef enum ShaderFxMode { + eShaderFxMode_Realtime = (1 << 0), + eShaderFxMode_Render = (1 << 1), + eShaderFxMode_Editmode = (1 << 2), + eShaderFxMode_Expanded = (1 << 3), +} ShaderFxMode; + +typedef enum { + /* This fx has been inserted in local override, and hence can be fully edited. */ + eShaderFxFlag_StaticOverride_Local = (1 << 0), +} ShaderFxFlag; + +typedef struct ShaderFxData { + struct ShaderFxData *next, *prev; + + int type, mode; + int stackindex; + short flag; + short pad; + char name[64]; /* MAX_NAME */ + + char *error; +} ShaderFxData; + +/* Runtime temp data */ +typedef struct ShaderFxData_runtime { + struct DRWShadingGroup *fx_sh; + struct DRWShadingGroup *fx_sh_b; + struct DRWShadingGroup *fx_sh_c; +} ShaderFxData_runtime; + +typedef struct BlurShaderFxData { + ShaderFxData shaderfx; + int radius[2]; + int flag; /* flags */ + int samples; /* number of samples */ + float coc; /* circle of confusion */ + int blur[2]; /* not visible in rna */ + char pad[4]; + ShaderFxData_runtime runtime; +} BlurShaderFxData; + +typedef enum eBlurShaderFx_Flag { + FX_BLUR_DOF_MODE = (1 << 0) +} eBlurShaderFx_Flag; + +typedef struct ColorizeShaderFxData { + ShaderFxData shaderfx; + int mode; + float low_color[4]; + float high_color[4]; + float factor; + int flag; /* flags */ + char pad[4]; + ShaderFxData_runtime runtime; +} ColorizeShaderFxData; + +typedef enum ColorizeShaderFxModes { + eShaderFxColorizeMode_GrayScale = 0, + eShaderFxColorizeMode_Sepia = 1, + eShaderFxColorizeMode_BiTone = 2, + eShaderFxColorizeMode_Custom = 3, + eShaderFxColorizeMode_Transparent = 4, +} ColorizeShaderFxModes; + +typedef struct FlipShaderFxData { + ShaderFxData shaderfx; + int flag; /* flags */ + int flipmode; /* internal, not visible in rna */ + ShaderFxData_runtime runtime; +} FlipShaderFxData; + +typedef enum eFlipShaderFx_Flag { + FX_FLIP_HORIZONTAL = (1 << 0), + FX_FLIP_VERTICAL = (1 << 1), +} eFlipShaderFx_Flag; + +typedef struct LightShaderFxData { + ShaderFxData shaderfx; + struct Object *object; + int flag; /* flags */ + float energy; + float ambient; + float loc[4]; /* internal, not visible in rna */ + char pad[4]; + ShaderFxData_runtime runtime; +} LightShaderFxData; + +typedef struct PixelShaderFxData { + ShaderFxData shaderfx; + int size[3]; /* last element used for shader only */ + int flag; /* flags */ + float rgba[4]; + ShaderFxData_runtime runtime; +} PixelShaderFxData; + +typedef enum ePixelShaderFx_Flag { + FX_PIXEL_USE_LINES = (1 << 0), +} ePixelShaderFx_Flag; + +typedef struct RimShaderFxData { + ShaderFxData shaderfx; + int offset[2]; + int flag; /* flags */ + float rim_rgb[3]; + float mask_rgb[3]; + int mode; + int blur[2]; + int samples; + char pad[4]; + ShaderFxData_runtime runtime; +} RimShaderFxData; + +typedef enum RimShaderFxModes { + eShaderFxRimMode_Normal = 0, + eShaderFxRimMode_Overlay = 1, + eShaderFxRimMode_Add = 2, + eShaderFxRimMode_Subtract = 3, + eShaderFxRimMode_Multiply = 4, + eShaderFxRimMode_Divide = 5, +} RimShaderFxModes; + +typedef struct SwirlShaderFxData { + ShaderFxData shaderfx; + struct Object *object; + int flag; /* flags */ + int radius; + float angle; + int transparent; /* not visible in rna */ + ShaderFxData_runtime runtime; +} SwirlShaderFxData; + +typedef enum eSwirlShaderFx_Flag { + FX_SWIRL_MAKE_TRANSPARENT = (1 << 0), +} eSwirlShaderFx_Flag; + +typedef struct WaveShaderFxData { + ShaderFxData shaderfx; + float amplitude; + float period; + float phase; + int orientation; + int flag; /* flags */ + char pad[4]; + ShaderFxData_runtime runtime; +} WaveShaderFxData; +#endif /* __DNA_SHADERFX_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index d6d043b03ae..5404f4160fd 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -198,6 +198,7 @@ typedef enum eSpaceButtons_Context { BCONTEXT_VIEW_LAYER = 13, BCONTEXT_TOOL = 14, BCONTEXT_WORKSPACE = 15, + BCONTEXT_SHADERFX = 16, /* always as last... */ BCONTEXT_TOT @@ -1346,7 +1347,7 @@ typedef enum eSpaceClip_Flag { SC_SHOW_GRID = (1 << 9), SC_SHOW_STABLE = (1 << 10), SC_MANUAL_CALIBRATION = (1 << 11), - SC_SHOW_GPENCIL = (1 << 12), + SC_SHOW_ANNOTATION = (1 << 12), SC_SHOW_FILTERS = (1 << 13), SC_SHOW_GRAPH_FRAMES = (1 << 14), SC_SHOW_GRAPH_TRACKS_MOTION = (1 << 15), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e7a540f9afc..cb2c69e2fa1 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -651,7 +651,9 @@ typedef struct UserDef { struct WalkNavigation walk_navigation; short opensubdiv_compute_type; - char pad5[6]; + short gpencil_multisamples; /* eMultiSample_Type, amount of samples for Grease Pencil */ + + char pad5[4]; } UserDef; extern UserDef U; /* from blenkernel blender.c */ @@ -958,7 +960,7 @@ typedef enum eNdof_Flag { #define NDOF_PIXELS_PER_SECOND 600.0f -/* UserDef.ogl_multisamples */ +/* UserDef.ogl_multisamples and gpencil_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 bbbaf8bb957..27b07a317ed 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -190,6 +190,14 @@ typedef struct View3DOverlay { float wireframe_threshold; char _pad0[4]; + /* grease pencil setttings */ + float gpencil_grid_scale; + float gpencil_paper_opacity; + int gpencil_grid_lines; + int gpencil_grid_axis; + float gpencil_grid_opacity; + char _pad1[4]; + } View3DOverlay; /* 3D ViewPort Struct */ @@ -256,7 +264,8 @@ typedef struct View3D { char multiview_eye; /* multiview current eye - for internal use */ - char pad3[4]; + /* actually only used to define the opacity of the grease pencil vertex in edit mode */ + float vertex_opacity; /* note, 'fx_settings.dof' is currently _not_ allocated, * instead set (temporarily) from camera */ @@ -340,7 +349,7 @@ typedef struct View3D { /* View3d->flag2 (short) */ #define V3D_RENDER_OVERRIDE (1 << 2) #define V3D_SOLID_TEX (1 << 3) -#define V3D_SHOW_GPENCIL (1 << 4) +#define V3D_SHOW_ANNOTATION (1 << 4) #define V3D_LOCK_CAMERA (1 << 5) #define V3D_RENDER_SHADOW (1 << 6) /* This is a runtime only flag that's used to tell draw_mesh_object() that we're doing a shadow pass instead of a regular draw */ #define V3D_SHOW_RECONSTRUCTION (1 << 7) @@ -353,9 +362,13 @@ typedef struct View3D { #define V3D_OCCLUDE_WIRE (1 << 14) #define V3D_SHOW_MODE_SHADE_OVERRIDE (1 << 15) /* XXX: DNA deprecated */ - /* View3d->flag3 (short) */ -#define V3D_SHOW_WORLD (1 << 0) /* LEGACY replaced by V3D_SHADING_BACKGROUND_WORLD */ +#define V3D_SHOW_WORLD (1 << 0) /* LEGACY replaced by V3D_SHADING_BACKGROUND_WORLD */ +#define V3D_GP_SHOW_PAPER (1 << 2) /* Activate paper to cover all viewport */ +#define V3D_GP_SHOW_GRID (1 << 3) /* Activate paper grid */ +#define V3D_GP_SHOW_EDIT_LINES (1 << 4) +#define V3D_GP_SHOW_MULTIEDIT_LINES (1 << 5) +#define V3D_GP_SHOW_ONION_SKIN (1 << 6) /* main switch at view level */ /* View3DShading->light */ enum { @@ -482,4 +495,12 @@ enum { #define RV3D_CAMZOOM_MIN_FACTOR 0.1657359312880714853f #define RV3D_CAMZOOM_MAX_FACTOR 44.9852813742385702928f +/* View3d.gpencil_grid_axis */ +enum { + V3D_GP_GRID_AXIS_LOCK = (1 << 0), + V3D_GP_GRID_AXIS_X = (1 << 1), + V3D_GP_GRID_AXIS_Y = (1 << 2), + V3D_GP_GRID_AXIS_Z = (1 << 3), +}; + #endif diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index a1bfac66115..7b27ec05865 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -113,6 +113,8 @@ static const char *includefiles[] = { "DNA_particle_types.h", "DNA_cloth_types.h", "DNA_gpencil_types.h", + "DNA_gpencil_modifier_types.h", + "DNA_shader_fx_types.h", "DNA_windowmanager_types.h", "DNA_anim_types.h", "DNA_boid_types.h", @@ -1337,6 +1339,8 @@ int main(int argc, char **argv) #include "DNA_particle_types.h" #include "DNA_cloth_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_windowmanager_types.h" #include "DNA_anim_types.h" #include "DNA_boid_types.h" diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index cb5e2e61f9a..16194c9b419 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -269,9 +269,6 @@ extern StructRNA RNA_FreestyleSettings; extern StructRNA RNA_Function; extern StructRNA RNA_GPencilFrame; extern StructRNA RNA_GPencilLayer; -extern StructRNA RNA_GPencilPalette; -extern StructRNA RNA_GPencilPaletteColor; -extern StructRNA RNA_GPencilBrush; extern StructRNA RNA_GPencilInterpolateSettings; extern StructRNA RNA_GPencilStroke; extern StructRNA RNA_GPencilStrokePoint; @@ -600,6 +597,31 @@ extern StructRNA RNA_SunLight; extern StructRNA RNA_SurfaceCurve; extern StructRNA RNA_SurfaceDeformModifier; extern StructRNA RNA_SurfaceModifier; +extern StructRNA RNA_GpencilModifier; +extern StructRNA RNA_BuildGpencilModifier; +extern StructRNA RNA_NoiseGpencilModifier; +extern StructRNA RNA_SubdivGpencilModifier; +extern StructRNA RNA_SimplifyGpencilModifier; +extern StructRNA RNA_ThickGpencilModifier; +extern StructRNA RNA_TintGpencilModifier; +extern StructRNA RNA_ColorGpencilModifier; +extern StructRNA RNA_InstanceGpencilModifier; +extern StructRNA RNA_DupliGpencilModifier; +extern StructRNA RNA_OpacityGpencilModifier; +extern StructRNA RNA_LatticeGpencilModifier; +extern StructRNA RNA_MirrorGpencilModifier; +extern StructRNA RNA_SmoothGpencilModifier; +extern StructRNA RNA_HookGpencilModifier; +extern StructRNA RNA_OffsetGpencilModifier; +extern StructRNA RNA_ShaderFx; +extern StructRNA RNA_ShaderFxBlur; +extern StructRNA RNA_ShaderFxColorize; +extern StructRNA RNA_ShaderFxFlip; +extern StructRNA RNA_ShaderFxLight; +extern StructRNA RNA_ShaderFxPixel; +extern StructRNA RNA_ShaderFxRim; +extern StructRNA RNA_ShaderFxSwirl; +extern StructRNA RNA_ShaderFxWave; extern StructRNA RNA_TexMapping; extern StructRNA RNA_Text; extern StructRNA RNA_TextBox; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 043375a066a..4c0861757f4 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -44,6 +44,7 @@ extern const EnumPropertyItem rna_enum_id_type_items[]; extern const EnumPropertyItem rna_enum_object_mode_items[]; extern const EnumPropertyItem rna_enum_object_empty_drawtype_items[]; +extern const EnumPropertyItem rna_enum_object_gpencil_type_items[]; extern const EnumPropertyItem rna_enum_metaelem_type_items[]; extern const EnumPropertyItem rna_enum_proportional_falloff_items[]; @@ -65,6 +66,8 @@ extern const EnumPropertyItem rna_enum_object_modifier_type_items[]; extern const EnumPropertyItem rna_enum_constraint_type_items[]; extern const EnumPropertyItem rna_enum_boidrule_type_items[]; extern const EnumPropertyItem rna_enum_sequence_modifier_type_items[]; +extern const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[]; +extern const EnumPropertyItem rna_enum_object_shaderfx_type_items[]; extern const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[]; extern const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[]; @@ -228,6 +231,7 @@ const EnumPropertyItem *rna_node_socket_type_itemf( struct bContext; struct PointerRNA; struct PropertyRNA; + const EnumPropertyItem *rna_TransformOrientation_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); const EnumPropertyItem *rna_Sensor_type_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); const EnumPropertyItem *rna_Actuator_type_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index eb32a5fc6cb..ec240c222a1 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -49,6 +49,8 @@ set(DEFSRC rna_fcurve.c rna_fluidsim.c rna_gpencil.c + rna_gpencil_modifier.c + rna_shader_fx.c rna_group.c rna_image.c rna_key.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 393ebc15d3e..b0713987e16 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3416,6 +3416,8 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh}, {"rna_meta.c", "rna_meta_api.c", RNA_def_meta}, {"rna_modifier.c", NULL, RNA_def_modifier}, + {"rna_gpencil_modifier.c", NULL, RNA_def_greasepencil_modifier}, + {"rna_shader_fx.c", NULL, RNA_def_shader_fx }, {"rna_nla.c", NULL, RNA_def_nla}, {"rna_nodetree.c", NULL, RNA_def_nodetree}, {"rna_object.c", "rna_object_api.c", RNA_def_object}, diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 4f1e29b482d..0f3e74c567f 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -31,6 +31,8 @@ #include "DNA_texture_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_workspace_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" @@ -122,6 +124,44 @@ const EnumPropertyItem rna_enum_brush_image_tool_items[] = { {0, NULL, 0, NULL, NULL} }; +#ifndef RNA_RUNTIME +static EnumPropertyItem rna_enum_gpencil_brush_types_items[] = { + { GP_BRUSH_TYPE_DRAW, "DRAW", ICON_GREASEPENCIL_STROKE_PAINT, "Draw", "The brush is of type used for drawing strokes" }, + { GP_BRUSH_TYPE_FILL, "FILL", ICON_COLOR, "Fill", "The brush is of type used for filling areas" }, + { GP_BRUSH_TYPE_ERASE, "ERASE", ICON_PANEL_CLOSE, "Erase", "The brush is used for erasing strokes" }, + { 0, NULL, 0, NULL, NULL } +}; + +static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = { + { GP_BRUSH_ERASER_SOFT, "SOFT", 0, "Soft", "Use soft eraser" }, + { GP_BRUSH_ERASER_HARD, "HARD", 0, "Hard", "Use hard eraser" }, + { GP_BRUSH_ERASER_STROKE, "STROKE", 0, "Stroke", "Use stroke eraser" }, + { 0, NULL, 0, NULL, NULL } +}; + +static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = { + { GP_FILL_DMODE_STROKE, "STROKE", 0, "Strokes", "Use visible strokes as fill boundary limits" }, + { GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Control", "Use internal control lines as fill boundary limits" }, + { GP_FILL_DMODE_BOTH, "BOTH", 0, "Both", "Use visible strokes and control lines as fill boundary limits" }, + { 0, NULL, 0, NULL, NULL } +}; + +static EnumPropertyItem rna_enum_gpencil_brush_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", "" }, + { GP_BRUSH_ICON_INKNOISE, "INKNOISE", ICON_GPBRUSH_INKNOISE, "Ink Noise", "" }, + { GP_BRUSH_ICON_BLOCK, "BLOCK", ICON_GPBRUSH_BLOCK, "Block", "" }, + { GP_BRUSH_ICON_MARKER, "MARKER", ICON_GPBRUSH_MARKER, "Marker", "" }, + { GP_BRUSH_ICON_FILL, "FILL", ICON_GPBRUSH_FILL, "Fill", "" }, + { GP_BRUSH_ICON_ERASE_SOFT, "SOFT", ICON_GPBRUSH_ERASE_SOFT, "Eraser Soft", "" }, + { GP_BRUSH_ICON_ERASE_HARD, "HARD", ICON_GPBRUSH_ERASE_HARD, "Eraser Hard", "" }, + { GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", "" }, + { 0, NULL, 0, NULL, NULL } +}; +#endif + + #ifdef RNA_RUNTIME #include "MEM_guardedalloc.h" @@ -442,6 +482,33 @@ static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); } +static const EnumPropertyItem *rna_DynamicGpencil_type_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + Main *bmain = CTX_data_main(C); + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + Brush *brush; + for (brush = bmain->brush.first; brush; brush = brush->id.next, i++) { + if (brush->gpencil_settings == NULL) + continue; + + item_tmp.identifier = brush->id.name + 2; + item_tmp.name = brush->id.name + 2; + item_tmp.value = i; + item_tmp.icon = brush->gpencil_settings->icon_id; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + static void rna_TextureSlot_brush_angle_update(bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); @@ -615,6 +682,58 @@ static const EnumPropertyItem *rna_Brush_stroke_itemf(bContext *C, PointerRNA *U return brush_stroke_method_items; } } + +/* Grease Pencil Drawing Brushes Settings */ +static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) +{ + ToolSettings *ts = scene->toolsettings; + Paint *paint = &ts->gp_paint->paint; + Brush *brush_cur = paint->brush; + + /* disable default eraser in all brushes */ + for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { + if ((brush != brush_cur) && + (brush->ob_mode == OB_MODE_GPENCIL_PAINT) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) + { + brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER; + } + } +} + +static void rna_BrushGpencilSettings_eraser_mode_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +{ + ToolSettings *ts = scene->toolsettings; + Paint *paint = &ts->gp_paint->paint; + Brush *brush = paint->brush; + + /* set eraser icon */ + if ((brush) && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) { + switch (brush->gpencil_settings->eraser_mode) { + case GP_BRUSH_ERASER_SOFT: + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + break; + case GP_BRUSH_ERASER_HARD: + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD; + break; + case GP_BRUSH_ERASER_STROKE: + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE; + break; + default: + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + break; + } + } +} + +static bool rna_BrushGpencilSettings_material_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + Material *ma = (Material *)value.data; + + /* GP materials only */ + return (ma->gp_style != NULL); +} + #else static void rna_def_brush_texture_slot(BlenderRNA *brna) @@ -809,6 +928,316 @@ static void rna_def_image_paint_capabilities(BlenderRNA *brna) #undef IMAPAINT_TOOL_CAPABILITY } +static void rna_def_gpencil_options(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* Grease Pencil Drawing - generated dynamically */ + static const EnumPropertyItem prop_dynamic_gpencil_type[] = { + { 1, "DRAW", 0, "Draw", "" }, + { 0, NULL, 0, NULL, NULL } + }; + + srna = RNA_def_struct(brna, "BrushGpencilSettings", NULL); + RNA_def_struct_sdna(srna, "BrushGpencilSettings"); + RNA_def_struct_ui_text(srna, "Grease Pencil Brush Settings", "Settings for grease pencil brush"); + + /* grease pencil drawing brushes */ + prop = RNA_def_property(srna, "grease_pencil_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "brush_type"); + RNA_def_property_enum_items(prop, prop_dynamic_gpencil_type); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_DynamicGpencil_type_itemf"); + RNA_def_property_ui_text(prop, "Grease Pencil Tool", ""); + /* TODO: GPXX review update */ + RNA_def_property_update(prop, 0, NULL); + //RNA_def_property_update(prop, 0, "rna_Brush_gpencil_tool_update"); + + /* 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength", "Color strength for new strokes (affect alpha factor of color)"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Jitter factor for new strokes */ + prop = RNA_def_property(srna, "pen_jitter", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_jitter"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for pressure */ + prop = RNA_def_property(srna, "random_pressure", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_press"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Pressure Randomness", "Randomness factor for pressure in new strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for strength */ + prop = RNA_def_property(srna, "random_strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength Randomness", "Randomness factor strength in new strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for subdivision */ + prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_NONE); + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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"); + RNA_def_property_range(prop, -M_PI_2, M_PI_2); + RNA_def_property_ui_text(prop, "Angle", + "Direction of the stroke at which brush gives maximal thickness " + "(0° for horizontal)"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Factor to change brush size depending of angle */ + prop = RNA_def_property(srna, "angle_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_angle_factor"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Angle Factor", + "Reduce brush thickness by this factor when stroke is perpendicular to 'Angle' direction"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Smoothing factor for new strokes */ + prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); + RNA_def_property_range(prop, 0.0, 2.0f); + RNA_def_property_ui_text(prop, "Smooth", + "Amount of smoothing to apply after finish newly created strokes, to reduce jitter/noise"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Iterations of the Smoothing factor */ + prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); + RNA_def_property_range(prop, 1, 3); + RNA_def_property_ui_text(prop, "Iterations", + "Number of times to smooth newly created strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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"); + RNA_def_property_range(prop, 0, 3); + RNA_def_property_ui_text(prop, "Subdivision Steps", + "Number of times to subdivide newly created strokes, for less jagged strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Curves for pressure */ + prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_sensitivity"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Sensitivity", "Curve used for the sensitivity"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_strength", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_strength"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Strength", "Curve used for the strength"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_jitter", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curve_jitter"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Jitter", "Curve used for the jitter effect"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* fill threshold for transparence */ + prop = RNA_def_property(srna, "gpencil_fill_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fill_threshold"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Threshold", + "Threshold to consider color transparent for filling"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* fill leak size */ + prop = RNA_def_property(srna, "gpencil_fill_leak", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "fill_leak"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Leak Size", + "Size in pixels to consider the leak closed"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* fill simplify steps */ + prop = RNA_def_property(srna, "gpencil_fill_simplyfy_level", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "fill_simplylvl"); + RNA_def_property_range(prop, 0, 10); + RNA_def_property_ui_text(prop, "Simplify", + "Number of simplify steps (large values reduce fill accuracy)"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "uv_random", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "uv_random"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "UV Random", "Random factor for autogenerated UV rotation"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "input_samples"); + RNA_def_property_range(prop, 0, GP_MAX_INPUT_SAMPLES); + RNA_def_property_ui_text(prop, "Input Samples", "Generate intermediate points for very fast mouse movements. Set to 0 to disable"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* active smooth factor while drawing */ + prop = RNA_def_property(srna, "active_smooth_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "active_smooth"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Active Smooth", + "Amount of smoothing while drawing "); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* brush standard icon */ + prop = RNA_def_property(srna, "gp_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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_ui_text(prop, "Grease Pencil Icon", ""); + + /* Flags */ + prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure", "Use tablet pressure"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_strength_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_STENGTH_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Strength", "Use tablet pressure for color strength"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_jitter_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_JITTER_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Jitter", "Use tablet pressure for jitter"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_stabilizer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_STABILIZE_MOUSE); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Stabilizer", + "Draw lines with a delay to allow smooth strokes. Press Shift key to override while drawing"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + prop = RNA_def_property(srna, "gpencil_brush_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "brush_type"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_types_items); + RNA_def_property_ui_text(prop, "Type", "Category of the brush"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + 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); + RNA_def_property_ui_text(prop, "Mode", "Eraser Mode"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update"); + + prop = RNA_def_property(srna, "gpencil_fill_draw_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "fill_draw_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_draw_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Mode to draw boundary limits"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + /* Material */ + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_BrushGpencilSettings_material_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_ui_text(prop, "Material", "Material used for strokes drawn using this brush"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "gpencil_fill_show_boundary", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_FILL_SHOW_HELPLINES); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Show Lines", "Show help lines for filling to see boundaries"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + prop = RNA_def_property(srna, "gpencil_fill_hide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_FILL_HIDE); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Hide", "Hide transparent lines to use as boundary for filling"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + prop = RNA_def_property(srna, "default_eraser", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_DEFAULT_ERASER); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1); + RNA_def_property_ui_text(prop, "Default Eraser", "Use this brush when enable eraser with fast switch key"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_default_eraser_update"); + + prop = RNA_def_property(srna, "enable_settings", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_GROUP_SETTINGS); + RNA_def_property_ui_text(prop, "Settings", "Enable additional post processing options for new strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + prop = RNA_def_property(srna, "enable_random", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_GROUP_RANDOM); + RNA_def_property_ui_text(prop, "Random Settings", "Enable random settings for brush"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); +} + static void rna_def_brush(BlenderRNA *brna) { StructRNA *srna; @@ -1373,6 +1802,10 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_TEXTURE_PAINT); RNA_def_property_ui_text(prop, "Use Texture", "Use this brush in texture paint mode"); + prop = RNA_def_property(srna, "use_paint_grease_pencil", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_GPENCIL_PAINT); + RNA_def_property_ui_text(prop, "Use Sculpt", "Use this brush in grease pencil drawing mode"); + /* texture */ prop = RNA_def_property(srna, "texture_slot", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "BrushTextureSlot"); @@ -1475,6 +1908,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_struct_type(prop, "ImapaintToolCapabilities"); RNA_def_property_pointer_funcs(prop, "rna_Imapaint_tool_capabilities_get", NULL, NULL, NULL); RNA_def_property_ui_text(prop, "Image Painting Capabilities", "Brush's capabilities in image paint mode"); + + prop = RNA_def_property(srna, "gpencil_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "BrushGpencilSettings"); + RNA_def_property_pointer_sdna(prop, NULL, "gpencil_settings"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Gpencil Settings", ""); + } @@ -1543,6 +1983,7 @@ void RNA_def_brush(BlenderRNA *brna) rna_def_brush_capabilities(brna); rna_def_sculpt_capabilities(brna); rna_def_image_paint_capabilities(brna); + rna_def_gpencil_options(brna); rna_def_brush_texture_slot(brna); rna_def_operator_stroke_element(brna); } diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index 7b07faf8ee7..781be07f1dc 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -52,6 +52,10 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { {CTX_MODE_PAINT_TEXTURE, "PAINT_TEXTURE", 0, "Texture Paint", ""}, {CTX_MODE_PARTICLE, "PARTICLE", 0, "Particle", ""}, {CTX_MODE_OBJECT, "OBJECT", 0, "Object", ""}, + {CTX_MODE_GPENCIL_PAINT, "GPENCIL_PAINT", 0, "Grease Pencil Paint", "" }, + {CTX_MODE_GPENCIL_EDIT, "GPENCIL_EDIT", 0, "Grease Pencil Edit", "" }, + {CTX_MODE_GPENCIL_SCULPT, "GPENCIL_SCULPT", 0, "Grease Pencil Sculpt", "" }, + {CTX_MODE_GPENCIL_WEIGHT, "GPENCIL_WEIGHT", 0, "Grease Pencil Weight 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 d9dba4679e0..f84f31b02f1 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -26,8 +26,12 @@ #include +#include "BLI_math.h" + +#include "DNA_meshdata_types.h" #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" +#include "DNA_brush_types.h" #include "MEM_guardedalloc.h" @@ -38,10 +42,12 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "rna_internal.h" #include "WM_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "ED_gpencil.h" @@ -53,31 +59,54 @@ static const EnumPropertyItem parent_type_items[] = { {0, NULL, 0, NULL, NULL} }; +#ifndef RNA_RUNTIME +static EnumPropertyItem rna_enum_gpencil_xraymodes_items[] = { + { GP_XRAY_FRONT, "FRONT", 0, "Front", "Draw all strokes in front" }, + { GP_XRAY_3DSPACE, "3DSPACE", 0, "3DSpace", "Draw strokes relative to other objects in 3D space" }, + { GP_XRAY_BACK, "BACK", 0, "Back", "Draw all strokes on back" }, + { 0, NULL, 0, NULL, NULL } +}; + +static EnumPropertyItem rna_enum_gpencil_onion_modes_items[] = { + { GP_ONION_MODE_ABSOLUTE, "ABSOLUTE", 0, "Frames", "Frames in absolute range of scene frame number" }, + { GP_ONION_MODE_RELATIVE, "RELATIVE", 0, "Keyframes", "Frames in relative range of grease pencil keyframes" }, + { GP_ONION_MODE_SELECTED, "SELECTED", 0, "Selected", "Only Selected Frames" }, + { 0, NULL, 0, NULL, NULL } +}; +#endif #ifdef RNA_RUNTIME #include "BLI_math.h" +#include "BLI_ghash.h" #include "WM_api.h" +#include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_gpencil.h" -#include "BKE_action.h" +#include "BKE_icons.h" + +#include "DEG_depsgraph.h" -static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { + DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } -static void rna_GPencil_editmode_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +static void rna_GPencil_editmode_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { + bGPdata *gpd = (bGPdata *)ptr->id.data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + /* Notify all places where GPencil data lives that the editing state is different */ WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); WM_main_add_notifier(NC_SCENE | ND_MODE | NC_MOVIECLIP, NULL); } -static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, PointerRNA *ptr) +static void UNUSED_FUNCTION(rna_GPencil_onion_skinning_update)(Main *bmain, Scene *scene, PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->id.data; bGPDlayer *gpl; @@ -87,7 +116,7 @@ static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, Pointer * stays in sync with the status of the actual layers */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->flag & GP_LAYER_ONIONSKIN) { + if (gpl->onion_flag & GP_LAYER_ONIONSKIN) { enabled = true; } } @@ -102,16 +131,22 @@ static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, Pointer rna_GPencil_update(bmain, scene, ptr); } -static void rna_GPencil_stroke_colorname_update(Main *bmain, Scene *scene, PointerRNA *ptr) -{ - bGPDstroke *gps = (bGPDstroke *)ptr->data; - gps->flag |= GP_STROKE_RECALC_COLOR; - gps->palcolor = NULL; - /* Now do standard updates... */ - rna_GPencil_update(bmain, scene, ptr); +/* Poll Callback to filter GP Datablocks to only show those for Annotations */ +bool rna_GPencil_datablocks_annotations_poll(PointerRNA *UNUSED(ptr), const PointerRNA value) +{ + bGPdata *gpd = value.data; + return (gpd->flag & GP_DATA_ANNOTATIONS) != 0; } +/* Poll Callback to filter GP Datablocks to only show those for GP Objects */ +bool rna_GPencil_datablocks_obdata_poll(PointerRNA *UNUSED(ptr), const PointerRNA value) +{ + bGPdata *gpd = value.data; + return (gpd->flag & GP_DATA_ANNOTATIONS) == 0; +} + + static char *rna_GPencilLayer_path(PointerRNA *ptr) { bGPDlayer *gpl = (bGPDlayer *)ptr->data; @@ -133,36 +168,6 @@ static int rna_GPencilLayer_active_frame_editable(PointerRNA *ptr, const char ** return PROP_EDITABLE; } -static void rna_GPencilLayer_line_width_range(PointerRNA *ptr, int *min, int *max, - int *softmin, int *softmax) -{ - bGPDlayer *gpl = ptr->data; - - /* The restrictions on max width here are due to OpenGL on Windows not supporting - * any widths greater than 10 (for driver-drawn) strokes/points. - * - * Although most of our 2D strokes also don't suffer from this restriction, - * it's relatively hard to test for that. So, for now, only volumetric strokes - * get to be larger... - */ - - /* From GP v2 this value is used to increase or decrease the thickness of the stroke */ - if (gpl->flag & GP_LAYER_VOLUMETRIC) { - *min = -300; - *max = 300; - - *softmin = -100; - *softmax = 100; - } - else { - *min = -10; - *max = 10; - - *softmin = -10; - *softmax = 10; - } -} - /* set parent */ static void set_parent(bGPDlayer *gpl, Object *par, const int type, const char *substr) { @@ -202,21 +207,6 @@ static void rna_GPencilLayer_parent_set(PointerRNA *ptr, PointerRNA value) set_parent(gpl, par, gpl->partype, gpl->parsubstr); } else { - /* keep strokes in the same place, so apply current transformation */ - if (gpl->parent != NULL) { - bGPDspoint *pt; - int i; - float diff_mat[4][4]; - /* calculate difference matrix */ - ED_gpencil_parent_location(gpl, diff_mat); - for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - mul_m4_v3(diff_mat, &pt->x); - } - } - } - } /* clear parent */ gpl->parent = NULL; } @@ -306,6 +296,15 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, PointerRNA value) { bGPdata *gpd = ptr->id.data; + /* Don't allow setting active layer to NULL if layers exist + * as this breaks various tools. Tools should be used instead + * if it's necessary to remove layers + */ + if (value.data == NULL) { + printf("%s: Setting active layer to None is not allowed\n", __func__); + return; + } + if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ bGPDlayer *gl; @@ -349,6 +348,36 @@ static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int *softmax = *max; } +static const EnumPropertyItem *rna_GPencil_active_layer_itemf( + bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + bGPDlayer *gpl; + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_NULL_items; + } + + /* Existing layers */ + for (gpl = gpd->layers.first, i = 0; gpl; gpl = gpl->next, i++) { + item_tmp.identifier = gpl->info; + item_tmp.name = gpl->info; + item_tmp.value = i; + + item_tmp.icon = BKE_icon_gplayer_color_ensure(gpl); + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value) { bGPdata *gpd = ptr->id.data; @@ -366,31 +395,6 @@ static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value) BKE_animdata_fix_paths_rename_all(&gpd->id, "layers", oldname, gpl->info); } -static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const bool value) -{ - bGPdata *gpd = ptr->id.data; - bGPDlayer *gpl; - - /* set new value */ - if (value) { - /* enable on active layer (it's the one that's most likely to be of interest right now) */ - gpl = BKE_gpencil_layer_getactive(gpd); - if (gpl) { - gpl->flag |= GP_LAYER_ONIONSKIN; - } - - gpd->flag |= GP_DATA_SHOW_ONIONSKINS; - } - else { - /* disable on all layers - allowa quickly turning them all off, without having to check */ - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { - gpl->flag &= ~GP_LAYER_ONIONSKIN; - } - - gpd->flag &= ~GP_DATA_SHOW_ONIONSKINS; - } -} - static bGPDstroke *rna_GPencil_stroke_point_find_stroke(const bGPdata *gpd, const bGPDspoint *pt, bGPDlayer **r_gpl, bGPDframe **r_gpf) { bGPDlayer *gpl; @@ -454,14 +458,21 @@ static void rna_GPencil_stroke_point_add(bGPDstroke *stroke, int count, float pr stroke->points = MEM_recallocN_id(stroke->points, sizeof(bGPDspoint) * (stroke->totpoints + count), "gp_stroke_points"); + stroke->dvert = MEM_recallocN_id(stroke->dvert, + sizeof(MDeformVert) * (stroke->totpoints + count), + "gp_stroke_weight"); /* init the pressure and strength values so that old scripts won't need to * be modified to give these initial values... */ for (int i = 0; i < count; i++) { bGPDspoint *pt = stroke->points + (stroke->totpoints + i); + MDeformVert *dvert = stroke->dvert + (stroke->totpoints + i); pt->pressure = pressure; pt->strength = strength; + + dvert->totweight = 0; + dvert->dw = NULL; } stroke->totpoints += count; @@ -471,6 +482,7 @@ static void rna_GPencil_stroke_point_add(bGPDstroke *stroke, int count, float pr static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports, int index) { bGPDspoint *pt_tmp = stroke->points; + MDeformVert *pt_dvert = stroke->dvert; /* python style negative indexing */ if (index < 0) { @@ -485,27 +497,35 @@ static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports stroke->totpoints--; stroke->points = MEM_callocN(sizeof(bGPDspoint) * stroke->totpoints, "gp_stroke_points"); + stroke->dvert = MEM_callocN(sizeof(MDeformVert) * stroke->totpoints, "gp_stroke_weights"); - if (index > 0) + if (index > 0) { memcpy(stroke->points, pt_tmp, sizeof(bGPDspoint) * index); + /* verify weight data is available */ + if (pt_dvert != NULL) { + memcpy(stroke->dvert, pt_dvert, sizeof(MDeformVert) * index); + } + } - if (index < stroke->totpoints) + if (index < stroke->totpoints) { memcpy(&stroke->points[index], &pt_tmp[index + 1], sizeof(bGPDspoint) * (stroke->totpoints - index)); + if (pt_dvert != NULL) { + memcpy(&stroke->dvert[index], &pt_dvert[index + 1], sizeof(MDeformVert) * (stroke->totpoints - index)); + } + } /* free temp buffer */ MEM_freeN(pt_tmp); + if (pt_dvert != NULL) { + MEM_freeN(pt_dvert); + } WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } -static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame, const char *colorname) +static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame) { bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - if (colorname) { - BLI_strncpy(stroke->colorname, colorname, sizeof(stroke->colorname)); - } - stroke->palcolor = NULL; - stroke->flag |= GP_STROKE_RECALC_COLOR; BLI_addtail(&frame->strokes, stroke); return stroke; @@ -635,260 +655,18 @@ static void rna_GPencil_clear(bGPdata *gpd) WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } -/* Palettes */ -static bGPDpalette *rna_GPencil_palette_new(bGPdata *gpd, const char *name, bool setactive) -{ - bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, name, setactive != 0); - - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return palette; -} - -static void rna_GPencil_palette_remove(bGPdata *gpd, ReportList *reports, PointerRNA *palette_ptr) -{ - bGPDpalette *palette = palette_ptr->data; - if (BLI_findindex(&gpd->palettes, palette) == -1) { - BKE_report(reports, RPT_ERROR, "Palette not found in grease pencil data"); - return; - } - - BKE_gpencil_palette_delete(gpd, palette); - RNA_POINTER_INVALIDATE(palette_ptr); - - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -} - -static PointerRNA rna_GPencil_active_palette_get(PointerRNA *ptr) -{ - bGPdata *gpd = ptr->id.data; - - if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ - bGPDpalette *palette; - - for (palette = gpd->palettes.first; palette; palette = palette->next) { - if (palette->flag & PL_PALETTE_ACTIVE) { - break; - } - } - - if (palette) { - return rna_pointer_inherit_refine(ptr, &RNA_GPencilPalette, palette); - } - } - - return rna_pointer_inherit_refine(ptr, NULL, NULL); -} - -static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value) -{ - bGPdata *gpd = ptr->id.data; - - if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ - bGPDpalette *palette; - - for (palette = gpd->palettes.first; palette; palette = palette->next) { - if (palette == value.data) { - palette->flag |= PL_PALETTE_ACTIVE; - } - else { - palette->flag &= ~PL_PALETTE_ACTIVE; - } - } - /* force color recalc */ - BKE_gpencil_palette_change_strokes(gpd); - - WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); - } -} - -static int rna_GPencilPalette_index_get(PointerRNA *ptr) -{ - bGPdata *gpd = (bGPdata *)ptr->id.data; - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - - return BLI_findindex(&gpd->palettes, palette); -} - -static void rna_GPencilPalette_index_set(PointerRNA *ptr, int value) -{ - bGPdata *gpd = (bGPdata *)ptr->id.data; - bGPDpalette *palette = BLI_findlink(&gpd->palettes, value); - - BKE_gpencil_palette_setactive(gpd, palette); - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -} - -static void rna_GPencilPalette_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) -{ - bGPdata *gpd = (bGPdata *)ptr->id.data; - - *min = 0; - *max = max_ii(0, BLI_listbase_count(&gpd->palettes) - 1); - - *softmin = *min; - *softmax = *max; -} - -/* Palette colors */ -static bGPDpalettecolor *rna_GPencilPalette_color_new(bGPDpalette *palette) -{ - bGPDpalettecolor *color = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); - - return color; -} - -static void rna_GPencilPalette_color_remove(bGPDpalette *palette, ReportList *reports, PointerRNA *color_ptr) -{ - bGPDpalettecolor *color = color_ptr->data; - - if (BLI_findindex(&palette->colors, color) == -1) { - BKE_reportf(reports, RPT_ERROR, "Palette '%s' does not contain color given", palette->info + 2); - return; - } - - BKE_gpencil_palettecolor_delete(palette, color); - RNA_POINTER_INVALIDATE(color_ptr); - - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -} - -static PointerRNA rna_GPencilPalette_active_color_get(PointerRNA *ptr) -{ - bGPDpalette *palette = (bGPDpalette *)ptr->data; - bGPDpalettecolor *color; - - for (color = palette->colors.first; color; color = color->next) { - if (color->flag & PC_COLOR_ACTIVE) { - break; - } - } - - if (color) { - return rna_pointer_inherit_refine(ptr, &RNA_GPencilPaletteColor, color); - } - - return rna_pointer_inherit_refine(ptr, NULL, NULL); -} - -static void rna_GPencilPalette_active_color_set(PointerRNA *ptr, PointerRNA value) -{ - bGPDpalette *palette = (bGPDpalette *)ptr->data; - bGPDpalettecolor *color = value.data; - - BKE_gpencil_palettecolor_setactive(palette, color); -} - -static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) -{ - bGPdata *gpd = ptr->id.data; - bGPDpalette *palette = ptr->data; - - char oldname[64] = ""; - BLI_strncpy(oldname, palette->info, sizeof(oldname)); - - /* copy the new name into the name slot */ - BLI_strncpy_utf8(palette->info, value, sizeof(palette->info)); - - BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), - sizeof(palette->info)); - /* now fix animation paths */ - BKE_animdata_fix_paths_rename_all(&gpd->id, "palettes", oldname, palette->info); -} - -static char *rna_GPencilPalette_path(PointerRNA *ptr) -{ - bGPDpalette *palette = ptr->data; - char name_esc[sizeof(palette->info) * 2]; - - BLI_strescape(name_esc, palette->info, sizeof(name_esc)); - - return BLI_sprintfN("palettes[\"%s\"]", name_esc); -} - -static char *rna_GPencilPalette_color_path(PointerRNA *ptr) -{ - bGPdata *gpd = ptr->id.data; - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = ptr->data; - - char name_palette[sizeof(palette->info) * 2]; - char name_color[sizeof(palcolor->info) * 2]; - - BLI_strescape(name_palette, palette->info, sizeof(name_palette)); - BLI_strescape(name_color, palcolor->info, sizeof(name_color)); - - return BLI_sprintfN("palettes[\"%s\"].colors[\"%s\"]", name_palette, name_color); -} - -static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) -{ - bGPdata *gpd = ptr->id.data; - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = ptr->data; - - char oldname[64] = ""; - BLI_strncpy(oldname, palcolor->info, sizeof(oldname)); - - /* copy the new name into the name slot */ - BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info)); - BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), - sizeof(palcolor->info)); - - /* rename all strokes */ - BKE_gpencil_palettecolor_changename(gpd, oldname, palcolor->info); - - /* now fix animation paths */ - BKE_animdata_fix_paths_rename_all(&gpd->id, "colors", oldname, palcolor->info); -} - -static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value) +static void rna_GpencilVertex_groups_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { bGPDstroke *gps = ptr->data; - /* copy the new name into the name slot */ - BLI_strncpy_utf8(gps->colorname, value, sizeof(gps->colorname)); + if (gps->dvert) { + MDeformVert *dvert = gps->dvert; + + rna_iterator_array_begin(iter, (void *)dvert->dw, sizeof(MDeformWeight), dvert->totweight, 0, NULL); + } + else + rna_iterator_array_begin(iter, NULL, 0, 0, 0, NULL); } - - -static bool rna_GPencilPaletteColor_is_stroke_visible_get(PointerRNA *ptr) -{ - bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; - return (pcolor->color[3] > GPENCIL_ALPHA_OPACITY_THRESH); -} - -static bool rna_GPencilPaletteColor_is_fill_visible_get(PointerRNA *ptr) -{ - bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; - return (pcolor->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH); -} - -static int rna_GPencilPaletteColor_index_get(PointerRNA *ptr) -{ - bGPDpalette *palette = (bGPDpalette *)ptr->data; - bGPDpalettecolor *pcolor = BKE_gpencil_palettecolor_getactive(palette); - - return BLI_findindex(&palette->colors, pcolor); -} - -static void rna_GPencilPaletteColor_index_set(PointerRNA *ptr, int value) -{ - bGPDpalette *palette = (bGPDpalette *)ptr->data; - bGPDpalettecolor *pcolor = BLI_findlink(&palette->colors, value); - BKE_gpencil_palettecolor_setactive(palette, pcolor); -} - -static void rna_GPencilPaletteColor_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) -{ - bGPDpalette *palette = (bGPDpalette *)ptr->data; - - *min = 0; - *max = max_ii(0, BLI_listbase_count(&palette->colors) - 1); - - *softmin = *min; - *softmax = *max; -} - #else static void rna_def_gpencil_stroke_point(BlenderRNA *brna) @@ -918,11 +696,24 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Strength", "Color intensity (alpha factor)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "uv_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "uv_fac"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "UV Factor", "Internal UV factor"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "uv_rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "uv_rot"); + RNA_def_property_range(prop, 0.0f, M_PI * 2); + RNA_def_property_ui_text(prop, "UV Rotation", "Internal UV factor for dot mode"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT); 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"); + } static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cprop) @@ -955,7 +746,7 @@ static void rna_def_gpencil_triangle(BlenderRNA *brna) srna = RNA_def_struct(brna, "GPencilTriangle", NULL); RNA_def_struct_sdna(srna, "bGPDtriangle"); - RNA_def_struct_ui_text(srna, "Triangle", "Triangulation data for HQ fill"); + RNA_def_struct_ui_text(srna, "Triangle", "Triangulation data for Grease Pencil fills"); /* point v1 */ prop = RNA_def_property(srna, "v1", PROP_INT, PROP_NONE); @@ -974,6 +765,51 @@ 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) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GpencilVertexGroupElement", NULL); + RNA_def_struct_sdna(srna, "MDeformWeight"); + RNA_def_struct_ui_text(srna, "Vertex Group Element", "Weight value of a vertex in a vertex group"); + RNA_def_struct_ui_icon(srna, ICON_GROUP_VERTEX); + + /* we can't point to actual group, it is in the object and so + * there is no unique group to point to, hence the index */ + prop = RNA_def_property(srna, "group", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "def_nr"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Group Index", ""); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Weight", "Vertex Weight"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); } static void rna_def_gpencil_stroke(BlenderRNA *brna) @@ -1000,24 +836,30 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stroke Points", "Stroke data points"); rna_def_gpencil_stroke_points_api(brna, prop); + /* vertex groups */ + prop = RNA_def_property(srna, "groups", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_GpencilVertex_groups_begin", "rna_iterator_array_next", + "rna_iterator_array_end", "rna_iterator_array_get", NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "GpencilVertexGroupElement"); + RNA_def_property_ui_text(prop, "Groups", "Weights for the vertex groups this vertex is member of"); + /* Triangles */ prop = RNA_def_property(srna, "triangles", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "triangles", "tot_triangles"); RNA_def_property_struct_type(prop, "GPencilTriangle"); RNA_def_property_ui_text(prop, "Triangles", "Triangulation data for HQ fill"); - /* Color */ - prop = RNA_def_property(srna, "color", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "GPencilPaletteColor"); - RNA_def_property_pointer_sdna(prop, NULL, "palcolor"); - RNA_def_property_ui_text(prop, "Palette Color", "Color from palette used in Stroke"); - RNA_def_property_update(prop, 0, "rna_GPencil_update"); + /* Material Index */ + prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "mat_nr"); + RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Settings */ prop = RNA_def_property(srna, "draw_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, stroke_draw_mode_items); - RNA_def_property_ui_text(prop, "Draw Mode", ""); + RNA_def_property_ui_text(prop, "Draw Mode", "Coordinate space that stroke is in"); RNA_def_property_update(prop, 0, "rna_GPencil_update"); prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); @@ -1026,22 +868,23 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Select", "Stroke is selected for viewport editing"); RNA_def_property_update(prop, 0, "rna_GPencil_update"); - /* Color Name */ - prop = RNA_def_property(srna, "colorname", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilStrokeColor_info_set"); - RNA_def_property_ui_text(prop, "Color Name", "Palette color name"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_colorname_update"); - /* Cyclic: Draw a line from end to start point */ prop = RNA_def_property(srna, "draw_cyclic", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_CYCLIC); RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke"); RNA_def_property_update(prop, 0, "rna_GPencil_update"); + /* No fill: The stroke never must fill area and must use fill color as stroke color (this is a special flag for fill brush) */ + prop = RNA_def_property(srna, "is_nofill_stroke", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_NOFILL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "No Fill", "Special stroke to use as boundary for filling areas"); + RNA_def_property_update(prop, 0, "rna_GPencil_update"); + /* Line Thickness */ prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "thickness"); - RNA_def_property_range(prop, 1, 300); + RNA_def_property_range(prop, 1, 1000); RNA_def_property_ui_range(prop, 1, 10, 1, 0); RNA_def_property_ui_text(prop, "Thickness", "Thickness of stroke (in pixels)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); @@ -1062,7 +905,6 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "new", "rna_GPencil_stroke_new"); RNA_def_function_ui_description(func, "Add a new grease pencil stroke"); - parm = RNA_def_string(func, "colorname", 0, MAX_NAME, "Color", "Name of the color"); parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "", "The newly created stroke"); RNA_def_function_return(func, parm); @@ -1183,20 +1025,30 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_editable_func(prop, "rna_GPencilLayer_active_frame_editable"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Draw Style */ - // TODO: replace these with a "draw type" combo (i.e. strokes only, filled strokes, strokes + fills, volumetric)? - prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_VOLUMETRIC); - RNA_def_property_ui_text(prop, "Volumetric Strokes", - "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Layer Opacity */ prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "opacity"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Layer Opacity"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Stroke Drawing Color (Annotations) */ + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Color", "Color for all strokes in this layer"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Line Thickness (Annotations) */ + prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of annotation strokes"); + 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); RNA_def_property_float_sdna(prop, NULL, "tintcolor"); @@ -1212,62 +1064,21 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Tint Factor", "Factor of tinting color"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Line Thickness change */ + /* Line Thickness Change */ prop = RNA_def_property(srna, "line_change", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "thickness"); - //RNA_def_property_range(prop, 1, 10); /* 10 px limit comes from Windows OpenGL limits for natively-drawn strokes */ - RNA_def_property_int_funcs(prop, NULL, NULL, "rna_GPencilLayer_line_width_range"); - RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply to current strokes (in pixels)"); + RNA_def_property_int_sdna(prop, NULL, "line_change"); + RNA_def_property_range(prop, -300, 300); + RNA_def_property_ui_range(prop, -100, 100, 1.0, 1); + RNA_def_property_ui_text(prop, "Thickness Change", "Thickness change to apply to current strokes (in pixels)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Onion-Skinning */ prop = RNA_def_property(srna, "use_onion_skinning", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_ONIONSKIN); + RNA_def_property_boolean_sdna(prop, NULL, "onion_flag", GP_LAYER_ONIONSKIN); RNA_def_property_ui_text(prop, "Onion Skinning", "Ghost frames on either side of frame"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_onion_skinning_update"); - - prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "gstep"); - RNA_def_property_range(prop, -1, 120); - RNA_def_property_ui_text(prop, "Frames Before", - "Maximum number of frames to show before current frame " - "(0 = show only the previous sketch, -1 = don't show any frames before current)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "ghost_after_range", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "gstep_next"); - RNA_def_property_range(prop, -1, 120); - RNA_def_property_ui_text(prop, "Frames After", - "Maximum number of frames to show after current frame " - "(0 = show only the next sketch, -1 = don't show any frames after current)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "use_ghost_custom_colors", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_GHOST_PREVCOL | GP_LAYER_GHOST_NEXTCOL); - 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); - RNA_def_property_float_sdna(prop, NULL, "gcolor_prev"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Before Color", "Base color for ghosts before the active frame"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "after_color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "gcolor_next"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "use_ghosts_always", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_GHOST_ALWAYS); - RNA_def_property_ui_text(prop, "Always Show Ghosts", - "Ghosts are shown in renders and animation playback. Useful for special effects (e.g. motion blur)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); @@ -1296,7 +1107,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* expose as layers.active */ + /* 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); @@ -1310,7 +1121,6 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Select", "Layer is selected for editing in the Dope Sheet"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, "rna_GPencil_update"); - /* XXX keep this option? */ prop = RNA_def_property(srna, "show_points", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_DRAWDEBUG); RNA_def_property_ui_text(prop, "Show Points", "Draw the points which make up the strokes (for debugging purposes)"); @@ -1399,215 +1209,24 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, - "rna_GPencil_active_layer_index_get", - "rna_GPencil_active_layer_index_set", - "rna_GPencil_active_layer_index_range"); + RNA_def_property_int_funcs( + prop, + "rna_GPencil_active_layer_index_get", + "rna_GPencil_active_layer_index_set", + "rna_GPencil_active_layer_index_range"); RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); -} -static void rna_def_gpencil_palettecolor(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "GPencilPaletteColor", NULL); - RNA_def_struct_sdna(srna, "bGPDpalettecolor"); - RNA_def_struct_ui_text(srna, "Grease Pencil Palette color", "Collection of related colors"); - RNA_def_struct_path_func(srna, "rna_GPencilPalette_color_path"); - - /* Stroke Drawing Color */ - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "color"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Color", "Color for strokes"); + /* Active Layer - As an enum (for selecting active layer for annotations) */ + prop = RNA_def_property(srna, "active_note", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_funcs( + prop, + "rna_GPencil_active_layer_index_get", + "rna_GPencil_active_layer_index_set", + "rna_GPencil_active_layer_itemf"); + RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items); /* purely dynamic, as it maps to user-data */ + RNA_def_property_ui_text(prop, "Active Note", "Note/Layer to add annotation strokes to"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "color[3]"); - RNA_def_property_range(prop, 0.0, 1.0f); - RNA_def_property_ui_text(prop, "Opacity", "Color Opacity"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Name */ - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "info"); - RNA_def_property_ui_text(prop, "Name", "Color name"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPaletteColor_info_set"); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Fill Drawing Color */ - prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "fill"); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Fill alpha */ - prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "fill[3]"); - RNA_def_property_range(prop, 0.0, 1.0f); - RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Flags */ - prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_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_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_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_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_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_DATA, "rna_GPencil_update"); - - /* Draw Style */ - prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_VOLUMETRIC); - RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in " - "a volumetric effect"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Use High quality fill */ - prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HQ_FILL); - RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality to avoid glitches " - "(slower fps during animation play)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Read-only state props (for simpler UI code) */ - prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_stroke_visible_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); - - prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_fill_visible_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); -} - -/* palette colors api */ -static void rna_def_gpencil_palettecolors_api(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - PropertyRNA *prop; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "GPencilPaletteColors"); - srna = RNA_def_struct(brna, "GPencilPaletteColors", NULL); - RNA_def_struct_sdna(srna, "bGPDpalette"); - RNA_def_struct_ui_text(srna, "Palette colors", "Collection of palette colors"); - - func = RNA_def_function(srna, "new", "rna_GPencilPalette_color_new"); - RNA_def_function_ui_description(func, "Add a new color to the palette"); - parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The newly created color"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_GPencilPalette_color_remove"); - RNA_def_function_ui_description(func, "Remove a color from the palette"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The color to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); - - prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "GPencilPaletteColor"); - RNA_def_property_pointer_funcs(prop, "rna_GPencilPalette_active_color_get", "rna_GPencilPalette_active_color_set", NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Active Palette Color", "Current active color"); - - prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, - "rna_GPencilPaletteColor_index_get", - "rna_GPencilPaletteColor_index_set", - "rna_GPencilPaletteColor_index_range"); - RNA_def_property_ui_text(prop, "Active color Index", "Index of active palette color"); -} - -static void rna_def_gpencil_palette(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "GPencilPalette", NULL); - RNA_def_struct_sdna(srna, "bGPDpalette"); - RNA_def_struct_ui_text(srna, "Grease Pencil Palette", "Collection of related palettes"); - RNA_def_struct_path_func(srna, "rna_GPencilPalette_path"); - RNA_def_struct_ui_icon(srna, ICON_COLOR); - - /* Name */ - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "info"); - RNA_def_property_ui_text(prop, "Name", "Palette name"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPalette_info_set"); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Colors */ - prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "colors", NULL); - RNA_def_property_struct_type(prop, "GPencilPaletteColor"); - RNA_def_property_ui_text(prop, "Colors", "Colors of the palette"); - rna_def_gpencil_palettecolors_api(brna, prop); - -} - -static void rna_def_gpencil_palettes_api(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - PropertyRNA *prop; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "GreasePencilPalettes"); - srna = RNA_def_struct(brna, "GreasePencilPalettes", NULL); - RNA_def_struct_sdna(srna, "bGPdata"); - RNA_def_struct_ui_text(srna, "Grease Pencil Palettes", "Collection of grease pencil palettes"); - - func = RNA_def_function(srna, "new", "rna_GPencil_palette_new"); - RNA_def_function_ui_description(func, "Add a new grease pencil palette"); - parm = RNA_def_string(func, "name", "GPencilPalette", MAX_NAME, "Name", "Name of the palette"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_boolean(func, "set_active", true, "Set Active", "Activate the newly created palette"); - parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The newly created palette"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_GPencil_palette_remove"); - RNA_def_function_ui_description(func, "Remove a grease pencil palette"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The palette to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); - - prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "GPencilPalette"); - RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_palette_get", "rna_GPencil_active_palette_set", - NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Active Palette", "Current active palette"); - - prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, - "rna_GPencilPalette_index_get", - "rna_GPencilPalette_index_set", - "rna_GPencilPalette_index_range"); - RNA_def_property_ui_text(prop, "Active Palette Index", "Index of active palette"); } static void rna_def_gpencil_data(BlenderRNA *brna) @@ -1616,6 +1235,10 @@ static void rna_def_gpencil_data(BlenderRNA *brna) PropertyRNA *prop; FunctionRNA *func; + static float default_1[4] = { 0.6f, 0.6f, 0.6f, 0.5f }; + static float onion_dft1[3] = { 0.145098f, 0.419608f, 0.137255f }; /* green */ + static float onion_dft2[3] = { 0.125490f, 0.082353f, 0.529412f }; /* blue */ + srna = RNA_def_struct(brna, "GreasePencil", "ID"); RNA_def_struct_sdna(srna, "bGPdata"); RNA_def_struct_ui_text(srna, "Grease Pencil", "Freehand annotation sketchbook"); @@ -1628,28 +1251,52 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Layers", ""); rna_def_gpencil_layers_api(brna, prop); - /* Palettes */ - prop = RNA_def_property(srna, "palettes", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "palettes", NULL); - RNA_def_property_struct_type(prop, "GPencilPalette"); - RNA_def_property_ui_text(prop, "Palettes", ""); - rna_def_gpencil_palettes_api(brna, prop); - /* Animation Data */ rna_def_animdata_common(srna); + /* materials */ + prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_ui_text(prop, "Materials", ""); + RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */ + RNA_def_property_collection_funcs(prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int"); + + /* xray modes */ + prop = RNA_def_property(srna, "xray_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "xray_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_xraymodes_items); + RNA_def_property_ui_text(prop, "Xray", ""); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Flags */ prop = RNA_def_property(srna, "use_stroke_edit_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_EDITMODE); RNA_def_property_ui_text(prop, "Stroke Edit Mode", "Edit Grease Pencil strokes instead of viewport data"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, "rna_GPencil_editmode_update"); + prop = RNA_def_property(srna, "is_stroke_paint_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_PAINTMODE); + RNA_def_property_ui_text(prop, "Stroke Paint Mode", "Draw Grease Pencil strokes on click/drag"); + 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, "is_stroke_sculpt_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_SCULPTMODE); + RNA_def_property_ui_text(prop, "Stroke Sculpt Mode", "Sculpt Grease Pencil strokes instead of viewport data"); + 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, "is_stroke_weight_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_WEIGHTMODE); + RNA_def_property_ui_text(prop, "Stroke Weight Paint Mode", "Grease Pencil weight 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_funcs(prop, NULL, "rna_GPencil_use_onion_skinning_set"); - RNA_def_property_ui_text(prop, "Onion Skins", - "Show ghosts of the frames before and after the current frame, toggle to enable on active layer or disable all"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + RNA_def_property_ui_text(prop, "Onion Skins", "Show ghosts of the frames before and after the current frame"); + 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); @@ -1657,6 +1304,102 @@ static void rna_def_gpencil_data(BlenderRNA *brna) "and smaller red dot (end) points"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "show_constant_thickness", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_KEEPTHICKNESS); + RNA_def_property_ui_text(prop, "Keep thickness", "Show stroke with same thickness when viewport zoom change"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "pixfactor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "pixfactor"); + RNA_def_property_range(prop, 0.1f, 30.0f); + RNA_def_property_ui_range(prop, 0.1f, 30.0f, 1, 2); + RNA_def_property_ui_text(prop, "Scale", "Scale conversion factor for pixel size (use larger values for thicker lines)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_multiedit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_MULTIEDIT); + RNA_def_property_ui_text(prop, "MultiFrame", "Edit strokes from multiple grease pencil keyframes at the same time (keyframes must be selected to be included)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "line_color"); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_array_default(prop, default_1); + RNA_def_property_ui_text(prop, "Edit Line Color", "Color for editing line"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* onion skinning */ + prop = RNA_def_property(srna, "ghost_before_range", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "gstep"); + RNA_def_property_range(prop, 0, 120); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text(prop, "Frames Before", + "Maximum number of frames to show before current frame " + "(0 = don't show any frames before current)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "ghost_after_range", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "gstep_next"); + RNA_def_property_range(prop, 0, 120); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text(prop, "Frames After", + "Maximum number of frames to show after current frame " + "(0 = don't show any frames after current)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_ghost_custom_colors", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "onion_flag", GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); + 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); + RNA_def_property_float_sdna(prop, NULL, "gcolor_prev"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_array_default(prop, onion_dft1); + RNA_def_property_ui_text(prop, "Before Color", "Base color for ghosts before the active frame"); + 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); + RNA_def_property_float_sdna(prop, NULL, "gcolor_next"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_array_default(prop, onion_dft2); + RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); + RNA_def_property_update(prop, NC_SCREEN | NC_SCENE | ND_TOOLSETTINGS | ND_DATA | NC_GPENCIL, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_ghosts_always", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "onion_flag", GP_ONION_GHOST_ALWAYS); + RNA_def_property_ui_text(prop, "Always Show Ghosts", + "Ghosts are shown in renders and animation playback. Useful for special effects (e.g. motion blur)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "onion_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "onion_mode"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_onion_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Mode to display frames"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "use_onion_fade", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "onion_flag", GP_ONION_FADE); + RNA_def_property_ui_text(prop, "Fade", + "Display onion keyframes with a fade in color transparency"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + 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_property_ui_text(prop, "Loop", + "Display first onion keyframes using next frame color to show indication of loop start frame"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "onion_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "onion_factor"); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Onion Opacity", "Change fade opacity of displayed onion frames"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* API Functions */ func = RNA_def_function(srna, "clear", "rna_GPencil_clear"); RNA_def_function_ui_description(func, "Remove all the grease pencil data"); @@ -1670,12 +1413,12 @@ void RNA_def_gpencil(BlenderRNA *brna) rna_def_gpencil_layer(brna); rna_def_gpencil_frame(brna); - rna_def_gpencil_triangle(brna); + rna_def_gpencil_stroke(brna); rna_def_gpencil_stroke_point(brna); + rna_def_gpencil_triangle(brna); - rna_def_gpencil_palette(brna); - rna_def_gpencil_palettecolor(brna); + rna_def_gpencil_mvert_group(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c new file mode 100644 index 00000000000..df64121b2b4 --- /dev/null +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -0,0 +1,1314 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_gpencil_modifier.c + * \ingroup RNA + */ + + +#include +#include +#include + +#include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_mesh_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_animsys.h" +#include "BKE_data_transfer.h" +#include "BKE_DerivedMesh.h" +#include "BKE_dynamicpaint.h" +#include "BKE_effect.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_remap.h" +#include "BKE_multires.h" +#include "BKE_smoke.h" /* For smokeModifier_free & smokeModifier_createType */ +#include "BKE_gpencil_modifier.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#include "WM_api.h" +#include "WM_types.h" + +const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { + {0, "", 0, N_("Generate"), "" }, + {eGpencilModifierType_Instance, "GP_INSTANCE", ICON_MOD_ARRAY, "Instance", "Create grid of duplicate instances"}, + {eGpencilModifierType_Build, "GP_BUILD", ICON_MOD_BUILD, "Build", "Create duplication of strokes"}, + {eGpencilModifierType_Simplify, "GP_SIMPLIFY", ICON_MOD_DECIM, "Simplify", "Simplify stroke reducing number of points"}, + {eGpencilModifierType_Subdiv, "GP_SUBDIV", ICON_MOD_SUBSURF, "Subdivide", "Subdivide stroke adding more control points"}, + {0, "", 0, N_("Deform"), "" }, + {eGpencilModifierType_Hook, "GP_HOOK", ICON_HOOK, "Hook", "Deform stroke points using objects"}, + {eGpencilModifierType_Lattice, "GP_LATTICE", ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, + {eGpencilModifierType_Mirror, "GP_MIRROR", ICON_MOD_MIRROR, "Mirror", "Duplicate strokes like a mirror"}, + {eGpencilModifierType_Noise, "GP_NOISE", ICON_RNDCURVE, "Noise", "Add noise to strokes"}, + {eGpencilModifierType_Offset, "GP_OFFSET", ICON_MOD_DISPLACE, "Offset", "Change stroke location, rotation or scale"}, + {eGpencilModifierType_Smooth, "GP_SMOOTH", ICON_MOD_SMOOTH, "Smooth", "Smooth stroke"}, + {eGpencilModifierType_Thick, "GP_THICK", ICON_MAN_ROT, "Thickness", "Change stroke thickness"}, + {0, "", 0, N_("Color"), "" }, + {eGpencilModifierType_Color, "GP_COLOR", ICON_GROUP_VCOL, "Hue/Saturation", "Apply changes to stroke colors"}, + {eGpencilModifierType_Opacity, "GP_OPACITY", ICON_MOD_MASK, "Opacity", "Opacity of the strokes"}, + {eGpencilModifierType_Tint, "GP_TINT", ICON_COLOR, "Tint", "Tint strokes with new color"}, + {0, NULL, 0, NULL, NULL} +}; + +#ifndef RNA_RUNTIME +static const EnumPropertyItem modifier_gphook_falloff_items[] = { + { eGPHook_Falloff_None, "NONE", 0, "No Falloff", "" }, + { eGPHook_Falloff_Curve, "CURVE", 0, "Curve", "" }, + { eGPHook_Falloff_Smooth, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", "" }, + { eGPHook_Falloff_Sphere, "SPHERE", ICON_SPHERECURVE, "Sphere", "" }, + { eGPHook_Falloff_Root, "ROOT", ICON_ROOTCURVE, "Root", "" }, + { eGPHook_Falloff_InvSquare, "INVERSE_SQUARE", ICON_ROOTCURVE, "Inverse Square", "" }, + { eGPHook_Falloff_Sharp, "SHARP", ICON_SHARPCURVE, "Sharp", "" }, + { eGPHook_Falloff_Linear, "LINEAR", ICON_LINCURVE, "Linear", "" }, + { eGPHook_Falloff_Const, "CONSTANT", ICON_NOCURVE, "Constant", "" }, + { 0, NULL, 0, NULL, NULL } +}; + +static const EnumPropertyItem rna_enum_gpencil_lockshift_items[] = { + { GP_LOCKAXIS_X, "GP_LOCKAXIS_X", 0, "X", "Use X axis" }, + { GP_LOCKAXIS_Y, "GP_LOCKAXIS_Y", 0, "Y", "Use Y axis" }, + { GP_LOCKAXIS_Z, "GP_LOCKAXIS_Z", 0, "Z", "Use Z axis" }, + { 0, NULL, 0, NULL, NULL } +}; + +#endif + +#ifdef RNA_RUNTIME + +#include "DNA_particle_types.h" +#include "DNA_curve_types.h" +#include "DNA_smoke_types.h" + +#include "BKE_cachefile.h" +#include "BKE_context.h" +#include "BKE_library.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_object.h" +#include "BKE_gpencil.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) +{ + GpencilModifierData *md = (GpencilModifierData *)ptr->data; + + switch ((GpencilModifierType)md->type) { + case eGpencilModifierType_Noise: + return &RNA_NoiseGpencilModifier; + case eGpencilModifierType_Subdiv: + return &RNA_SubdivGpencilModifier; + case eGpencilModifierType_Simplify: + return &RNA_SimplifyGpencilModifier; + case eGpencilModifierType_Thick: + return &RNA_ThickGpencilModifier; + case eGpencilModifierType_Tint: + return &RNA_TintGpencilModifier; + case eGpencilModifierType_Color: + return &RNA_ColorGpencilModifier; + case eGpencilModifierType_Instance: + return &RNA_InstanceGpencilModifier; + case eGpencilModifierType_Build: + return &RNA_BuildGpencilModifier; + case eGpencilModifierType_Opacity: + return &RNA_OpacityGpencilModifier; + case eGpencilModifierType_Lattice: + return &RNA_LatticeGpencilModifier; + case eGpencilModifierType_Mirror: + return &RNA_MirrorGpencilModifier; + case eGpencilModifierType_Smooth: + return &RNA_SmoothGpencilModifier; + case eGpencilModifierType_Hook: + return &RNA_HookGpencilModifier; + case eGpencilModifierType_Offset: + return &RNA_OffsetGpencilModifier; + /* Default */ + case eGpencilModifierType_None: + case NUM_GREASEPENCIL_MODIFIER_TYPES: + return &RNA_GpencilModifier; + } + + return &RNA_GpencilModifier; +} + +static void rna_GpencilModifier_name_set(PointerRNA *ptr, const char *value) +{ + GpencilModifierData *gmd = ptr->data; + char oldname[sizeof(gmd->name)]; + + /* make a copy of the old name first */ + BLI_strncpy(oldname, gmd->name, sizeof(gmd->name)); + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(gmd->name, value, sizeof(gmd->name)); + + /* make sure the name is truly unique */ + if (ptr->id.data) { + Object *ob = ptr->id.data; + BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, gmd); + } + + /* fix all the animation data which may link to this */ + BKE_animdata_fix_paths_rename_all(NULL, "grease_pencil_modifiers", oldname, gmd->name); +} + +static char *rna_GpencilModifier_path(PointerRNA *ptr) +{ + GpencilModifierData *gmd = ptr->data; + char name_esc[sizeof(gmd->name) * 2]; + + BLI_strescape(name_esc, gmd->name, sizeof(name_esc)); + return BLI_sprintfN("grease_pencil_modifiers[\"%s\"]", name_esc); +} + +static void rna_GpencilModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->id.data); +} + +static void rna_GpencilModifier_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_GpencilModifier_update(bmain, scene, ptr); + DEG_relations_tag_update(bmain); +} + +/* Vertex Groups */ + +#define RNA_GP_MOD_VGROUP_NAME_SET(_type, _prop) \ +static void rna_##_type##GpencilModifier_##_prop##_set(PointerRNA *ptr, const char *value) \ +{ \ + _type##GpencilModifierData *tmd = (_type##GpencilModifierData *)ptr->data; \ + rna_object_vgroup_name_set(ptr, value, tmd->_prop, sizeof(tmd->_prop)); \ +} + +RNA_GP_MOD_VGROUP_NAME_SET(Noise, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Thick, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Opacity, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Lattice, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Smooth, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Hook, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname); + +#undef RNA_GP_MOD_VGROUP_NAME_SET + +/* Objects */ + +static void greasepencil_modifier_object_set(Object *self, Object **ob_p, int type, PointerRNA value) +{ + Object *ob = value.data; + + if (!self || ob != self) { + if (!ob || type == OB_EMPTY || ob->type == type) { + id_lib_extern((ID *)ob); + *ob_p = ob; + } + } +} + +#define RNA_GP_MOD_OBJECT_SET(_type, _prop, _obtype) \ +static void rna_##_type##GpencilModifier_##_prop##_set(PointerRNA *ptr, PointerRNA value) \ +{ \ + _type##GpencilModifierData *tmd = (_type##GpencilModifierData *)ptr->data; \ + greasepencil_modifier_object_set(ptr->id.data, &tmd->_prop, _obtype, value); \ +} + +RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); +RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY); + +#undef RNA_GP_MOD_OBJECT_SET + +static void rna_HookGpencilModifier_object_set(PointerRNA *ptr, PointerRNA value) +{ + HookGpencilModifierData *hmd = ptr->data; + Object *ob = (Object *)value.data; + + hmd->object = ob; + id_lib_extern((ID *)ob); + BKE_object_modifier_gpencil_hook_reset(ob, hmd); +} + +#else + +static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NoiseGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Noise Modifier", "Noise effect modifier"); + RNA_def_struct_sdna(srna, "NoiseGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_RNDCURVE); + + 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, "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_NoiseGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + 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_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_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_MOD_LOCATION); + RNA_def_property_ui_text(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, "affect_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, "affect_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, "affect_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"); + 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); + RNA_def_property_ui_text(prop, "Full Stroke", "The noise moves the stroke as a whole, not point by point"); + 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"); + 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, "step", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "step"); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_ui_text(prop, "Step", "Number of frames before recalculate random values again"); + 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_NOISE_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_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_NOISE_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SmoothGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Smooth Modifier", "Smooth effect modifier"); + RNA_def_struct_sdna(srna, "SmoothGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SMOOTH); + + 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, "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_SmoothGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "factor"); + RNA_def_property_range(prop, 0, 2); + RNA_def_property_ui_text(prop, "Factor", "Amount of smooth to apply"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_MOD_LOCATION); + RNA_def_property_ui_text(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, "affect_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_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, "affect_thickness", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_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, "affect_uv", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_MOD_UV); + RNA_def_property_ui_text(prop, "Affect UV", "The modifier affects the UV rotation factor of the point"); + 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, "step", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "step"); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_text(prop, "Step", "Number of times to apply smooth (high numbers can reduce fps)"); + 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_SMOOTH_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SMOOTH_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_SMOOTH_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SubdivGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Subdivision Modifier", "Subdivide Stroke modifier"); + RNA_def_struct_sdna(srna, "SubdivGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SUBSURF); + + 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, "level", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "level"); + RNA_def_property_range(prop, 0, 5); + 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"); + 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_SUBDIV_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SUBDIV_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_gpencil_simplify_mode_items[] = { + { GP_SIMPLIFY_FIXED, "FIXED", ICON_IPO_CONSTANT, "Fixed", + "Delete alternative vertices in the stroke, except extrems" }, + { GP_SIMPLIFY_ADAPTATIVE, "ADAPTATIVE", ICON_IPO_EASE_IN_OUT, "Adaptative", + "Use a RDP algorithm to simplify" }, + { 0, NULL, 0, NULL, NULL } + }; + + srna = RNA_def_struct(brna, "SimplifyGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Simplify Modifier", "Simplify Stroke modifier"); + RNA_def_struct_sdna(srna, "SimplifyGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_DECIM); + + 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, "factor", PROP_FLOAT, PROP_NONE); + 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); + RNA_def_property_ui_text(prop, "Factor", "Factor of Simplify"); + 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_SIMPLIFY_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SIMPLIFY_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Mode */ + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_gpencil_simplify_mode_items); + RNA_def_property_ui_text(prop, "Mode", "How simplify the stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "step"); + RNA_def_property_range(prop, 1, 50); + RNA_def_property_ui_text(prop, "Iterations", "Number of times to apply simplify"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilthick(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ThickGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Thick Modifier", "Subdivide and Smooth Stroke modifier"); + RNA_def_struct_sdna(srna, "ThickGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MAN_ROT); + + 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, "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_ThickGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + 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_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_THICK_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_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_THICK_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "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_THICK_CUSTOM_CURVE); + RNA_def_property_ui_text(prop, "Custom Curve", "Use a custom curve to define thickness changes"); + 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_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_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpenciloffset(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "OffsetGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Offset Modifier", "Offset Stroke modifier"); + RNA_def_struct_sdna(srna, "OffsetGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_DISPLACE); + + 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, "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_OffsetGpencilModifier_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_OFFSET_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OFFSET_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_OFFSET_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_float_sdna(prop, NULL, "loc"); + RNA_def_property_ui_text(prop, "Location", "Values for change 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, "rotation", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, NULL, "rot"); + RNA_def_property_ui_text(prop, "Rotation", "Values for chages 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, "scale", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_ui_text(prop, "Scale", "Values 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"); +} + +static void rna_def_modifier_gpenciltint(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "TintGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Tint Modifier", "Tint Stroke Color modifier"); + RNA_def_struct_sdna(srna, "TintGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_COLOR); + + 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, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "rgb"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Color", "Color used for tinting"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + 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 for mixing color"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "create_colors", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TINT_CREATE_COLORS); + RNA_def_property_ui_text(prop, "Create Colors", "When apply modifier, create new color in the palette"); + 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_TINT_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TINT_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilcolor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ColorGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Hue/Saturation Modifier", "Change Hue/Saturation modifier"); + RNA_def_struct_sdna(srna, "ColorGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_GROUP_VCOL); + + 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, "hue", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 2.0); + RNA_def_property_ui_range(prop, 0.0, 2.0, 0.1, 3); + 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_ui_range(prop, 0.0, 2.0, 0.1, 3); + 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_ui_range(prop, 0.0, 2.0, 0.1, 3); + 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_colors", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_COLOR_CREATE_COLORS); + RNA_def_property_ui_text(prop, "Create Colors", "When apply modifier, create new color in the palette"); + 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_COLOR_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_COLOR_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "OpacityGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Opacity Modifier", "Opacity of Strokes modifier"); + RNA_def_struct_sdna(srna, "OpacityGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_MASK); + + 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, "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_OpacityGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + 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, "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_OPACITY_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_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_OPACITY_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilinstance(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "InstanceGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Instance Modifier", "Create grid of duplicate instances"); + RNA_def_struct_sdna(srna, "InstanceGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_ARRAY); + + 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, "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, "count", PROP_INT, PROP_XYZ); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_ui_range(prop, 1, 1000, 1, -1); + RNA_def_property_ui_text(prop, "Count", "Number of items"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Offset parameters */ + prop = RNA_def_property(srna, "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_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); + RNA_def_property_float_sdna(prop, NULL, "shift"); + RNA_def_property_ui_text(prop, "Shift", "Shiftness 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, "lock_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "lock_axis"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_lockshift_items); + //RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Axis", ""); + 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 chages 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, "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"); + 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_INSTANCE_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); + 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_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_INSTANCE_RANDOM_SIZE); + RNA_def_property_ui_text(prop, "Random Scale", "Use random factors for scale"); + 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); + 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_INSTANCE_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_INSTANCE_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, "use_make_objects", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_INSTANCE_MAKE_OBJECTS); + RNA_def_property_ui_text(prop, "Make Objects", + "When applying this modifier, instances get created as separate objects"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilbuild(BlenderRNA *brna) +{ + static EnumPropertyItem prop_gpencil_build_mode_items[] = { + {GP_BUILD_MODE_SEQUENTIAL, "SEQUENTIAL", ICON_PARTICLE_POINT, "Sequential", + "Strokes appear/disappear one after the other, but only a single one changes at a time"}, + {GP_BUILD_MODE_CONCURRENT, "CONCURRENT", ICON_PARTICLE_TIP, "Concurrent", + "Multiple strokes appear/disappear at once"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem prop_gpencil_build_transition_items[] = { + {GP_BUILD_TRANSITION_GROW, "GROW", 0, "Grow", + "Show points in the order they occur in each stroke " + "(e.g. for animating lines being drawn)"}, + {GP_BUILD_TRANSITION_SHRINK, "SHRINK", 0, "Shrink", + "Hide points from the end of each stroke to the start " + "(e.g. for animating lines being erased)"}, + {GP_BUILD_TRANSITION_FADE, "FADE", 0, "Fade", + "Hide points in the order they occur in each stroke " + "(e.g. for animating ink fading or vanishing after getting drawn)"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem prop_gpencil_build_time_align_items[] = { + {GP_BUILD_TIMEALIGN_START, "START", 0, "Align Start", + "All strokes start at same time (i.e. short strokes finish earlier)"}, + {GP_BUILD_TIMEALIGN_END, "END", 0, "Align End", + "All strokes end at same time (i.e. short strokes start later)"}, + {0, NULL, 0, NULL, NULL} + }; + + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "BuildGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Build Modifier", "Animate strokes appearing and disappearing"); + RNA_def_struct_sdna(srna, "BuildGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_BUILD); + + /* Mode */ + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_gpencil_build_mode_items); + RNA_def_property_ui_text(prop, "Mode", "How many strokes are being animated at a time"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Direction */ + prop = RNA_def_property(srna, "transition", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_gpencil_build_transition_items); + RNA_def_property_ui_text(prop, "Transition", "How are strokes animated (i.e. are they appearing or disappearing)"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + + /* Transition Onset Delay + Length */ + prop = RNA_def_property(srna, "start_delay", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "start_delay"); + RNA_def_property_ui_text(prop, "Start Delay", "Number of frames after each GP keyframe before the modifier has any effect"); + RNA_def_property_range(prop, 0, MAXFRAMEF); + RNA_def_property_ui_range(prop, 0, 200, 1, -1); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "length", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "length"); + RNA_def_property_ui_text(prop, "Length", + "Maximum number of frames that the build effect can run for " + "(unless another GP keyframe occurs before this time has elapsed)"); + RNA_def_property_range(prop, 1, MAXFRAMEF); + RNA_def_property_ui_range(prop, 1, 1000, 1, -1); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + + /* Concurrent Mode Settings */ + prop = RNA_def_property(srna, "concurrent_time_alignment", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "time_alignment"); + RNA_def_property_enum_items(prop, prop_gpencil_build_time_align_items); + RNA_def_property_ui_text(prop, "Time Alignment", "When should strokes start to appear/disappear"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + + + /* Time Limits */ + prop = RNA_def_property(srna, "use_restrict_frame_range", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BUILD_RESTRICT_TIME); + RNA_def_property_ui_text(prop, "Restrict Frame Range", "Only modify strokes during the specified frame range"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "start_frame"); + RNA_def_property_ui_text(prop, "Start Frame", "Start Frame (when Restrict Frame Range is enabled)"); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "end_frame"); + RNA_def_property_ui_text(prop, "End Frame", "End Frame (when Restrict Frame Range is enabled)"); + RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + + /* Filters - Layer */ + 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, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BUILD_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Filters - Pass Index */ +#if 0 + 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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BUILD_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +#endif +} + +static void rna_def_modifier_gpencillattice(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "LatticeGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Lattice Modifier", "Change stroke using lattice to deform modifier"); + RNA_def_struct_sdna(srna, "LatticeGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_LATTICE); + + 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, "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_LatticeGpencilModifier_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_LATTICE_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LATTICE_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_LATTICE_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Lattice object to deform with"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_LatticeGpencilModifier_object_set", NULL, "rna_Lattice_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 1, 10, 2); + RNA_def_property_ui_text(prop, "Strength", "Strength of modifier effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilmirror(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MirrorGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Mirror Modifier", "Change stroke using lattice to deform modifier"); + RNA_def_struct_sdna(srna, "MirrorGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR); + + 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, "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_MIRROR_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_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, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Object used as center"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_MirrorGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "clip", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_CLIPPING); + RNA_def_property_ui_text(prop, "Clip", "Clip points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "x_axis", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_AXIS_X); + RNA_def_property_ui_text(prop, "X", "Mirror this axis"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "y_axis", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_AXIS_Y); + RNA_def_property_ui_text(prop, "Y", "Mirror this axis"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "z_axis", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_AXIS_Z); + RNA_def_property_ui_text(prop, "Z", "Mirror this axis"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} + +static void rna_def_modifier_gpencilhook(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "HookGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Hook Modifier", "Hook modifier to modify the location of stroke points"); + RNA_def_struct_sdna(srna, "HookGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_HOOK); + + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Object", "Parent Object for hook, also recalculates and clears offset"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_pointer_funcs(prop, NULL, "rna_HookGpencilModifier_object_set", NULL, NULL); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "subtarget", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "subtarget"); + RNA_def_property_ui_text(prop, "Sub-Target", + "Name of Parent Bone for hook (if applicable), also recalculates and clears offset"); + 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, "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_HOOK_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_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_HOOK_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_HOOK_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "force"); + RNA_def_property_range(prop, 0, 1); + RNA_def_property_ui_text(prop, "Strength", "Relative force of the hook"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, modifier_gphook_falloff_items); /* share the enum */ + RNA_def_property_ui_text(prop, "Falloff Type", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "falloff"); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 100, 100, 2); + RNA_def_property_ui_text(prop, "Radius", "If not zero, the distance from the hook where influence ends"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "falloff_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "curfalloff"); + RNA_def_property_ui_text(prop, "Falloff Curve", "Custom Lamp Falloff Curve"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "center", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "cent"); + RNA_def_property_ui_text(prop, "Hook Center", ""); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "matrix_inverse", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "parentinv"); + RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); + RNA_def_property_ui_text(prop, "Matrix", "Reverse the transformation between this object and its target"); + RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_falloff_uniform", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_HOOK_UNIFORM_SPACE); + RNA_def_property_ui_text(prop, "Uniform Falloff", "Compensate for non-uniform object scale"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); +} +void RNA_def_greasepencil_modifier(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* data */ + srna = RNA_def_struct(brna, "GpencilModifier", NULL); + RNA_def_struct_ui_text(srna, "GpencilModifier", "Modifier affecting the grease pencil object"); + RNA_def_struct_refine_func(srna, "rna_GpencilModifier_refine"); + RNA_def_struct_path_func(srna, "rna_GpencilModifier_path"); + RNA_def_struct_sdna(srna, "GpencilModifierData"); + + /* strings */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GpencilModifier_name_set"); + RNA_def_property_ui_text(prop, "Name", "Modifier name"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + /* enums */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, rna_enum_object_greasepencil_modifier_type_items); + RNA_def_property_ui_text(prop, "Type", ""); + + /* flags */ + prop = RNA_def_property(srna, "show_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eGpencilModifierMode_Realtime); + RNA_def_property_ui_text(prop, "Realtime", "Display modifier in viewport"); + RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 0); + + prop = RNA_def_property(srna, "show_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eGpencilModifierMode_Render); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_ui_text(prop, "Render", "Use modifier during render"); + RNA_def_property_ui_icon(prop, ICON_SCENE, 0); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL); + + prop = RNA_def_property(srna, "show_in_editmode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eGpencilModifierMode_Editmode); + RNA_def_property_ui_text(prop, "Edit Mode", "Display modifier in Edit mode"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_def_property_ui_icon(prop, ICON_EDITMODE_HLT, 0); + + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eGpencilModifierMode_Expanded); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_ui_text(prop, "Expanded", "Set modifier expanded in the user interface"); + RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); + + /* types */ + rna_def_modifier_gpencilnoise(brna); + rna_def_modifier_gpencilsmooth(brna); + rna_def_modifier_gpencilsubdiv(brna); + rna_def_modifier_gpencilsimplify(brna); + rna_def_modifier_gpencilthick(brna); + rna_def_modifier_gpenciloffset(brna); + rna_def_modifier_gpenciltint(brna); + rna_def_modifier_gpencilcolor(brna); + rna_def_modifier_gpencilinstance(brna); + rna_def_modifier_gpencilbuild(brna); + rna_def_modifier_gpencilopacity(brna); + rna_def_modifier_gpencillattice(brna); + rna_def_modifier_gpencilmirror(brna); + rna_def_modifier_gpencilhook(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 83d173de6c8..a88623e5b5b 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -153,6 +153,8 @@ void RNA_def_dynamic_paint(struct BlenderRNA *brna); void RNA_def_fluidsim(struct BlenderRNA *brna); void RNA_def_fcurve(struct BlenderRNA *brna); void RNA_def_gpencil(struct BlenderRNA *brna); +void RNA_def_greasepencil_modifier(struct BlenderRNA *brna); +void RNA_def_shader_fx(struct BlenderRNA *brna); void RNA_def_image(struct BlenderRNA *brna); void RNA_def_key(struct BlenderRNA *brna); void RNA_def_light(struct BlenderRNA *brna); @@ -283,6 +285,7 @@ void rna_TextureSlot_update(struct bContext *C, struct PointerRNA *ptr); bool rna_Armature_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Camera_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Curve_object_poll(struct PointerRNA *ptr, struct PointerRNA value); +bool rna_GPencil_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Light_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Lattice_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Mesh_object_poll(struct PointerRNA *ptr, struct PointerRNA value); @@ -291,6 +294,10 @@ bool rna_Mesh_object_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Action_id_poll(struct PointerRNA *ptr, struct PointerRNA value); bool rna_Action_actedit_assign_poll(struct PointerRNA *ptr, struct PointerRNA value); +/* Grease Pencil datablock polling functions - for filtering GP Object vs Annotation datablocks */ +bool rna_GPencil_datablocks_annotations_poll(struct PointerRNA *ptr, const struct PointerRNA value); +bool rna_GPencil_datablocks_obdata_poll(struct PointerRNA *ptr, const struct PointerRNA value); + char *rna_TextureSlot_path(struct PointerRNA *ptr); char *rna_Node_ImageUser_path(struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index febe74f63c9..50aa3cb5b81 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -231,6 +231,9 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char case ID_LT: type = OB_LATTICE; break; + case ID_GD: + type = OB_GPENCIL; + break; case ID_AR: type = OB_ARMATURE; break; @@ -266,6 +269,11 @@ static Material *rna_Main_materials_new(Main *bmain, const char *name) return (Material *)id; } +static void rna_Main_materials_gpencil_data(Main *UNUSED(bmain), struct PointerRNA *ma) +{ + BKE_material_init_gpencil_settings((Material *)ma); +} + static const EnumPropertyItem *rna_Main_nodetree_type_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { return rna_node_tree_type_itemf(NULL, NULL, r_free); @@ -783,6 +791,11 @@ void RNA_def_main_materials(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_pointer(func, "material", "Material", "", "New material data-block"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "create_gpencil_data", "rna_Main_materials_gpencil_data"); + RNA_def_function_ui_description(func, "Add grease pencil material settings"); + parm = RNA_def_pointer(func, "material", "Material", "", "Material"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + func = RNA_def_function(srna, "remove", "rna_Main_ID_remove"); RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Remove a material from the current blendfile"); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 0a8ea99b8fb..56f5a12516b 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -72,6 +72,7 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = { #include "BKE_colorband.h" #include "BKE_context.h" #include "BKE_main.h" +#include "BKE_gpencil.h" #include "BKE_material.h" #include "BKE_texture.h" #include "BKE_node.h" @@ -85,6 +86,7 @@ const EnumPropertyItem rna_enum_ramp_blend_items[] = { #include "ED_node.h" #include "ED_image.h" #include "ED_screen.h" +#include "ED_gpencil.h" static void rna_Material_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { @@ -104,6 +106,30 @@ static void rna_Material_update_previews(Main *UNUSED(bmain), Scene *UNUSED(scen WM_main_add_notifier(NC_MATERIAL | ND_SHADING_PREVIEW, ma); } +static void rna_MaterialGpencil_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Material *ma = ptr->id.data; + PreviewImage *preview = ma->preview; + + rna_Material_update(bmain, scene, ptr); + + /* update previews (icon and thumbnail) */ + preview->flag[ICON_SIZE_ICON] |= PRV_CHANGED; + preview->flag[ICON_SIZE_PREVIEW] |= PRV_CHANGED; + WM_main_add_notifier(NC_MATERIAL | ND_SHADING_PREVIEW, ma); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma); +} + +static void rna_MaterialGpencil_nopreview_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Material *ma = ptr->id.data; + + rna_Material_update(bmain, scene, ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA, ma); +} + static void rna_Material_draw_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Material *ma = ptr->id.data; @@ -243,6 +269,59 @@ void rna_mtex_texture_slots_clear(ID *self_id, struct bContext *C, ReportList *r WM_event_add_notifier(C, NC_TEXTURE, CTX_data_scene(C)); } +static bool rna_is_grease_pencil_get(PointerRNA *ptr) +{ + Material *ma = (Material *)ptr->data; + if (ma->gp_style != NULL) + return true; + + return false; +} + +static void rna_gpcolordata_uv_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + /* update all uv strokes of this color */ + Material *ma = ptr->id.data; + ED_gpencil_update_color_uv(bmain, ma); + + rna_MaterialGpencil_update(bmain, scene, ptr); +} + +static char *rna_GpencilColorData_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_sprintfN("grease_pencil"); +} + +static int rna_GpencilColorData_is_stroke_visible_get(PointerRNA *ptr) +{ + MaterialGPencilStyle *pcolor = ptr->data; + return (pcolor->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH); +} + +static int rna_GpencilColorData_is_fill_visible_get(PointerRNA *ptr) +{ + MaterialGPencilStyle *pcolor = (MaterialGPencilStyle *)ptr->data; + return ((pcolor->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (pcolor->fill_style > 0)); +} + +static void rna_GpencilColorData_stroke_image_set(PointerRNA *ptr, PointerRNA value) +{ + MaterialGPencilStyle *pcolor = ptr->data; + ID *id = value.data; + + id_us_plus(id); + pcolor->sima = (struct Image *)id; +} + +static void rna_GpencilColorData_fill_image_set(PointerRNA *ptr, PointerRNA value) +{ + MaterialGPencilStyle *pcolor = (MaterialGPencilStyle *)ptr->data; + ID *id = value.data; + + id_us_plus(id); + pcolor->ima = (struct Image *)id; +} + #else static void rna_def_material_display(StructRNA *srna) @@ -296,6 +375,251 @@ static void rna_def_material_display(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Material_update"); } +static void rna_def_material_greasepencil(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* 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" }, + { 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" }, + { 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_CHESSBOARD, "CHESSBOARD", 0, "Checker Board", "Fill area with chessboard pattern" }, + { GP_STYLE_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" }, + { 0, NULL, 0, NULL, NULL } + }; + + srna = RNA_def_struct(brna, "MaterialGPencilStyle", NULL); + RNA_def_struct_sdna(srna, "MaterialGPencilStyle"); + 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); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "stroke_rgba"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Color", ""); + 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); + RNA_def_property_float_sdna(prop, NULL, "fill_rgba"); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); + 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); + RNA_def_property_float_sdna(prop, NULL, "mix_rgba"); + RNA_def_property_array(prop, 4); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Mix Color", "Color for mixing with primary filling color"); + 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); + 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_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_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* Texture angle */ + prop = RNA_def_property(srna, "texture_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "texture_angle"); + RNA_def_property_ui_text(prop, "Angle", "Texture Orientation Angle"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* Scale factor for texture */ + prop = RNA_def_property(srna, "texture_scale", PROP_FLOAT, PROP_COORDS); + RNA_def_property_float_sdna(prop, NULL, "texture_scale"); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "Scale", "Scale Factor for Texture"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* Shift factor to move texture in 2d space */ + prop = RNA_def_property(srna, "texture_offset", PROP_FLOAT, PROP_COORDS); + RNA_def_property_float_sdna(prop, NULL, "texture_offset"); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "Offset", "Shift Texture in 2d Space"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* Texture opacity size */ + prop = RNA_def_property(srna, "texture_opacity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "texture_opacity"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Opacity", "Texture Opacity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* texture pixsize factor (used for UV along the stroke) */ + prop = RNA_def_property(srna, "pixel_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "texture_pixsize"); + RNA_def_property_range(prop, 1, 5000); + RNA_def_property_ui_text(prop, "UV Factor", "Texture Pixel Size factor along the stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_gpcolordata_uv_update"); + + /* 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_ui_icon(prop, ICON_RESTRICT_VIEW_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"); + + 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_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"); + + 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_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"); + + 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_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, "texture_mix", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STYLE_COLOR_TEX_MIX); + RNA_def_property_ui_text(prop, "Mix Texture", "Mix texture image with filling colors"); + 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_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"); + + /* 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"); + + /* mode type */ + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, gpcolordata_mode_types_items); + RNA_def_property_ui_text(prop, "Mode Type", "Select draw mode for stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* stroke style */ + prop = RNA_def_property(srna, "stroke_style", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "stroke_style"); + RNA_def_property_enum_items(prop, stroke_style_items); + RNA_def_property_ui_text(prop, "Stroke Style", "Select style used to draw strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* stroke image texture */ + prop = RNA_def_property(srna, "stroke_image", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "sima"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_GpencilColorData_stroke_image_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Image", ""); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* fill style */ + prop = RNA_def_property(srna, "fill_style", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "fill_style"); + RNA_def_property_enum_items(prop, fill_style_items); + RNA_def_property_ui_text(prop, "Fill Style", "Select style used to fill strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* gradient type */ + prop = RNA_def_property(srna, "gradient_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "gradient_type"); + RNA_def_property_enum_items(prop, fill_gradient_items); + RNA_def_property_ui_text(prop, "Gradient Type", "Select type of gradient used to fill strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* fill image texture */ + prop = RNA_def_property(srna, "fill_image", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "ima"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_GpencilColorData_fill_image_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Image", ""); + RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); + + /* Read-only state props (for simpler UI code) */ + prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GpencilColorData_is_stroke_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); + + prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GpencilColorData_is_fill_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); + +} + void RNA_def_material(BlenderRNA *brna) { StructRNA *srna; @@ -410,6 +734,19 @@ void RNA_def_material(BlenderRNA *brna) rna_def_material_display(srna); + /* grease pencil */ + prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_style"); + RNA_def_property_ui_text(prop, "Grease Pencil Settings", "Grease pencil color settings for material"); + + prop = RNA_def_property(srna, "is_grease_pencil", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_is_grease_pencil_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Grease Pencil", "True if this material has grease pencil data"); + + rna_def_material_greasepencil(brna); + + RNA_api_material(srna); } diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index aded4229a3c..81c3c9b43b9 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -338,6 +338,7 @@ static void rna_def_movieclip(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this movie clip"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b9a8f306baf..a6172bd9cc2 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -8358,6 +8358,7 @@ static void rna_def_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block"); RNA_def_property_update(prop, NC_NODE, NULL); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index ac1a6d512c3..d84827210bf 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -37,6 +37,8 @@ #include "DNA_scene_types.h" #include "DNA_meta_types.h" #include "DNA_workspace_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" @@ -71,7 +73,10 @@ const EnumPropertyItem rna_enum_object_mode_items[] = { {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""}, {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""}, {OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""}, - {OB_MODE_GPENCIL, "GPENCIL_EDIT", ICON_GREASEPENCIL, "Edit Strokes", "Edit Grease Pencil Strokes"}, + {OB_MODE_GPENCIL_EDIT, "GPENCIL_EDIT", ICON_EDITMODE_HLT, "Edit Mode", "Edit Grease Pencil Strokes"}, + {OB_MODE_GPENCIL_SCULPT, "GPENCIL_SCULPT", ICON_SCULPTMODE_HLT, "Sculpt Mode", "Sculpt Grease Pencil Strokes"}, + {OB_MODE_GPENCIL_PAINT, "GPENCIL_PAINT", ICON_GREASEPENCIL, "Draw", "Paint Grease Pencil Strokes"}, + {OB_MODE_GPENCIL_WEIGHT, "GPENCIL_WEIGHT", ICON_WPAINT_HLT, "Weight Paint", "Grease Pencil Weight Paint Strokes" }, {0, NULL, 0, NULL, NULL} }; @@ -87,6 +92,11 @@ const EnumPropertyItem rna_enum_object_empty_drawtype_items[] = { {0, NULL, 0, NULL, NULL} }; +const EnumPropertyItem rna_enum_object_gpencil_type_items[] = { + { GP_EMPTY, "EMPTY", ICON_OUTLINER_OB_GREASEPENCIL, "Blank", "Create an empty grease pencil object" }, + { GP_MONKEY, "MONKEY", ICON_MONKEY, "Monkey", "Construct a Suzanne grease pencil object" }, + { 0, NULL, 0, NULL, NULL } +}; static const EnumPropertyItem parent_type_items[] = { {PAROBJECT, "OBJECT", 0, "Object", "The object is parented to an object"}, @@ -144,6 +154,7 @@ const EnumPropertyItem rna_enum_object_type_items[] = { {OB_ARMATURE, "ARMATURE", 0, "Armature", ""}, {OB_LATTICE, "LATTICE", 0, "Lattice", ""}, {OB_EMPTY, "EMPTY", 0, "Empty", ""}, + {OB_GPENCIL, "GPENCIL", 0, "GPencil", ""}, {0, "", 0, NULL, NULL}, {OB_CAMERA, "CAMERA", 0, "Camera", ""}, {OB_LAMP, "LIGHT", 0, "Light", ""}, @@ -175,6 +186,7 @@ const EnumPropertyItem rna_enum_object_axis_items[] = { #include "DNA_key_types.h" #include "DNA_constraint_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ID.h" #include "DNA_lattice_types.h" #include "DNA_node_types.h" @@ -383,10 +395,24 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) case OB_ARMATURE: return &RNA_Armature; case OB_SPEAKER: return &RNA_Speaker; case OB_LIGHTPROBE: return &RNA_LightProbe; + case OB_GPENCIL: return &RNA_GreasePencil; default: return &RNA_ID; } } +static bool rna_Object_data_poll(PointerRNA *ptr, const PointerRNA value) +{ + Object *ob = (Object *)ptr->data; + + if (ob->type == OB_GPENCIL) { + /* GP Object - Don't allow using "Annotation" GP datablocks here */ + bGPdata *gpd = value.data; + return (gpd->flag & GP_DATA_ANNOTATIONS) == 0; + } + + return true; +} + static void rna_Object_parent_set(PointerRNA *ptr, PointerRNA value) { Object *ob = (Object *)ptr->data; @@ -942,6 +968,21 @@ static void rna_MaterialSlot_material_set(PointerRNA *ptr, PointerRNA value) assign_material(G_MAIN, ob, value.data, index + 1, BKE_MAT_ASSIGN_EXISTING); } +static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value) +{ + Object *ob = (Object *)ptr->id.data; + Material *ma = (Material *)value.data; + + if (ob->type == OB_GPENCIL) { + /* GP Materials only */ + return (ma->gp_style != NULL); + } + else { + /* Everything except GP materials */ + return (ma->gp_style == NULL); + } +} + static int rna_MaterialSlot_link_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->id.data; @@ -1007,7 +1048,6 @@ static char *rna_MaterialSlot_path(PointerRNA *ptr) Object *ob = (Object *)ptr->id.data; int index = (Material **)ptr->data - ob->mat; - /* from armature... */ return BLI_sprintfN("material_slots[%d]", index); } @@ -1275,6 +1315,61 @@ bool rna_Object_modifiers_override_apply( return true; } +static GpencilModifierData *rna_Object_greasepencil_modifier_new( + Object *object, bContext *C, ReportList *reports, + const char *name, int type) +{ + return ED_object_gpencil_modifier_add(reports, CTX_data_main(C), CTX_data_scene(C), object, name, type); +} + +static void rna_Object_greasepencil_modifier_remove( + Object *object, bContext *C, ReportList *reports, PointerRNA *gmd_ptr) +{ + GpencilModifierData *gmd = gmd_ptr->data; + if (ED_object_gpencil_modifier_remove(reports, CTX_data_main(C), object, gmd) == false) { + /* error is already set */ + return; + } + + RNA_POINTER_INVALIDATE(gmd_ptr); + + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); +} + +static void rna_Object_greasepencil_modifier_clear(Object *object, bContext *C) +{ + ED_object_gpencil_modifier_clear(CTX_data_main(C), object); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); +} + +/* shader fx */ +static ShaderFxData *rna_Object_shaderfx_new( + Object *object, bContext *C, ReportList *reports, + const char *name, int type) +{ + return ED_object_shaderfx_add(reports, CTX_data_main(C), CTX_data_scene(C), object, name, type); +} + +static void rna_Object_shaderfx_remove( + Object *object, bContext *C, ReportList *reports, PointerRNA *gmd_ptr) +{ + ShaderFxData *gmd = gmd_ptr->data; + if (ED_object_shaderfx_remove(reports, CTX_data_main(C), object, gmd) == false) { + /* error is already set */ + return; + } + + RNA_POINTER_INVALIDATE(gmd_ptr); + + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); +} + +static void rna_Object_shaderfx_clear(Object *object, bContext *C) +{ + ED_object_shaderfx_clear(CTX_data_main(C), object); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); +} + static void rna_Object_boundbox_get(PointerRNA *ptr, float *values) { Object *ob = (Object *)ptr->id.data; @@ -1453,6 +1548,11 @@ bool rna_Light_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) return ((Object *)value.id.data)->type == OB_LAMP; } +bool rna_GPencil_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + return ((Object *)value.id.data)->type == OB_GPENCIL; +} + int rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr) { SculptSession *ss = ((Object *)ptr->id.data)->sculpt; @@ -1601,7 +1701,7 @@ static void rna_def_material_slot(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_editable_func(prop, "rna_MaterialSlot_material_editable"); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); - RNA_def_property_pointer_funcs(prop, "rna_MaterialSlot_material_get", "rna_MaterialSlot_material_set", NULL, NULL); + RNA_def_property_pointer_funcs(prop, "rna_MaterialSlot_material_get", "rna_MaterialSlot_material_set", NULL, "rna_MaterialSlot_material_poll"); RNA_def_property_ui_text(prop, "Material", "Material data-block used by this material slot"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_MaterialSlot_update"); @@ -1715,6 +1815,88 @@ static void rna_def_object_modifiers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_ui_description(func, "Remove all modifiers from the object"); } +/* object.grease_pencil_modifiers */ +static void rna_def_object_grease_pencil_modifiers(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "ObjectGpencilModifiers"); + srna = RNA_def_struct(brna, "ObjectGpencilModifiers", NULL); + RNA_def_struct_sdna(srna, "Object"); + RNA_def_struct_ui_text(srna, "Object Grease Pencil Modifiers", "Collection of object grease pencil modifiers"); + + /* add greasepencil modifier */ + func = RNA_def_function(srna, "new", "rna_Object_greasepencil_modifier_new"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Add a new greasepencil_modifier"); + parm = RNA_def_string(func, "name", "Name", 0, "", "New name for the greasepencil_modifier"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* greasepencil_modifier to add */ + parm = RNA_def_enum(func, "type", rna_enum_object_greasepencil_modifier_type_items, 1, "", "Modifier type to add"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* return type */ + parm = RNA_def_pointer(func, "greasepencil_modifier", "GpencilModifier", "", "Newly created modifier"); + RNA_def_function_return(func, parm); + + /* remove greasepencil_modifier */ + func = RNA_def_function(srna, "remove", "rna_Object_greasepencil_modifier_remove"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove an existing greasepencil_modifier from the object"); + /* greasepencil_modifier to remove */ + parm = RNA_def_pointer(func, "greasepencil_modifier", "GpencilModifier", "", "Modifier to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + /* clear all greasepencil modifiers */ + func = RNA_def_function(srna, "clear", "rna_Object_greasepencil_modifier_clear"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Remove all grease pencil modifiers from the object"); +} + +/* object.shaderfxs */ +static void rna_def_object_shaderfxs(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "ObjectShaderFx"); + srna = RNA_def_struct(brna, "ObjectShaderFx", NULL); + RNA_def_struct_sdna(srna, "Object"); + RNA_def_struct_ui_text(srna, "Object Shader Effects", "Collection of object effects"); + + /* add shader_fx */ + func = RNA_def_function(srna, "new", "rna_Object_shaderfx_new"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Add a new shader fx"); + parm = RNA_def_string(func, "name", "Name", 0, "", "New name for the effect"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* shader to add */ + parm = RNA_def_enum(func, "type", rna_enum_object_shaderfx_type_items, 1, "", "Effect type to add"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* return type */ + parm = RNA_def_pointer(func, "shader_fx", "ShaderFx", "", "Newly created effect"); + RNA_def_function_return(func, parm); + + /* remove shader_fx */ + func = RNA_def_function(srna, "remove", "rna_Object_shaderfx_remove"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS); + RNA_def_function_ui_description(func, "Remove an existing effect from the object"); + /* shader to remove */ + parm = RNA_def_pointer(func, "shader_fx", "ShaderFx", "", "Effect to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + /* clear all shader fx */ + func = RNA_def_function(srna, "clear", "rna_Object_shaderfx_clear"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Remove all effects from the object"); +} + /* object.particle_systems */ static void rna_def_object_particle_systems(BlenderRNA *brna, PropertyRNA *cprop) { @@ -1919,7 +2101,7 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "data", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "ID"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_Object_data_set", "rna_Object_data_typef", NULL); + RNA_def_property_pointer_funcs(prop, NULL, "rna_Object_data_set", "rna_Object_data_typef", "rna_Object_data_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); RNA_def_property_ui_text(prop, "Data", "Object data"); @@ -2017,7 +2199,8 @@ static void rna_def_object(BlenderRNA *brna) prop = RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Material"); RNA_def_property_pointer_funcs(prop, "rna_Object_active_material_get", - "rna_Object_active_material_set", NULL, NULL); + "rna_Object_active_material_set", NULL, + "rna_MaterialSlot_material_poll"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); RNA_def_property_editable_func(prop, "rna_Object_active_material_editable"); @@ -2211,6 +2394,20 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC | PROPOVERRIDE_STATIC_INSERTION); rna_def_object_modifiers(brna, prop); + /* Grease Pencil modifiers. */ + prop = RNA_def_property(srna, "grease_pencil_modifiers", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "greasepencil_modifiers", NULL); + RNA_def_property_struct_type(prop, "GpencilModifier"); + RNA_def_property_ui_text(prop, "Grease Pencil Modifiers", "Modifiers affecting the data of the grease pencil object"); + rna_def_object_grease_pencil_modifiers(brna, prop); + + /* Shader FX. */ + prop = RNA_def_property(srna, "shader_effects", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "shader_fx", NULL); + RNA_def_property_struct_type(prop, "ShaderFx"); + RNA_def_property_ui_text(prop, "Shader Effects", "Effects affecting display of object"); + rna_def_object_shaderfxs(brna, prop); + /* constraints */ prop = RNA_def_property(srna, "constraints", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Constraint"); @@ -2482,12 +2679,15 @@ static void rna_def_object(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); /* Grease Pencil */ +#if 1 /* FIXME: Remove this code when all Open-Movie assets have been fixed */ prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_obdata_poll"); /* XXX */ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block"); + RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block (deprecated)"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); +#endif /* pose */ prop = RNA_def_property(srna, "pose_library", PROP_POINTER, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_palette.c b/source/blender/makesrna/intern/rna_palette.c index 4d6b94bf709..547cac9f38d 100644 --- a/source/blender/makesrna/intern/rna_palette.c +++ b/source/blender/makesrna/intern/rna_palette.c @@ -39,7 +39,6 @@ #include "BKE_paint.h" #include "BKE_report.h" - static PaletteColor *rna_Palette_color_new(Palette *palette) { PaletteColor *color = BKE_palette_color_add(palette); @@ -139,6 +138,7 @@ static void rna_def_palettecolor(BlenderRNA *brna) prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rgb"); + RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Color", ""); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); @@ -153,6 +153,7 @@ static void rna_def_palettecolor(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "value"); RNA_def_property_ui_text(prop, "Weight", ""); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + } static void rna_def_palette(BlenderRNA *brna) @@ -167,6 +168,7 @@ static void rna_def_palette(BlenderRNA *brna) prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "PaletteColor"); rna_def_palettecolors(brna, prop); + } void RNA_def_palette(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 915018612a1..9fde87be486 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -51,6 +51,7 @@ #include "BKE_paint.h" #include "ED_object.h" +#include "ED_gpencil.h" #include "GPU_extensions.h" @@ -530,6 +531,13 @@ static const EnumPropertyItem transform_orientation_items[] = { #include "FRS_freestyle.h" #endif +/* Grease Pencil update cache */ +static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + DEG_id_type_tag(bmain, ID_GD); + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + /* Grease Pencil Interpolation settings */ static char *rna_GPencilInterpolateSettings_path(PointerRNA *UNUSED(ptr)) { @@ -551,110 +559,8 @@ static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value) { settings->custom_ipo = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); } + } - -/* Grease pencil Drawing Brushes */ -static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, bool setactive) -{ - bGPDbrush *brush = BKE_gpencil_brush_addnew(ts, name, setactive != 0); - - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return brush; -} - -static void rna_GPencil_brush_remove(ToolSettings *ts, ReportList *reports, PointerRNA *brush_ptr) -{ - bGPDbrush *brush = brush_ptr->data; - if (BLI_findindex(&ts->gp_brushes, brush) == -1) { - BKE_report(reports, RPT_ERROR, "Brush not found in grease pencil data"); - return; - } - - BKE_gpencil_brush_delete(ts, brush); - RNA_POINTER_INVALIDATE(brush_ptr); - - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -} - -static PointerRNA rna_GPencilBrushes_active_get(PointerRNA *ptr) -{ - ToolSettings *ts = (ToolSettings *) ptr->data; - - bGPDbrush *brush; - - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - if (brush->flag & GP_BRUSH_ACTIVE) { - break; - } - } - - if (brush) { - return rna_pointer_inherit_refine(ptr, &RNA_GPencilBrush, brush); - } - - return rna_pointer_inherit_refine(ptr, NULL, NULL); -} - -static void rna_GPencilBrushes_active_set(PointerRNA *ptr, PointerRNA value) -{ - ToolSettings *ts = (ToolSettings *) ptr->data; - - bGPDbrush *brush; - - for (brush = ts->gp_brushes.first; brush; brush = brush->next) { - if (brush == value.data) { - brush->flag |= GP_BRUSH_ACTIVE; - } - else { - brush->flag &= ~GP_BRUSH_ACTIVE; - } - } - WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); -} - -static int rna_GPencilBrushes_index_get(PointerRNA *ptr) -{ - ToolSettings *ts = (ToolSettings *) ptr->data; - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - return BLI_findindex(&ts->gp_brushes, brush); -} - -static void rna_GPencilBrushes_index_set(PointerRNA *ptr, int value) -{ - ToolSettings *ts = (ToolSettings *) ptr->data; - - bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, value); - - BKE_gpencil_brush_setactive(ts, brush); - WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -} - -static void rna_GPencilBrushes_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) -{ - ToolSettings *ts = (ToolSettings *) ptr->data; - - *min = 0; - *max = max_ii(0, BLI_listbase_count(&ts->gp_brushes) - 1); - - *softmin = *min; - *softmax = *max; -} - -static void rna_GPencilBrush_name_set(PointerRNA *ptr, const char *value) -{ - ToolSettings *ts = ((Scene *) ptr->id.data)->toolsettings; - bGPDbrush *brush = ptr->data; - - /* copy the new name into the name slot */ - BLI_strncpy_utf8(brush->info, value, sizeof(brush->info)); - - BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); -} - -/* ----------------- end of Grease pencil drawing brushes ------------*/ - static void rna_ToolSettings_gizmo_flag_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { ToolSettings *ts = scene->toolsettings; @@ -2190,205 +2096,6 @@ static void rna_def_gpencil_interpolate(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); } -/* Grease Pencil Drawing Brushes */ -static void rna_def_gpencil_brush(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "GPencilBrush", NULL); - RNA_def_struct_sdna(srna, "bGPDbrush"); - RNA_def_struct_ui_text(srna, "Grease Pencil Brush", - "Collection of brushes being used to control the line style of new strokes"); - RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); - - /* Name */ - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "info"); - RNA_def_property_ui_text(prop, "Name", "Brush name"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilBrush_name_set"); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Line Thickness */ - prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "thickness"); - RNA_def_property_range(prop, 1, 300); - RNA_def_property_ui_range(prop, 1, 10, 1, 0); - RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* 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_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Strength factor for new strokes */ - prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_strength"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Strength", "Color strength for new strokes (affect alpha factor of color)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Jitter factor for new strokes */ - prop = RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_jitter"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Randomnes factor for sensitivity and strength */ - prop = RNA_def_property(srna, "random_press", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_random_press"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Randomness", "Randomness factor for pressure and strength in new strokes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Randomnes factor for subdivision */ - prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_NONE); - 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_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"); - RNA_def_property_range(prop, -M_PI_2, M_PI_2); - RNA_def_property_ui_text(prop, "Angle", - "Direction of the stroke at which brush gives maximal thickness " - "(0° for horizontal)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Factor to change brush size depending of angle */ - prop = RNA_def_property(srna, "angle_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_angle_factor"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Angle Factor", - "Reduce brush thickness by this factor when stroke is perpendicular to 'Angle' direction"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Smoothing factor for new strokes */ - prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); - RNA_def_property_range(prop, 0.0, 2.0f); - RNA_def_property_ui_text(prop, "Smooth", - "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Iterations of the Smoothing factor */ - prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); - RNA_def_property_range(prop, 1, 3); - RNA_def_property_ui_text(prop, "Iterations", - "Number of times to smooth newly created strokes"); - 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, "sublevel"); - RNA_def_property_range(prop, 0, 3); - RNA_def_property_ui_text(prop, "Subdivision Steps", - "Number of times to subdivide newly created strokes, for less jagged strokes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Curves for pressure */ - prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "cur_sensitivity"); - RNA_def_property_struct_type(prop, "CurveMapping"); - RNA_def_property_ui_text(prop, "Curve Sensitivity", "Curve used for the sensitivity"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "curve_strength", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "cur_strength"); - RNA_def_property_struct_type(prop, "CurveMapping"); - RNA_def_property_ui_text(prop, "Curve Strength", "Curve used for the strength"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "curve_jitter", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "cur_jitter"); - RNA_def_property_struct_type(prop, "CurveMapping"); - RNA_def_property_ui_text(prop, "Curve Jitter", "Curve used for the jitter effect"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Flags */ - prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); - RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); - RNA_def_property_ui_text(prop, "Use Pressure", "Use tablet pressure"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "use_strength_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_STENGTH_PRESSURE); - RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); - RNA_def_property_ui_text(prop, "Use Pressure Strength", "Use tablet pressure for color strength"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "use_jitter_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_JITTER_PRESSURE); - RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); - RNA_def_property_ui_text(prop, "Use Pressure Jitter", "Use tablet pressure for jitter"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "use_random_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_PRESSURE); - RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); - RNA_def_property_ui_text(prop, "Random Pressure", "Use random value for pressure"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - prop = RNA_def_property(srna, "use_random_strength", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_STRENGTH); - RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); - RNA_def_property_ui_text(prop, "Random Strength", "Use random value for strength"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - -} - -/* Grease Pencil Drawing Brushes API */ -static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop) -{ - StructRNA *srna; - PropertyRNA *prop; - - FunctionRNA *func; - PropertyRNA *parm; - - RNA_def_property_srna(cprop, "GreasePencilBrushes"); - srna = RNA_def_struct(brna, "GreasePencilBrushes", NULL); - RNA_def_struct_sdna(srna, "ToolSettings"); - RNA_def_struct_ui_text(srna, "Grease Pencil Brushes", "Collection of grease pencil brushes"); - - func = RNA_def_function(srna, "new", "rna_GPencil_brush_new"); - RNA_def_function_ui_description(func, "Add a new grease pencil brush"); - parm = RNA_def_string(func, "name", "GPencilBrush", MAX_NAME, "Name", "Name of the brush"); - RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_boolean(func, "set_active", 0, "Set Active", "Set the newly created brush to the active brush"); - parm = RNA_def_pointer(func, "palette", "GPencilBrush", "", "The newly created brush"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_GPencil_brush_remove"); - RNA_def_function_ui_description(func, "Remove a grease pencil brush"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "brush", "GPencilBrush", "", "The brush to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); - - prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "GPencilBrush"); - RNA_def_property_pointer_funcs(prop, "rna_GPencilBrushes_active_get", "rna_GPencilBrushes_active_set", NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Active Brush", "Current active brush"); - - prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, - "rna_GPencilBrushes_index_get", - "rna_GPencilBrushes_index_set", - "rna_GPencilBrushes_index_range"); - RNA_def_property_ui_text(prop, "Active Brush Index", "Index of active brush"); -} - static void rna_def_transform_orientation(BlenderRNA *brna) { StructRNA *srna; @@ -2446,21 +2153,20 @@ static void rna_def_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; - static const EnumPropertyItem gpencil_source_3d_items[] = { - {GP_TOOL_SOURCE_SCENE, "SCENE", 0, "Scene", - "Grease Pencil data attached to the current scene is used, " - "unless the active object already has Grease Pencil data (i.e. for old files)"}, - {GP_TOOL_SOURCE_OBJECT, "OBJECT", 0, "Object", - "Grease Pencil data-blocks attached to the active object are used " - "(required when using pre 2.73 add-ons, e.g. BSurfaces)"}, + static const EnumPropertyItem gpencil_stroke_placement_items[] = { + {GP_PROJECT_VIEWSPACE, "ORIGIN", ICON_OBJECT_ORIGIN, "Origin", "Draw stroke at Object origin"}, + {GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR, "CURSOR", ICON_CURSOR, "3D Cursor", "Draw stroke at 3D cursor location" }, + // {0, "VIEW", ICON_VISIBLE_IPO_ON, "View", "Stick stroke to the view "}, /* weird, GP_PROJECT_VIEWALIGN is inverted */ + {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_VIEW, "SURFACE", ICON_FACESEL, "Surface", "Stick stroke to surfaces"}, + //{GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_STROKE, "STROKE", ICON_GREASEPENCIL, "Stroke", "Stick stroke to other strokes"}, {0, NULL, 0, NULL, NULL} }; - static const EnumPropertyItem gpencil_stroke_placement_items[] = { - {GP_PROJECT_VIEWSPACE, "CURSOR", 0, "Cursor", "Draw stroke at the 3D cursor"}, - {0, "VIEW", 0, "View", "Stick stroke to the view "}, /* weird, GP_PROJECT_VIEWALIGN is inverted */ - {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_VIEW, "SURFACE", 0, "Surface", "Stick stroke to surfaces"}, - {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_STROKE, "STROKE", 0, "Stroke", "Stick stroke to other strokes"}, + static const EnumPropertyItem annotation_stroke_placement_items[] = { + {GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR, "CURSOR", ICON_CURSOR, "3D Cursor", "Draw stroke at 3D cursor location" }, + {0, "VIEW", ICON_VISIBLE_IPO_ON, "View", "Stick stroke to the view "}, /* weird, GP_PROJECT_VIEWALIGN is inverted */ + {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_VIEW, "SURFACE", ICON_FACESEL, "Surface", "Stick stroke to surfaces"}, + {GP_PROJECT_VIEWSPACE | GP_PROJECT_DEPTH_STROKE, "STROKE", ICON_GREASEPENCIL, "Stroke", "Stick stroke to other strokes"}, {0, NULL, 0, NULL, NULL} }; @@ -2504,7 +2210,8 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data"); prop = RNA_def_property(srna, "vertex_paint", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "vpaint"); RNA_def_property_ui_text(prop, "Vertex Paint", ""); + RNA_def_property_pointer_sdna(prop, NULL, "vpaint"); + RNA_def_property_ui_text(prop, "Vertex Paint", ""); prop = RNA_def_property(srna, "weight_paint", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "wpaint"); @@ -2518,6 +2225,10 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "uvsculpt"); RNA_def_property_ui_text(prop, "UV Sculpt", ""); + prop = RNA_def_property(srna, "gpencil_paint", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_paint"); + RNA_def_property_ui_text(prop, "Grease Pencil 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", ""); @@ -2695,12 +2406,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ToolSettings_gizmo_flag_update"); /* Grease Pencil */ - prop = RNA_def_property(srna, "use_gpencil_continuous_drawing", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_PAINTSESSIONS_ON); - RNA_def_property_ui_text(prop, "Use Continuous Drawing", - "Allow drawing multiple strokes at a time with Grease Pencil"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* xxx: need toolbar to be redrawn... */ - prop = RNA_def_property(srna, "use_gpencil_additive_drawing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_RETAIN_LAST); RNA_def_property_ui_text(prop, "Use Additive Drawing", @@ -2714,12 +2419,11 @@ static void rna_def_tool_settings(BlenderRNA *brna) "When draw new strokes, the new stroke is drawn below of all strokes in the layer"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "grease_pencil_source", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_src"); - RNA_def_property_enum_items(prop, gpencil_source_3d_items); - RNA_def_property_ui_text(prop, "Grease Pencil Source", - "Data-block where active Grease Pencil data is found from"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + prop = RNA_def_property(srna, "use_gpencil_thumbnail_list", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_THUMBNAIL_LIST); + RNA_def_property_ui_text(prop, "Compact List", + "Show compact list of color instead of thumbnails"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "gpencil_sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt"); @@ -2733,13 +2437,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grease Pencil Interpolate", "Settings for Grease Pencil Interpolation tools"); - /* Grease Pencil - Drawing brushes */ - prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "gp_brushes", NULL); - RNA_def_property_struct_type(prop, "GPencilBrush"); - RNA_def_property_ui_text(prop, "Grease Pencil Brushes", "Grease Pencil drawing brushes"); - rna_def_gpencil_brushes(brna, prop); - /* Grease Pencil - 3D View Stroke Placement */ prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v3d_align"); @@ -2752,27 +2449,41 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Only Endpoints", "Only use the first and last parts of the stroke for snapping"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Grease Pencil - 2D Views Stroke Placement */ - prop = RNA_def_property(srna, "gpencil_stroke_placement_view2d", PROP_ENUM, PROP_NONE); + /* 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"); - RNA_def_property_enum_items(prop, gpencil_stroke_placement_items); + RNA_def_property_enum_items(prop, annotation_stroke_placement_items); RNA_def_property_ui_text(prop, "Stroke Placement (2D View)", ""); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Grease Pencil - Sequencer Preview Stroke Placement */ - prop = RNA_def_property(srna, "gpencil_stroke_placement_sequencer_preview", PROP_ENUM, PROP_NONE); + /* Annotations - Sequencer Preview Stroke Placement */ + prop = RNA_def_property(srna, "annotation_stroke_placement_sequencer_preview", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_seq_align"); - RNA_def_property_enum_items(prop, gpencil_stroke_placement_items); + RNA_def_property_enum_items(prop, annotation_stroke_placement_items); RNA_def_property_ui_text(prop, "Stroke Placement (Sequencer Preview)", ""); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Grease Pencil - Image Editor Stroke Placement */ - prop = RNA_def_property(srna, "gpencil_stroke_placement_image_editor", PROP_ENUM, PROP_NONE); + /* Annotations - Image Editor Stroke Placement */ + prop = RNA_def_property(srna, "annotation_stroke_placement_image_editor", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_ima_align"); - RNA_def_property_enum_items(prop, gpencil_stroke_placement_items); + RNA_def_property_enum_items(prop, annotation_stroke_placement_items); RNA_def_property_ui_text(prop, "Stroke Placement (Image Editor)", ""); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + /* Annotations - 3D View Stroke Placement */ + /* XXX: Do we need to decouple the stroke_endpoints setting too? */ + prop = RNA_def_property(srna, "annotation_stroke_placement_view3d", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "annotate_v3d_align"); + RNA_def_property_enum_items(prop, annotation_stroke_placement_items); + RNA_def_property_ui_text(prop, "Annotation Stroke Placement (3D View)", "How annotation strokes are orientated in 3D space"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Annotations - Stroke Thickness */ + prop = RNA_def_property(srna, "annotation_thickness", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "annotate_thickness"); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_text(prop, "Annotation Stroke Thickness", "Thickness of annotation strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Auto Keying */ prop = RNA_def_property(srna, "use_keyframe_insert_auto", PROP_BOOLEAN, PROP_NONE); @@ -5474,6 +5185,32 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Simplify Child Particles", "Global child particles percentage during rendering"); RNA_def_property_update(prop, 0, "rna_Scene_simplify_update"); + /* Grease Pencil - Simplify Options */ + prop = RNA_def_property(srna, "simplify_gpencil", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_ENABLE); + RNA_def_property_ui_text(prop, "Simplify", "Simplify Grease Pencil Drawing"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + 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, "On Play", "Simplify Grease Pencil only when play animation"); + 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); + RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FILL); + RNA_def_property_ui_text(prop, "Fill", "Do not fill strokes on 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_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_REMOVE_FILL_LINE); + RNA_def_property_ui_text(prop, "Remove Lines", "Remove External Lines of Filling 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); + RNA_def_property_boolean_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_MODIFIER); + RNA_def_property_ui_text(prop, "Fill", "Do not apply modifiers on viewport"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* persistent data */ prop = RNA_def_property(srna, "use_persistent_data", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "mode", R_PERSISTENT_DATA); @@ -6550,8 +6287,9 @@ void RNA_def_scene(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block"); + RNA_def_property_ui_text(prop, "Annotations", "Grease Pencil data-block used for annotations in the 3D view"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); /* active MovieClip */ @@ -6606,7 +6344,6 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Non-Animated *** */ RNA_define_animate_sdna(false); rna_def_tool_settings(brna); - rna_def_gpencil_brush(brna); rna_def_gpencil_interpolate(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 5b2a3c9c4f4..6a6c97b41ad 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -64,26 +64,29 @@ static const EnumPropertyItem particle_edit_hair_brush_items[] = { }; const EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { - {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"}, - {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"}, - { GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" }, - { GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" }, - {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them"}, - {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush"}, - {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush"}, - {GP_EDITBRUSH_TYPE_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Introduce jitter/randomness into strokes"}, - //{GP_EDITBRUSH_TYPE_SUBDIVIDE, "SUBDIVIDE", 0, "Subdivide", "Increase point density for higher resolution strokes when zoomed in"}, - //{GP_EDITBRUSH_TYPE_SIMPLIFY, "SIMPLIFY", 0, "Simplify", "Reduce density of stroke points"}, - {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard"}, + {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", "Smooth stroke points"}, + {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", "Adjust thickness of strokes"}, + {GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", "Adjust color strength of strokes" }, + {GP_EDITBRUSH_TYPE_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", "Translate the set of points initially within the brush circle" }, + {GP_EDITBRUSH_TYPE_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", "Move points out of the way, as if combing them"}, + {GP_EDITBRUSH_TYPE_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", "Rotate points around the midpoint of the brush"}, + {GP_EDITBRUSH_TYPE_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", "Pull points towards the midpoint of the brush"}, + {GP_EDITBRUSH_TYPE_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", "Introduce jitter/randomness into strokes"}, + {GP_EDITBRUSH_TYPE_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", "Paste copies of the strokes stored on the clipboard"}, + { 0, NULL, 0, NULL, NULL } +}; + +EnumPropertyItem rna_enum_gpencil_weight_brush_items[] = { + { GP_EDITBRUSH_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_lockaxis_items[] = { - { GP_LOCKAXIS_NONE, "GP_LOCKAXIS_NONE", 0, "None", "" }, - { GP_LOCKAXIS_X, "GP_LOCKAXIS_X", 0, "X", "Project strokes to plane locked to X" }, - { GP_LOCKAXIS_Y, "GP_LOCKAXIS_Y", 0, "Y", "Project strokes to plane locked to Y" }, - { GP_LOCKAXIS_Z, "GP_LOCKAXIS_Z", 0, "Z", "Project strokes to plane locked to Z" }, + { GP_LOCKAXIS_NONE, "GP_LOCKAXIS_NONE", ICON_UNLOCKED, "None", "" }, + { GP_LOCKAXIS_X, "GP_LOCKAXIS_X", ICON_NDOF_DOM, "X", "Project strokes to plane locked to X" }, + { GP_LOCKAXIS_Y, "GP_LOCKAXIS_Y", ICON_NDOF_DOM, "Y", "Project strokes to plane locked to Y" }, + { GP_LOCKAXIS_Z, "GP_LOCKAXIS_Z", ICON_NDOF_DOM, "Z", "Project strokes to plane locked to Z" }, { 0, NULL, 0, NULL, NULL } }; #endif @@ -108,13 +111,16 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = { #include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_object.h" +#include "BKE_gpencil.h" + #include "DEG_depsgraph.h" #include "ED_particle.h" -static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { + DEG_id_type_tag(bmain, ID_GD); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } @@ -265,6 +271,8 @@ static bool rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value) mode = OB_MODE_VERTEX_PAINT; else if (ptr->data == ts->wpaint) mode = OB_MODE_WEIGHT_PAINT; + else if (ptr->data == ts->gp_paint) + mode = OB_MODE_GPENCIL_PAINT; return brush->ob_mode & mode; } @@ -346,6 +354,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.uv_sculpt"); } +static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gp_paint"); +} + static char *rna_ParticleBrush_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.particle_edit.brush"); @@ -435,9 +448,14 @@ static PointerRNA rna_GPencilSculptSettings_brush_get(PointerRNA *ptr) GP_BrushEdit_Settings *gset = (GP_BrushEdit_Settings *)ptr->data; GP_EditBrush_Data *brush = NULL; - if ((gset->brushtype >= 0) && (gset->brushtype < TOT_GP_EDITBRUSH_TYPES)) - brush = &gset->brush[gset->brushtype]; - + if ((gset) && (gset->flag & GP_BRUSHEDIT_FLAG_WEIGHT_MODE)) { + if ((gset->weighttype >= GP_EDITBRUSH_TYPE_WEIGHT) && (gset->weighttype < TOT_GP_EDITBRUSH_TYPES)) + brush = &gset->brush[gset->weighttype]; + } + else { + if ((gset->brushtype >= 0) && (gset->brushtype < GP_EDITBRUSH_TYPE_WEIGHT)) + brush = &gset->brush[gset->brushtype]; + } return rna_pointer_inherit_refine(ptr, &RNA_GPencilSculptBrush, brush); } @@ -708,6 +726,14 @@ static void rna_def_uv_sculpt(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "UV Sculpting", ""); } +static void rna_def_gp_paint(BlenderRNA *brna) +{ + StructRNA *srna; + + 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 for weight paint too */ static void rna_def_vertex_paint(BlenderRNA *brna) @@ -1059,8 +1085,8 @@ static void rna_def_particle_edit(BlenderRNA *brna) static void rna_def_gpencil_sculpt(BlenderRNA *brna) { static const EnumPropertyItem prop_direction_items[] = { - {0, "ADD", 0, "Add", "Add effect of brush"}, - {GP_EDITBRUSH_FLAG_INVERT, "SUBTRACT", 0, "Subtract", "Subtract effect of brush"}, + {0, "ADD", ICON_ZOOMIN, "Add", "Add effect of brush"}, + {GP_EDITBRUSH_FLAG_INVERT, "SUBTRACT", ICON_ZOOMOUT, "Subtract", "Subtract effect of brush"}, {0, NULL, 0, NULL, NULL}}; StructRNA *srna; @@ -1076,86 +1102,148 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) 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_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_ui_text(prop, "Brush", ""); prop = RNA_def_property(srna, "use_select_mask", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_SELECT_MASK); RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points"); RNA_def_property_ui_icon(prop, ICON_VERTEXSEL, 0); // FIXME: this needs a custom icon + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_POSITION); RNA_def_property_ui_text(prop, "Affect Position", "The brush affects the position of the point"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_STRENGTH); RNA_def_property_ui_text(prop, "Affect Strength", "The brush affects the color strength of the point"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_THICKNESS); RNA_def_property_ui_text(prop, "Affect Thickness", "The brush affects the thickness of the point"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "affect_uv", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_UV); + RNA_def_property_ui_text(prop, "Affect UV", "The brush affects the UV rotation of the point"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "alpha"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Alpha", "Alpha value for selected vertices"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); + prop = RNA_def_property(srna, "use_multiframe_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_FRAME_FALLOFF); + RNA_def_property_ui_text(prop, "Use Falloff", "Use falloff effect when edit in multiframe mode to compute brush effect by frame"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + 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"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve", + "Custom curve to control falloff of brush effect by Grease Pencil frames"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* lock axis */ prop = RNA_def_property(srna, "lockaxis", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "lock_axis"); RNA_def_property_enum_items(prop, rna_enum_gpencil_lockaxis_items); RNA_def_property_ui_text(prop, "Lock", ""); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* brush */ srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL); RNA_def_struct_sdna(srna, "GP_EditBrush_Data"); RNA_def_struct_path_func(srna, "rna_GPencilSculptBrush_path"); RNA_def_struct_ui_text(srna, "GPencil Sculpt Brush", "Stroke editing brush"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); - RNA_def_property_range(prop, 1, MAX_BRUSH_PIXEL_RADIUS); - RNA_def_property_ui_range(prop, 1, 100, 10, 3); // XXX: too big + 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); 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_EDITBRUSH_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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); 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_EDITBRUSH_FLAG_USE_FALLOFF); RNA_def_property_ui_text(prop, "Use Falloff", "Strength of brush decays with distance from cursor"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "affect_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE); RNA_def_property_ui_text(prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); 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_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + 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 substration"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + + prop = RNA_def_property(srna, "use_cursor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_EDITBRUSH_FLAG_ENABLE_CURSOR); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Enable Cursor", "Enable cursor on screen"); + RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0); + } void RNA_def_sculpt_paint(BlenderRNA *brna) @@ -1166,6 +1254,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) rna_def_paint(brna); rna_def_sculpt(brna); rna_def_uv_sculpt(brna); + rna_def_gp_paint(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 new file mode 100644 index 00000000000..4956333b202 --- /dev/null +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -0,0 +1,538 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/makesrna/intern/rna_shader_fx.c + * \ingroup RNA + */ + + +#include +#include +#include + +#include "DNA_shader_fx_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "BKE_animsys.h" +#include "BKE_shader_fx.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#include "WM_api.h" +#include "WM_types.h" + +const EnumPropertyItem rna_enum_object_shaderfx_type_items[] = { + {eShaderFxType_Blur, "FX_BLUR", ICON_SOLO_ON, "Blur", "Apply Gaussian Blur to object" }, + {eShaderFxType_Colorize, "FX_COLORIZE", ICON_SOLO_ON, "Colorize", "Apply different tint effects" }, + {eShaderFxType_Flip, "FX_FLIP", ICON_SOLO_ON, "Flip", "Flip image" }, + {eShaderFxType_Light, "FX_LIGHT", ICON_SOLO_ON, "Light", "Simulate ilumination" }, + {eShaderFxType_Pixel, "FX_PIXEL", ICON_SOLO_ON, "Pixelate", "Pixelate image"}, + {eShaderFxType_Rim, "FX_RIM", ICON_SOLO_ON, "Rim", "Add a rim to the image" }, + {eShaderFxType_Swirl, "FX_SWIRL", ICON_SOLO_ON, "Swirl", "Create a rotation distortion"}, + {eShaderFxType_Wave, "FX_WAVE", ICON_SOLO_ON, "Wave Distortion", "Apply sinusoidal deformation"}, + {0, NULL, 0, NULL, NULL} +}; + +const EnumPropertyItem rna_enum_shaderfx_rim_modes_items[] = { + {eShaderFxRimMode_Normal, "NORMAL", 0, "Normal", "" }, + {eShaderFxRimMode_Overlay, "OVERLAY", 0, "Overlay", "" }, + {eShaderFxRimMode_Add, "ADD", 0, "Add", "" }, + {eShaderFxRimMode_Subtract, "SUBTRACT", 0, "Subtract", "" }, + {eShaderFxRimMode_Multiply, "MULTIPLY", 0, "Multiply", "" }, + {eShaderFxRimMode_Divide, "DIVIDE", 0, "Divide", "" }, + {0, NULL, 0, NULL, NULL } +}; + +const EnumPropertyItem rna_enum_shaderfx_colorize_modes_items[] = { + {eShaderFxColorizeMode_GrayScale, "GRAYSCALE", 0, "Gray Scale", "" }, + {eShaderFxColorizeMode_Sepia, "SEPIA", 0, "Sepia", "" }, + {eShaderFxColorizeMode_BiTone, "BITONE", 0, "Bi-Tone", "" }, + {eShaderFxColorizeMode_Transparent, "TRANSPARENT", 0, "Transparent", "" }, + {eShaderFxColorizeMode_Custom, "CUSTOM", 0, "Custom", "" }, + {0, NULL, 0, NULL, NULL } +}; + +#ifdef RNA_RUNTIME + +#include "BKE_shader_fx.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +static StructRNA *rna_ShaderFx_refine(struct PointerRNA *ptr) +{ + ShaderFxData *md = (ShaderFxData *)ptr->data; + + switch ((ShaderFxType)md->type) { + case eShaderFxType_Blur: + return &RNA_ShaderFxBlur; + case eShaderFxType_Colorize: + return &RNA_ShaderFxColorize; + case eShaderFxType_Wave: + return &RNA_ShaderFxWave; + case eShaderFxType_Pixel: + return &RNA_ShaderFxPixel; + case eShaderFxType_Rim: + return &RNA_ShaderFxRim; + case eShaderFxType_Swirl: + return &RNA_ShaderFxSwirl; + case eShaderFxType_Flip: + return &RNA_ShaderFxFlip; + case eShaderFxType_Light: + return &RNA_ShaderFxLight; + /* Default */ + case eShaderFxType_None: + case NUM_SHADER_FX_TYPES: + return &RNA_ShaderFx; + } + + return &RNA_ShaderFx; +} + +static void rna_ShaderFx_name_set(PointerRNA *ptr, const char *value) +{ + ShaderFxData *gmd = ptr->data; + char oldname[sizeof(gmd->name)]; + + /* make a copy of the old name first */ + BLI_strncpy(oldname, gmd->name, sizeof(gmd->name)); + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(gmd->name, value, sizeof(gmd->name)); + + /* make sure the name is truly unique */ + if (ptr->id.data) { + Object *ob = ptr->id.data; + BKE_shaderfx_unique_name(&ob->shader_fx, gmd); + } + + /* fix all the animation data which may link to this */ + BKE_animdata_fix_paths_rename_all(NULL, "shader_effects", oldname, gmd->name); +} + +static char *rna_ShaderFx_path(PointerRNA *ptr) +{ + ShaderFxData *gmd = ptr->data; + char name_esc[sizeof(gmd->name) * 2]; + + BLI_strescape(name_esc, gmd->name, sizeof(name_esc)); + return BLI_sprintfN("shader_effects[\"%s\"]", name_esc); +} + +static void rna_ShaderFx_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + DEG_id_tag_update(ptr->id.data, OB_RECALC_DATA); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NC_GPENCIL, ptr->id.data); +} + +static void rna_ShaderFx_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_ShaderFx_update(bmain, scene, ptr); + DEG_relations_tag_update(bmain); +} + +/* Objects */ + +static void shaderfx_object_set(Object *self, Object **ob_p, int type, PointerRNA value) +{ + Object *ob = value.data; + + if (!self || ob != self) { + if (!ob || type == OB_EMPTY || ob->type == type) { + id_lib_extern((ID *)ob); + *ob_p = ob; + } + } +} + +#define RNA_FX_OBJECT_SET(_type, _prop, _obtype) \ +static void rna_##_type##ShaderFx_##_prop##_set(PointerRNA *ptr, PointerRNA value) \ +{ \ + _type##ShaderFxData *tmd = (_type##ShaderFxData *)ptr->data; \ + shaderfx_object_set(ptr->id.data, &tmd->_prop, _obtype, value); \ +} + +RNA_FX_OBJECT_SET(Light, object, OB_EMPTY); +RNA_FX_OBJECT_SET(Swirl, object, OB_EMPTY); + +#undef RNA_FX_OBJECT_SET + +#else + +static void rna_def_shader_fx_blur(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxBlur", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Gaussian Blur Effect", "Gaussian Blur effect"); + RNA_def_struct_sdna(srna, "BlurShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "factor", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Factor", "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); + RNA_def_property_int_sdna(prop, NULL, "samples"); + RNA_def_property_range(prop, 0, 32); + RNA_def_property_ui_range(prop, 0, 32, 2, -1); + RNA_def_property_int_default(prop, 4); + 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"); + 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_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); +} + +static void rna_def_shader_fx_colorize(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxColorize", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Colorize Effect", "Colorize effect"); + RNA_def_struct_sdna(srna, "ColorizeShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "factor"); + RNA_def_property_range(prop, 0, 1.0); + 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); + 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); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "high_color"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Hight color", "Second color used for effect"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, rna_enum_shaderfx_colorize_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Effect mode"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); +} + +static void rna_def_shader_fx_wave(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem prop_shaderfx_wave_type_items[] = { + { 0, "HORIZONTAL", 0, "Horizontal", "" }, + { 1, "VERTICAL", 0, "Vertical", "" }, + { 0, NULL, 0, NULL, NULL } + }; + + srna = RNA_def_struct(brna, "ShaderFxWave", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Wave Deformation Effect", "Wave Deformation effect"); + RNA_def_struct_sdna(srna, "WaveShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "orientation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "orientation"); + RNA_def_property_enum_items(prop, prop_shaderfx_wave_type_items); + RNA_def_property_ui_text(prop, "Orientation", "Direction of the wave"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "amplitude"); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of Wave"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "period"); + RNA_def_property_range(prop, 0, FLT_MAX); + RNA_def_property_ui_text(prop, "Period", "Period of Wave"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "phase"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); +} + +static void rna_def_shader_fx_pixel(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxPixel", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Pixelate Effect", "Pixelate effect"); + RNA_def_struct_sdna(srna, "PixelShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "size"); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_array(prop, 2); + 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); + 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) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxRim", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Rim Effect", "Rim effect"); + RNA_def_struct_sdna(srna, "RimShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "offset", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "offset"); + RNA_def_property_range(prop, -INT_MAX, INT_MAX); + 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); + 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); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "mask_rgb"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Mask Color", "Color that must be keept"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, rna_enum_shaderfx_rim_modes_items); + RNA_def_property_ui_text(prop, "Mode", "Blend mode"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "blur", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "blur"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Blur", "Number of pixels for bluring rim (set to 0 to disable)"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "samples"); + RNA_def_property_range(prop, 0, 32); + RNA_def_property_ui_range(prop, 0, 32, 2, -1); + RNA_def_property_int_default(prop, 4); + 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"); +} + +static void rna_def_shader_fx_swirl(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxSwirl", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Swirl Effect", "Swirl effect"); + RNA_def_struct_sdna(srna, "SwirlShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "radius", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "radius"); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Radius", "Radius to apply"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "angle"); + RNA_def_property_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360)); + RNA_def_property_ui_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360), 5, 2); + RNA_def_property_ui_text(prop, "Angle", "Angle of rotation"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "transparent", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SWIRL_MAKE_TRANSPARENT); + RNA_def_property_ui_text(prop, "Transparent", "Make image transparent outside of radius"); + 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 center location"); + RNA_def_property_pointer_funcs(prop, NULL, "rna_SwirlShaderFx_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update"); +} + +static void rna_def_shader_fx_flip(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ShaderFxFlip", "ShaderFx"); + RNA_def_struct_ui_text(srna, "Flip Effect", "Flip effect"); + RNA_def_struct_sdna(srna, "FlipShaderFxData"); + RNA_def_struct_ui_icon(srna, ICON_SOLO_ON); + + prop = RNA_def_property(srna, "flip_horizontal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_HORIZONTAL); + RNA_def_property_ui_text(prop, "Horizontal", "Flip image horizontally"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + + prop = RNA_def_property(srna, "flip_vertical", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_VERTICAL); + RNA_def_property_ui_text(prop, "Vertical", "Flip image vertically"); + 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_SOLO_ON); + + 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_update(prop, 0, "rna_ShaderFx_dependency_update"); +} + +void RNA_def_shader_fx(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + /* data */ + srna = RNA_def_struct(brna, "ShaderFx", NULL); + RNA_def_struct_ui_text(srna, "ShaderFx", "Effect affecting the grease pencil object"); + RNA_def_struct_refine_func(srna, "rna_ShaderFx_refine"); + RNA_def_struct_path_func(srna, "rna_ShaderFx_path"); + RNA_def_struct_sdna(srna, "ShaderFxData"); + + /* strings */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_ShaderFx_name_set"); + RNA_def_property_ui_text(prop, "Name", "Effect name"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + /* enums */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, rna_enum_object_shaderfx_type_items); + RNA_def_property_ui_text(prop, "Type", ""); + + /* flags */ + prop = RNA_def_property(srna, "show_viewport", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Realtime); + RNA_def_property_ui_text(prop, "Realtime", "Display effect in viewport"); + RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 0); + + prop = RNA_def_property(srna, "show_render", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Render); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_ui_text(prop, "Render", "Use effect during render"); + RNA_def_property_ui_icon(prop, ICON_SCENE, 0); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL); + + prop = RNA_def_property(srna, "show_in_editmode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Editmode); + RNA_def_property_ui_text(prop, "Edit Mode", "Display effect in Edit mode"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_ui_icon(prop, ICON_EDITMODE_HLT, 0); + + prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Expanded); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + RNA_def_property_ui_text(prop, "Expanded", "Set effect expanded in the user interface"); + RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); + + /* types */ + rna_def_shader_fx_blur(brna); + rna_def_shader_fx_colorize(brna); + rna_def_shader_fx_wave(brna); + rna_def_shader_fx_pixel(brna); + rna_def_shader_fx_rim(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 2f009238851..56491fd70e4 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "DNA_action_types.h" +#include "DNA_gpencil_types.h" #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" @@ -288,6 +289,7 @@ static const EnumPropertyItem buttons_context_items[] = { {BCONTEXT_PARTICLE, "PARTICLES", ICON_PARTICLES, "Particles", "Particle"}, {BCONTEXT_PHYSICS, "PHYSICS", ICON_PHYSICS, "Physics", "Physics"}, {BCONTEXT_WORKSPACE, "WORKSPACE", ICON_SPLITSCREEN, "Workspace", "Workspace"}, + {BCONTEXT_SHADERFX, "SHADERFX", ICON_SOLO_ON, "Effects", "Object visual effects" }, {0, NULL, 0, NULL, NULL} }; @@ -308,6 +310,14 @@ const EnumPropertyItem rna_enum_file_sort_items[] = { {0, NULL, 0, NULL, NULL} }; +static const EnumPropertyItem rna_enum_gpencil_grid_axis_items[] = { + {V3D_GP_GRID_AXIS_LOCK, "LOCK", 0, "Lock", "Use current drawing locked axis" }, + {V3D_GP_GRID_AXIS_X, "X", 0, "X", ""}, + {V3D_GP_GRID_AXIS_Y, "Y", 0, "Y", ""}, + {V3D_GP_GRID_AXIS_Z, "Z", 0, "Z", ""}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "DNA_anim_types.h" @@ -475,6 +485,17 @@ static void rna_Space_view2d_sync_update(Main *UNUSED(bmain), Scene *UNUSED(scen } } +static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + /* need set all caches as dirty to recalculate onion skinning */ + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->type == OB_GPENCIL) { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + } + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + /* Space 3D View */ static void rna_SpaceView3D_camera_update(Main *bmain, Scene *scene, PointerRNA *ptr) { @@ -1338,6 +1359,10 @@ static const EnumPropertyItem *rna_SpaceProperties_context_itemf( RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_MODIFIER); } + if (sbuts->pathflag & (1 << BCONTEXT_SHADERFX)) { + RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_SHADERFX); + } + if (sbuts->pathflag & (1 << BCONTEXT_DATA)) { RNA_enum_items_add_value(&item, &totitem, buttons_context_items, BCONTEXT_DATA); (item + totitem - 1)->icon = sbuts->dataicon; @@ -2613,7 +2638,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "show_overlays", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag2", V3D_RENDER_OVERRIDE); RNA_def_property_ui_text(prop, "Show Overlays", "Display overlays like gizmos and outlines"); - 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, "show_floor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gridflag", V3D_SHOW_FLOOR); @@ -2831,6 +2856,86 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Weight Paint Opacity", "Opacity of the weight paint mode overlay"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* grease pencil paper settings */ + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SHOW_ANNOTATION); + 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, "flag3", V3D_GP_SHOW_PAPER); + RNA_def_property_ui_text(prop, "Use Paper", + "Cover all viewport with a full color layer to improve visibility while drawing over complex scenes"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_gpencil_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag3", V3D_GP_SHOW_GRID); + RNA_def_property_ui_text(prop, "Use Grid", + "Draw a grid over grease pencil paper"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "gpencil_grid_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_grid_scale"); + RNA_def_property_range(prop, 0.01f, FLT_MAX); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_ui_text(prop, "Scale", "Grid scale"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "gpencil_grid_lines", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "overlay.gpencil_grid_lines"); + RNA_def_property_range(prop, 1, INT_MAX); + RNA_def_property_int_default(prop, GP_DEFAULT_GRID_LINES); + RNA_def_property_ui_text(prop, "Subdivisions", "Number of subdivisions in each side of symmetry line"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "gpencil_grid_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "overlay.gpencil_grid_axis"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_grid_axis_items); + RNA_def_property_ui_text(prop, "Axis", "Axis to display grid"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + 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); + RNA_def_property_float_default(prop, 0.9f); + RNA_def_property_ui_text(prop, "Opacity", "Grid opacity"); + 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); + RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_paper_opacity"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Opacity", "Paper opacity"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* show edit lines */ + prop = RNA_def_property(srna, "use_gpencil_edit_lines", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag3", V3D_GP_SHOW_EDIT_LINES); + RNA_def_property_ui_text(prop, "Edit Lines", "Show edit lines when edit 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, "flag3", V3D_GP_SHOW_MULTIEDIT_LINES); + RNA_def_property_ui_text(prop, "Lines Only", "Show only edit lines for additional frames"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + + /* main grease pencil onion switch */ + prop = RNA_def_property(srna, "use_gpencil_onion_skin", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag3", V3D_GP_SHOW_ONION_SKIN); + RNA_def_property_ui_text(prop, "Onion Skins", "Show ghosts of the frames before and after the current frame"); + 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); + 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"); + } static void rna_def_space_view3d(BlenderRNA *brna) @@ -2957,12 +3062,6 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Clip End", "3D View far clipping distance"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "show_grease_pencil", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Grease Pencil", - "Show grease pencil for this view"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "show_textured_solid", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SOLID_TEX); RNA_def_property_ui_text(prop, "Textured Solid", "Display face-assigned textures in solid view"); @@ -3143,6 +3242,8 @@ static void rna_def_space_view3d(BlenderRNA *brna) {"show_object_viewport_lattice", "show_object_select_lattice"}}, {"Empty", (1 << OB_EMPTY), {"show_object_viewport_empty", "show_object_select_empty"}}, + {"Grease Pencil", (1 << OB_GPENCIL), + {"show_object_viewport_grease_pencil", "show_object_select_grease_pencil"}}, {"Camera", (1 << OB_CAMERA), {"show_object_viewport_camera", "show_object_select_camera"}}, {"Light", (1 << OB_LAMP), @@ -3382,10 +3483,10 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw Repeated", "Draw the image repeated outside of the main view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); - prop = RNA_def_property(srna, "show_grease_pencil", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Grease Pencil", - "Show grease pencil for this view"); + RNA_def_property_ui_text(prop, "Show Annotation", + "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); prop = RNA_def_property(srna, "draw_channels", PROP_ENUM, PROP_NONE); @@ -3434,6 +3535,7 @@ static void rna_def_space_image(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this space"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); @@ -3587,10 +3689,10 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_grease_pencil", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Grease Pencil", - "Show grease pencil for this view"); + RNA_def_property_ui_text(prop, "Show Annotation", + "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); prop = RNA_def_property(srna, "display_channel", PROP_INT, PROP_NONE); @@ -3629,8 +3731,9 @@ static void rna_def_space_sequencer(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this space"); + RNA_def_property_ui_text(prop, "Grease Pencil", "Grease Pencil data for this Preview region"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); prop = RNA_def_property(srna, "overlay_type", PROP_ENUM, PROP_NONE); @@ -4734,10 +4837,10 @@ static void rna_def_space_node(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Backdrop", "Use active Viewer Node output as backdrop for compositing nodes"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, "rna_SpaceNodeEditor_show_backdrop_update"); - prop = RNA_def_property(srna, "show_grease_pencil", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SNODE_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Grease Pencil", - "Show grease pencil for this view"); + RNA_def_property_ui_text(prop, "Show Annotation", + "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); prop = RNA_def_property(srna, "use_auto_render", PROP_BOOLEAN, PROP_NONE); @@ -4802,8 +4905,8 @@ static void rna_def_space_clip(BlenderRNA *brna) }; static const EnumPropertyItem gpencil_source_items[] = { - {SC_GPENCIL_SRC_CLIP, "CLIP", 0, "Clip", "Show grease pencil data-block which belongs to movie clip"}, - {SC_GPENCIL_SRC_TRACK, "TRACK", 0, "Track", "Show grease pencil data-block which belongs to active track"}, + {SC_GPENCIL_SRC_CLIP, "CLIP", 0, "Clip", "Show annotation data-block which belongs to movie clip"}, + {SC_GPENCIL_SRC_TRACK, "TRACK", 0, "Track", "Show annotation data-block which belongs to active track"}, {0, NULL, 0, NULL, NULL} }; @@ -4953,11 +5056,11 @@ static void rna_def_space_clip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Manual Calibration", "Use manual calibration helpers"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); - /* show grease pencil */ - prop = RNA_def_property(srna, "show_grease_pencil", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SC_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Grease Pencil", - "Show grease pencil for this view"); + /* show annotation */ + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SC_SHOW_ANNOTATION); + RNA_def_property_ui_text(prop, "Show Annotation", + "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL); /* show filters */ diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 5da49ac5957..4c5af755b13 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -1480,6 +1480,7 @@ static void rna_def_trackingTrack(BlenderRNA *brna) prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gpd"); RNA_def_property_struct_type(prop, "GreasePencil"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); RNA_def_property_ui_text(prop, "Grease Pencil", "Grease pencil data for this track"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index c9ce9ae7961..a5deb0a32f1 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -759,6 +759,7 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_string(func, "unlink", NULL, 0, "", "Operator identifier to unlink the ID block"); RNA_def_enum(func, "filter", id_template_filter_items, UI_TEMPLATE_ID_FILTER_ALL, "", "Optionally limit the items which can be selected"); + RNA_def_boolean(func, "live_icon", false, "", "Show preview instead of fixed icon"); func = RNA_def_function(srna, "template_ID_preview", "uiTemplateIDPreview"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); @@ -831,6 +832,31 @@ void RNA_api_ui_layout(StructRNA *srna) parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "template_greasepencil_modifier", "uiTemplateGpencilModifier"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Generates the UI layout for grease pencil modifiers"); + parm = RNA_def_pointer(func, "data", "GpencilModifier", "", "Modifier data"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "template_shaderfx", "uiTemplateShaderFx"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Generates the UI layout for shader effect"); + parm = RNA_def_pointer(func, "data", "ShaderFx", "", "Shader data"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "template_greasepencil_color", "uiTemplateGpencilColorPreview"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + api_ui_item_rna_common(func); + RNA_def_int(func, "rows", 0, 0, INT_MAX, "Number of thumbnail preview rows to display", "", 0, INT_MAX); + RNA_def_int(func, "cols", 0, 0, INT_MAX, "Number of thumbnail preview columns to display", "", 0, INT_MAX); + RNA_def_float(func, "scale", 1.0f, 0.1f, 1.5f, "Scale of the image thumbnails", "", 0.5f, 1.0f); + RNA_def_enum(func, "filter", id_template_filter_items, UI_TEMPLATE_ID_FILTER_ALL, + "", "Optionally limit the items which can be selected"); + func = RNA_def_function(srna, "template_constraint", "uiTemplateConstraint"); RNA_def_function_ui_description(func, "Generates the UI layout for constraints"); parm = RNA_def_pointer(func, "data", "Constraint", "", "Constraint data"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 3c92b6c143b..4c3074bba4f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4371,6 +4371,14 @@ static void rna_def_userdef_system(BlenderRNA *brna) "Enable OpenGL multi-sampling, only for systems that support it, 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(prop, "Region Overlap", diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index 5c3f510ffca..0b27cadd086 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -739,6 +739,9 @@ int RE_engine_render(Render *re, int do_all) type->render(engine, engine->depsgraph); + /* grease pencil render over previous render result */ + DRW_render_gpencil(engine, engine->depsgraph); + engine_depsgraph_free(engine); } FOREACH_VIEW_LAYER_TO_RENDER_END; diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt new file mode 100644 index 00000000000..2b33c9817c3 --- /dev/null +++ b/source/blender/shader_fx/CMakeLists.txt @@ -0,0 +1,64 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2018, Blender Foundation +# All rights reserved. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern + ../blenkernel + ../blenlib + ../blenfont + ../depsgraph + ../makesdna + ../makesrna + ../bmesh + ../render/extern/include + ../../../intern/elbeem/extern + ../../../intern/guardedalloc + ../../../intern/eigen +) + +set(INC_SYS + ${ZLIB_INCLUDE_DIRS} +) + +set(SRC + intern/FX_shader_util.h + + intern/FX_shader_util.c + intern/FX_shader_blur.c + intern/FX_shader_colorize.c + intern/FX_shader_flip.c + intern/FX_shader_light.c + intern/FX_shader_pixel.c + intern/FX_shader_rim.c + intern/FX_shader_swirl.c + intern/FX_shader_wave.c + + FX_shader_types.h +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +add_definitions(${GL_DEFINITIONS}) + +blender_add_lib(bf_shader_fx "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/shader_fx/FX_shader_types.h b/source/blender/shader_fx/FX_shader_types.h new file mode 100644 index 00000000000..b8d8f04e07f --- /dev/null +++ b/source/blender/shader_fx/FX_shader_types.h @@ -0,0 +1,47 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Ben Batt + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file FX_shader_types.h + * \ingroup shader_fx + */ + +#ifndef __FX_SHADER_TYPES_H__ +#define __FX_SHADER_TYPES_H__ + +#include "BKE_shader_fx.h" + +/* ****************** Type structures for all effects ****************** */ + +extern ShaderFxTypeInfo shaderfx_Type_None; +extern ShaderFxTypeInfo shaderfx_Type_Blur; +extern ShaderFxTypeInfo shaderfx_Type_Colorize; +extern ShaderFxTypeInfo shaderfx_Type_Flip; +extern ShaderFxTypeInfo shaderfx_Type_Light; +extern ShaderFxTypeInfo shaderfx_Type_Pixel; +extern ShaderFxTypeInfo shaderfx_Type_Rim; +extern ShaderFxTypeInfo shaderfx_Type_Swirl; +extern ShaderFxTypeInfo shaderfx_Type_Wave; + +/* FX_shaderfx_util.c */ +void shaderfx_type_init(ShaderFxTypeInfo *types[]); + +#endif /* __FX_SHADER_TYPES_H__ */ diff --git a/source/blender/shader_fx/intern/FX_shader_blur.c b/source/blender/shader_fx/intern/FX_shader_blur.c new file mode 100644 index 00000000000..128ebba8875 --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_blur.c @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_blur.c + * \ingroup shader_fx + */ + +#include + +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + BlurShaderFxData *gpfx = (BlurShaderFxData *)fx; + ARRAY_SET_ITEMS(gpfx->radius, 1, 1); + gpfx->samples = 4; + gpfx->coc = 0.025f; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Blur = { + /* name */ "Blur", + /* structName */ "BlurShaderFxData", + /* structSize */ sizeof(BlurShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ eShaderFxTypeFlag_Single, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_colorize.c b/source/blender/shader_fx/intern/FX_shader_colorize.c new file mode 100644 index 00000000000..edf276b842c --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_colorize.c @@ -0,0 +1,69 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_colorize.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_shader_fx_types.h" + +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + ColorizeShaderFxData *gpfx = (ColorizeShaderFxData *)fx; + ARRAY_SET_ITEMS(gpfx->low_color, 0.0f, 0.0f, 0.0f, 1.0f); + ARRAY_SET_ITEMS(gpfx->high_color, 1.0f, 1.0f, 1.0f, 1.0f); + gpfx->mode = eShaderFxColorizeMode_GrayScale; + gpfx->factor = 0.5f; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Colorize = { + /* name */ "Colorize", + /* structName */ "ColorizeShaderFxData", + /* structSize */ sizeof(ColorizeShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ 0, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_flip.c b/source/blender/shader_fx/intern/FX_shader_flip.c new file mode 100644 index 00000000000..404e2f8160e --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_flip.c @@ -0,0 +1,69 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_flip.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" + +#include "BLI_math_base.h" +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + FlipShaderFxData *gpfx = (FlipShaderFxData *)fx; + gpfx->flag |= FX_FLIP_HORIZONTAL; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Flip = { + /* name */ "Flip", + /* structName */ "FlipShaderFxData", + /* structSize */ sizeof(FlipShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ eShaderFxTypeFlag_Single, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_light.c b/source/blender/shader_fx/intern/FX_shader_light.c new file mode 100644 index 00000000000..9a17ea8ae5f --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_light.c @@ -0,0 +1,104 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_light.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" + +#include "BLI_math_base.h" +#include "BLI_utildefines.h" + +#include "BKE_library_query.h" +#include "BKE_modifier.h" +#include "BKE_shader_fx.h" + +#include "FX_shader_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +static void initData(ShaderFxData *fx) +{ + LightShaderFxData *gpfx = (LightShaderFxData *)fx; + gpfx->energy = 10.0f; + gpfx->ambient = 5.0f; + gpfx->object = NULL; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +static void updateDepsgraph(ShaderFxData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + LightShaderFxData *fxd = (LightShaderFxData *)md; + if (fxd->object != NULL) { + DEG_add_object_relation(ctx->node, fxd->object, DEG_OB_COMP_GEOMETRY, "Light ShaderFx"); + DEG_add_object_relation(ctx->node, fxd->object, DEG_OB_COMP_TRANSFORM, "Light ShaderFx"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Light ShaderFx"); +} + +static bool isDisabled(ShaderFxData *fx, int UNUSED(userRenderParams)) +{ + LightShaderFxData *fxd = (LightShaderFxData *)fx; + + return !fxd->object; +} + +static void foreachObjectLink( + ShaderFxData *fx, Object *ob, + ShaderFxObjectWalkFunc walk, void *userData) +{ + LightShaderFxData *fxd = (LightShaderFxData *)fx; + + walk(userData, ob, &fxd->object, IDWALK_CB_NOP); +} + +ShaderFxTypeInfo shaderfx_Type_Light = { + /* name */ "Light", + /* structName */ "LightShaderFxData", + /* structSize */ sizeof(LightShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ 0, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_pixel.c b/source/blender/shader_fx/intern/FX_shader_pixel.c new file mode 100644 index 00000000000..a3ffd3a9b0d --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_pixel.c @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_pixel.c + * \ingroup shader_fx + */ + +#include + +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + PixelShaderFxData *gpfx = (PixelShaderFxData *)fx; + ARRAY_SET_ITEMS(gpfx->size, 5, 5); + ARRAY_SET_ITEMS(gpfx->rgba, 0.0f, 0.0f, 0.0f, 0.9f); + gpfx->flag |= FX_PIXEL_USE_LINES; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Pixel = { + /* name */ "Pixelate", + /* structName */ "PixelShaderFxData", + /* structSize */ sizeof(PixelShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ eShaderFxTypeFlag_Single, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_rim.c b/source/blender/shader_fx/intern/FX_shader_rim.c new file mode 100644 index 00000000000..611e6f91bf7 --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_rim.c @@ -0,0 +1,70 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_rim.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_shader_fx_types.h" + +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + RimShaderFxData *gpfx = (RimShaderFxData *)fx; + ARRAY_SET_ITEMS(gpfx->offset, 50, -100); + ARRAY_SET_ITEMS(gpfx->rim_rgb, 1.0f, 1.0f, 0.5f, 0.9f); + ARRAY_SET_ITEMS(gpfx->mask_rgb, 0.0f, 0.0f, 0.0f, 1.0f); + gpfx->mode = eShaderFxRimMode_Multiply; + ARRAY_SET_ITEMS(gpfx->blur, 0, 0); +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Rim = { + /* name */ "Rim", + /* structName */ "RimShaderFxData", + /* structSize */ sizeof(RimShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ 0, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_swirl.c b/source/blender/shader_fx/intern/FX_shader_swirl.c new file mode 100644 index 00000000000..9667f466eec --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_swirl.c @@ -0,0 +1,103 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_swirl.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" + +#include "BLI_math_base.h" +#include "BLI_utildefines.h" + +#include "BKE_library_query.h" +#include "BKE_modifier.h" +#include "BKE_shader_fx.h" + +#include "FX_shader_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +static void initData(ShaderFxData *md) +{ + SwirlShaderFxData *gpmd = (SwirlShaderFxData *)md; + gpmd->radius = 100; + gpmd->angle = M_PI_2; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +static void updateDepsgraph(ShaderFxData *fx, const ModifierUpdateDepsgraphContext *ctx) +{ + SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; + if (fxd->object != NULL) { + DEG_add_object_relation(ctx->node, fxd->object, DEG_OB_COMP_GEOMETRY, "Swirl ShaderFx"); + DEG_add_object_relation(ctx->node, fxd->object, DEG_OB_COMP_TRANSFORM, "Swirl ShaderFx"); + } + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Swirl ShaderFx"); +} + +static bool isDisabled(ShaderFxData *fx, int UNUSED(userRenderParams)) +{ + SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; + + return !fxd->object; +} + +static void foreachObjectLink( + ShaderFxData *fx, Object *ob, + ShaderFxObjectWalkFunc walk, void *userData) +{ + SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx; + + walk(userData, ob, &fxd->object, IDWALK_CB_NOP); +} + +ShaderFxTypeInfo shaderfx_Type_Swirl = { + /* name */ "Swirl", + /* structName */ "SwirlShaderFxData", + /* structSize */ sizeof(SwirlShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ 0, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ foreachObjectLink, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/shader_fx/intern/FX_shader_util.c b/source/blender/shader_fx/intern/FX_shader_util.c new file mode 100644 index 00000000000..c55b9304503 --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_util.c @@ -0,0 +1,56 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/shader_fx/intern/FX_shader_util.c + * \ingroup shader_fx + */ + + +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_shader_fx.h" + +#include "FX_shader_types.h" +#include "FX_shader_util.h" + +void shaderfx_type_init(ShaderFxTypeInfo *types[]) +{ +#define INIT_FX_TYPE(typeName) (types[eShaderFxType_##typeName] = &shaderfx_Type_##typeName) + INIT_FX_TYPE(Blur); + INIT_FX_TYPE(Colorize); + INIT_FX_TYPE(Flip); + INIT_FX_TYPE(Light); + INIT_FX_TYPE(Pixel); + INIT_FX_TYPE(Rim); + INIT_FX_TYPE(Swirl); + INIT_FX_TYPE(Wave); +#undef INIT_FX_TYPE +} + diff --git a/source/blender/shader_fx/intern/FX_shader_util.h b/source/blender/shader_fx/intern/FX_shader_util.h new file mode 100644 index 00000000000..e2fdcd6fb4c --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_util.h @@ -0,0 +1,36 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/shader_fx/intern/FX_shader_util.h + * \ingroup shader_fx + */ + + +#ifndef __FX_SHADER_UTIL_H__ +#define __FX_SHADER_UTIL_H__ + +#endif /* __FX_SHADER_UTIL_H__ */ diff --git a/source/blender/shader_fx/intern/FX_shader_wave.c b/source/blender/shader_fx/intern/FX_shader_wave.c new file mode 100644 index 00000000000..ea4563a00e1 --- /dev/null +++ b/source/blender/shader_fx/intern/FX_shader_wave.c @@ -0,0 +1,71 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/shader_fx/intern/FX_shader_wave.c + * \ingroup shader_fx + */ + +#include + +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" + +#include "BLI_utildefines.h" + +#include "FX_shader_types.h" + +static void initData(ShaderFxData *fx) +{ + WaveShaderFxData *gpfx = (WaveShaderFxData *)fx; + gpfx->amplitude = 10.0f; + gpfx->period = 20.0f; + gpfx->phase = 0.0f; + gpfx->orientation = 1; +} + +static void copyData(const ShaderFxData *md, ShaderFxData *target) +{ + BKE_shaderfx_copyData_generic(md, target); +} + +ShaderFxTypeInfo shaderfx_Type_Wave = { + /* name */ "Wave Distorsion", + /* structName */ "WaveShaderFxData", + /* structSize */ sizeof(WaveShaderFxData), + /* type */ eShaderFxType_GpencilType, + /* flags */ eShaderFxTypeFlag_Single, + + /* copyData */ copyData, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ NULL, +}; diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index df869ba6b68..76a1482ac7c 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -440,7 +440,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr switch (GS(((ID *)ptr->id.data)->name)) { case ID_SCE: { - CTX_TEST_PTR_DATA_TYPE(C, "active_gpencil_brush", RNA_GPencilBrush, ptr, CTX_data_active_gpencil_brush(C)); + CTX_TEST_PTR_DATA_TYPE(C, "active_gpencil_brush", RNA_Brush, ptr, CTX_data_active_gpencil_brush(C)); CTX_TEST_PTR_ID(C, "scene", ptr->id.data); break; } diff --git a/source/creator/creator.c b/source/creator/creator.c index 18396149342..914211afd56 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -63,7 +63,9 @@ #include "BKE_global.h" #include "BKE_material.h" #include "BKE_modifier.h" +#include "BKE_gpencil_modifier.h" #include "BKE_node.h" +#include "BKE_shader_fx.h" #include "BKE_sound.h" #include "BKE_image.h" #include "BKE_particle.h" @@ -371,6 +373,8 @@ int main( BKE_cachefiles_init(); BKE_images_init(); BKE_modifier_init(); + BKE_gpencil_modifier_init(); + BKE_shaderfx_init(); DEG_register_node_types(); BKE_brush_system_init();