diff options
author | Antony Riakiotakis <kalast@gmail.com> | 2014-07-21 14:02:05 +0400 |
---|---|---|
committer | Antony Riakiotakis <kalast@gmail.com> | 2014-07-21 14:02:05 +0400 |
commit | f745564e4ee791e4faf804b09ce975b882f4f8d9 (patch) | |
tree | 90ebbe363ccd925cedc652c9bb018ce552b5a2ab | |
parent | 8489b94e07f9e73bd3c9c3e4f6a91f1f0a259827 (diff) |
GSOC 2013 paint
Yep, at last it's here!
There are a few minor issues remaining but development can go on in
master after discussion at blender institute.
For full list of features see:
http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.72/Painting
Thanks to Sergey and Campbell for the extensive review and to the
countless artists that have given their input and reported issues during
development.
113 files changed, 7928 insertions, 1221 deletions
diff --git a/SConstruct b/SConstruct index 6abf91c3c13..331158e89d2 100644 --- a/SConstruct +++ b/SConstruct @@ -730,6 +730,8 @@ if B.targets != ['cudakernels']: data_to_c_simple("release/datafiles/brushicons/soften.png") data_to_c_simple("release/datafiles/brushicons/subtract.png") data_to_c_simple("release/datafiles/brushicons/texdraw.png") + data_to_c_simple("release/datafiles/brushicons/texfill.png") + data_to_c_simple("release/datafiles/brushicons/texmask.png") data_to_c_simple("release/datafiles/brushicons/thumb.png") data_to_c_simple("release/datafiles/brushicons/twist.png") data_to_c_simple("release/datafiles/brushicons/vertexdraw.png") diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 462ca2e85ca..8359ae651ff 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -50,6 +50,10 @@ class UnifiedPaintPanel(): row.prop(ups, "use_unified_strength", text="Strength") if context.weight_paint_object: parent.prop(ups, "use_unified_weight", text="Weight") + elif context.vertex_paint_object or context.image_paint_object: + parent.prop(ups, "use_unified_color", text="Color") + else: + parent.prop(ups, "use_unified_color", text="Color") @staticmethod def prop_unified_size(parent, context, brush, prop_name, icon='NONE', text="", slider=False): @@ -69,6 +73,105 @@ class UnifiedPaintPanel(): ptr = ups if ups.use_unified_weight else brush parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + @staticmethod + def prop_unified_color(parent, context, brush, prop_name, text=""): + ups = context.tool_settings.unified_paint_settings + ptr = ups if ups.use_unified_color else brush + parent.prop(ptr, prop_name, text=text) + + @staticmethod + def prop_unified_color_picker(parent, context, brush, prop_name, value_slider=True): + ups = context.tool_settings.unified_paint_settings + ptr = ups if ups.use_unified_color else brush + parent.template_color_picker(ptr, prop_name, value_slider=value_slider) + + +def brush_texpaint_common(panel, context, layout, brush, settings): + capabilities = brush.image_paint_capabilities + + col = layout.column() + + if brush.image_tool in {'DRAW', 'FILL'}: + if brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'}: + if not brush.use_gradient: + panel.prop_unified_color_picker(col, context, brush, "color", value_slider=True) + + if settings.palette: + col.template_palette(settings, "palette", color=True) + + if brush.use_gradient: + col.label("Gradient Colors") + col.template_color_ramp(brush, "gradient", expand=True) + + if brush.image_tool != 'FILL': + col.label("Background Color") + row = col.row(align=True) + panel.prop_unified_color(row, context, brush, "secondary_color", text="") + + if brush.image_tool == 'DRAW': + col.prop(brush, "gradient_stroke_mode", text="Mode") + if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: + col.prop(brush, "grad_spacing") + elif brush.image_tool == 'FILL': + col.prop(brush, "gradient_fill_mode") + else: + row = col.row(align=True) + panel.prop_unified_color(row, context, brush, "color", text="") + if brush.image_tool == 'FILL': + col.prop(brush, "fill_threshold") + else: + panel.prop_unified_color(row, context, brush, "secondary_color", text="") + row.separator() + row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="") + + elif brush.image_tool == 'SOFTEN': + col = layout.column(align=True) + col.row().prop(brush, "direction", expand=True) + col.separator() + col.prop(brush, "sharp_threshold") + col.prop(brush, "blur_kernel_radius") + col.separator() + col.prop(brush, "blur_mode") + elif brush.image_tool == 'MASK': + col.prop(brush, "weight", text="Mask Value", slider=True) + + elif brush.image_tool == 'CLONE': + col.separator() + col.prop(brush, "clone_image", text="Image") + col.prop(brush, "clone_alpha", text="Alpha") + + col.separator() + + if capabilities.has_radius: + row = col.row(align=True) + panel.prop_unified_size(row, context, brush, "size", slider=True, text="Radius") + panel.prop_unified_size(row, context, brush, "use_pressure_size") + + row = col.row(align=True) + + if capabilities.has_space_attenuation: + row.prop(brush, "use_space_attenuation", toggle=True, icon_only=True) + + panel.prop_unified_strength(row, context, brush, "strength", text="Strength") + panel.prop_unified_strength(row, context, brush, "use_pressure_strength") + + if brush.image_tool in {'DRAW', 'FILL'}: + col.separator() + col.prop(brush, "blend", text="Blend") + + col = layout.column() + + # use_accumulate + if capabilities.has_accumulate: + col = layout.column(align=True) + col.prop(brush, "use_accumulate") + + col.prop(brush, "use_alpha") + col.prop(brush, "use_gradient") + + col.separator() + col.template_ID(settings, "palette", new="palette.new") + # Used in both the View3D toolbar and texture properties def brush_texture_settings(layout, brush, sculpt): @@ -136,6 +239,7 @@ def brush_mask_texture_settings(layout, brush): layout.operator("brush.stencil_reset_transform").mask = True col = layout.column() + col.prop(brush, "use_pressure_masking", text="") col.label(text="Angle:") col.active = brush.brush_capabilities.has_texture_angle col.prop(mask_tex_slot, "angle", text="") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 97c89df0693..b3ba3b6305f 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -22,6 +22,7 @@ from bpy.types import Header, Menu, Panel from bl_ui.properties_paint_common import ( UnifiedPaintPanel, brush_texture_settings, + brush_texpaint_common, brush_mask_texture_settings, ) from bl_ui.properties_grease_pencil_common import GreasePencilPanel @@ -31,13 +32,11 @@ from bpy.app.translations import pgettext_iface as iface_ class ImagePaintPanel(UnifiedPaintPanel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'TOOLS' - bl_category = "Tools" -class BrushButtonsPanel: +class BrushButtonsPanel(UnifiedPaintPanel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'TOOLS' - bl_category = "Tools" @classmethod def poll(cls, context): @@ -66,6 +65,7 @@ class IMAGE_MT_view(Menu): sima = context.space_data uv = sima.uv_editor toolsettings = context.tool_settings + paint = toolsettings.image_paint show_uvedit = sima.show_uvedit show_render = sima.show_render @@ -80,6 +80,8 @@ class IMAGE_MT_view(Menu): layout.prop(toolsettings, "show_uv_local_view") layout.prop(uv, "show_other_objects") + if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'): + layout.prop(uv, "show_texpaint") layout.separator() @@ -140,6 +142,24 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_split") +class IMAGE_MT_brush(Menu): + bl_label = "Brush" + + def draw(self, context): + layout = self.layout + toolsettings = context.tool_settings + settings = toolsettings.image_paint + brush = settings.brush + + ups = context.tool_settings.unified_paint_settings + layout.prop(ups, "use_unified_size", text="Unified Size") + layout.prop(ups, "use_unified_strength", text="Unified Strength") + layout.separator() + + # brush tool + layout.prop_menu_enum(brush, "image_tool") + + class IMAGE_MT_image(Menu): bl_label = "Image" @@ -382,7 +402,6 @@ class IMAGE_HT_header(Header): mode = sima.mode show_render = sima.show_render - # show_paint = sima.show_paint show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit @@ -401,8 +420,7 @@ class IMAGE_HT_header(Header): row = layout.row() row.template_ID(sima, "mask", new="mask.new") - if show_uvedit or show_maskedit: - layout.prop(sima, "pivot_point", icon_only=True) + layout.prop(sima, "pivot_point", icon_only=True) # uv editing if show_uvedit: @@ -462,6 +480,7 @@ class MASK_MT_editor_menus(Menu): show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit + show_paint = sima.show_paint layout.menu("IMAGE_MT_view") @@ -469,6 +488,8 @@ class MASK_MT_editor_menus(Menu): layout.menu("IMAGE_MT_select") if show_maskedit: layout.menu("MASK_MT_select") + if show_paint: + layout.menu("IMAGE_MT_brush") if ima and ima.is_dirty: layout.menu("IMAGE_MT_image", text="Image*") @@ -658,49 +679,27 @@ class IMAGE_PT_tools_transform_uvs(Panel, UVToolsPanel): col.operator("transform.shear") -class IMAGE_PT_paint(Panel, ImagePaintPanel): +class IMAGE_PT_paint(Panel, BrushButtonsPanel): bl_label = "Paint" - - @classmethod - def poll(cls, context): - sima = context.space_data - return sima.show_paint + bl_category = "Tools" def draw(self, context): layout = self.layout - toolsettings = context.tool_settings.image_paint - brush = toolsettings.brush + settings = context.tool_settings.image_paint + brush = settings.brush col = layout.column() - col.template_ID_preview(toolsettings, "brush", new="brush.add", rows=2, cols=6) + col.template_ID_preview(settings, "brush", new="brush.add", rows=2, cols=6) if brush: - col = layout.column() - - if brush.image_tool == 'DRAW' and brush.blend not in ('ERASE_ALPHA', 'ADD_ALPHA'): - col.template_color_picker(brush, "color", value_slider=True) - col.prop(brush, "color", text="") - - row = col.row(align=True) - self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius") - self.prop_unified_size(row, context, brush, "use_pressure_size") - - row = col.row(align=True) - self.prop_unified_strength(row, context, brush, "strength", slider=True, text="Strength") - self.prop_unified_strength(row, context, brush, "use_pressure_strength") - - col.prop(brush, "blend", text="Blend") - - if brush.image_tool == 'CLONE': - col.separator() - col.prop(brush, "clone_image", text="Image") - col.prop(brush, "clone_alpha", text="Alpha") + brush_texpaint_common(self, context, layout, brush, settings) class IMAGE_PT_tools_brush_overlay(BrushButtonsPanel, Panel): bl_label = "Overlay" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Options" def draw(self, context): layout = self.layout @@ -754,6 +753,7 @@ class IMAGE_PT_tools_brush_overlay(BrushButtonsPanel, Panel): class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): bl_label = "Texture" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" def draw(self, context): layout = self.layout @@ -770,6 +770,7 @@ class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel): bl_label = "Texture Mask" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" def draw(self, context): layout = self.layout @@ -786,6 +787,7 @@ class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel): class IMAGE_PT_tools_brush_tool(BrushButtonsPanel, Panel): bl_label = "Tool" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Options" def draw(self, context): layout = self.layout @@ -804,6 +806,7 @@ class IMAGE_PT_tools_brush_tool(BrushButtonsPanel, Panel): class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel): bl_label = "Paint Stroke" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" def draw(self, context): layout = self.layout @@ -828,10 +831,19 @@ class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel): if brush.use_space: col.separator() row = col.row(align=True) - row.active = brush.use_space row.prop(brush, "spacing", text="Spacing") row.prop(brush, "use_pressure_spacing", toggle=True, text="") + if brush.use_line or brush.use_curve: + col.separator() + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + + if brush.use_curve: + col.separator() + col.template_ID(brush, "paint_curve", new="paintcurve.new") + col.operator("paintcurve.draw") + col = layout.column() col.separator() @@ -846,25 +858,23 @@ class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel): col = layout.column() col.separator() - col.prop(brush, "use_smooth_stroke") + if brush.brush_capabilities.has_smooth_stroke: + col.prop(brush, "use_smooth_stroke") - sub = col.column() - sub.active = brush.use_smooth_stroke - sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + sub = col.column() + sub.active = brush.use_smooth_stroke + sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) - col.separator() + col.separator() col.prop(toolsettings, "input_samples") - col.separator() - - col.prop(brush, "use_wrap") - class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): bl_label = "Paint Curve" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Tools" def draw(self, context): layout = self.layout @@ -874,7 +884,8 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): layout.template_curve_mapping(brush, "curve") - row = layout.row(align=True) + col = layout.column(align=True) + row = col.row(align=True) row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' @@ -886,6 +897,7 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): class IMAGE_PT_tools_brush_appearance(BrushButtonsPanel, Panel): bl_label = "Appearance" bl_options = {'DEFAULT_CLOSED'} + bl_category = "Options" def draw(self, context): layout = self.layout @@ -912,6 +924,30 @@ class IMAGE_PT_tools_brush_appearance(BrushButtonsPanel, Panel): sub.prop(brush, "icon_filepath", text="") +class IMAGE_PT_tools_paint_options(BrushButtonsPanel, Panel): + bl_label = "Image Paint" + bl_category = "Options" + + def draw(self, context): + layout = self.layout + + toolsettings = context.tool_settings + brush = toolsettings.image_paint.brush + + ups = toolsettings.unified_paint_settings + + col = layout.column(align=True) + + col.prop(brush, "use_wrap") + col.separator() + + col.label(text="Unified Settings:") + row = col.row() + row.prop(ups, "use_unified_size", text="Size") + row.prop(ups, "use_unified_strength", text="Strength") + col.prop(ups, "use_unified_color", text="Color") + + class IMAGE_UV_sculpt_curve(Panel): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'TOOLS' diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 4cb3e8e116c..1079c5016a9 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1454,7 +1454,7 @@ class VIEW3D_MT_brush(Menu): layout.separator() if sculpt_tool != 'GRAB': - layout.prop_menu_enum(brush, "stroke_method") + layout.prop_menu_enum(brush, "sculpt_stroke_method") if sculpt_tool in {'DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'}: layout.prop_menu_enum(brush, "direction") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 0f2c04d1cdc..99b2b852936 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -18,11 +18,12 @@ # <pep8 compliant> import bpy -from bpy.types import Menu, Panel +from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import GreasePencilPanel from bl_ui.properties_paint_common import ( UnifiedPaintPanel, brush_texture_settings, + brush_texpaint_common, brush_mask_texture_settings, ) @@ -363,6 +364,7 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, Panel): draw_repeat_tools(context, layout) + class VIEW3D_PT_tools_meshweight(View3DPanel, Panel): bl_category = "Tools" bl_context = "mesh_edit" @@ -388,6 +390,7 @@ class VIEW3D_PT_tools_meshweight(View3DPanel, Panel): layout = self.layout self.draw_generic(layout) + class VIEW3D_PT_tools_add_mesh_edit(View3DPanel, Panel): bl_category = "Create" bl_context = "mesh_edit" @@ -979,25 +982,7 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): # Texture Paint Mode # elif context.image_paint_object and brush: - col = layout.column() - - if brush.image_tool == 'DRAW' and brush.blend not in ('ERASE_ALPHA', 'ADD_ALPHA'): - col.template_color_picker(brush, "color", value_slider=True) - col.prop(brush, "color", text="") - - row = col.row(align=True) - self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius") - self.prop_unified_size(row, context, brush, "use_pressure_size") - - row = col.row(align=True) - self.prop_unified_strength(row, context, brush, "strength", text="Strength") - self.prop_unified_strength(row, context, brush, "use_pressure_strength") - - col.prop(brush, "blend", text="Blend") - - col = layout.column() - col.active = (brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'}) - col.prop(brush, "use_alpha") + brush_texpaint_common(self, context, layout, brush, settings) # Weight Paint Mode # elif context.weight_paint_object and brush: @@ -1024,9 +1009,12 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): # Vertex Paint Mode # elif context.vertex_paint_object and brush: col = layout.column() - col.template_color_picker(brush, "color", value_slider=True) - col.prop(brush, "color", text="") + self.prop_unified_color_picker(col, context, brush, "color", value_slider=True) + if settings.palette: + col.template_palette(settings, "palette", color=True) + self.prop_unified_color(col, context, brush, "color", text="") + col.separator() row = col.row(align=True) self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius") self.prop_unified_size(row, context, brush, "use_pressure_size") @@ -1036,12 +1024,75 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): self.prop_unified_strength(row, context, brush, "use_pressure_strength") # XXX - TODO - #row = col.row(align=True) - #row.prop(brush, "jitter", slider=True) - #row.prop(brush, "use_pressure_jitter", toggle=True, text="") - + # row = col.row(align=True) + # row.prop(brush, "jitter", slider=True) + # row.prop(brush, "use_pressure_jitter", toggle=True, text="") + col.separator() col.prop(brush, "vertex_tool", text="Blend") + col.separator() + col.template_ID(settings, "palette", new="palette.new") + + +class TEXTURE_UL_texpaintslots(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + ma = data + ima = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.label(text=ima.name, translate=False, icon_value=icon) + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="") + + +class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel): + bl_context = "imagepaint" + bl_label = "Slots" + bl_category = "Layers" + + @classmethod + def poll(cls, context): + brush = context.tool_settings.image_paint.brush + ob = context.active_object + return (brush is not None and ob is not None) + + def draw(self, context): + layout = self.layout + + settings = context.tool_settings.image_paint + brush = settings.brush + + ob = context.active_object + col = layout.column() + + if len(ob.material_slots) > 1: + col.label("Materials") + col.template_list("MATERIAL_UL_matslots", "", + ob, "material_slots", + ob, "active_material_index", rows=2) + + mat = ob.active_material + if mat: + col.label("Available Paint Slots") + col.template_list("TEXTURE_UL_texpaintslots", "", + mat, "texture_paint_slots", + mat, "paint_active_slot", rows=2) + + if not mat.use_nodes: + col.operator_menu_enum("paint.add_texture_paint_slot", "type") + + row = col.row(align=True) + row.prop(settings, "slot_xresolution_default") + row.prop(settings, "slot_yresolution_default") + col.prop(settings, "slot_color_default") + + if brush.image_tool == 'CLONE' and settings.use_clone_layer: + col.label("Clone Slot") + col.template_list("TEXTURE_UL_texpaintslots", "", + mat, "texture_paint_slots", + mat, "paint_clone_slot", rows=2) + class VIEW3D_PT_tools_brush_overlay(Panel, View3DPaintPanel): bl_category = "Options" @@ -1194,10 +1245,19 @@ class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel): if brush.use_space: col.separator() row = col.row(align=True) - row.active = brush.use_space row.prop(brush, "spacing", text="Spacing") row.prop(brush, "use_pressure_spacing", toggle=True, text="") + if brush.use_line or brush.use_curve: + col.separator() + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + + if brush.use_curve: + col.separator() + col.template_ID(brush, "paint_curve", new="paintcurve.new") + col.operator("paintcurve.draw") + if context.sculpt_object: if brush.sculpt_capabilities.has_jitter: col.separator() @@ -1234,12 +1294,13 @@ class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel): col = layout.column() col.separator() - col.prop(brush, "use_smooth_stroke") + if brush.brush_capabilities.has_smooth_stroke: + col.prop(brush, "use_smooth_stroke") - sub = col.column() - sub.active = brush.use_smooth_stroke - sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + sub = col.column() + sub.active = brush.use_smooth_stroke + sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) layout.prop(settings, "input_samples") @@ -1263,7 +1324,8 @@ class VIEW3D_PT_tools_brush_curve(Panel, View3DPaintPanel): layout.template_curve_mapping(brush, "curve", brush=True) - row = layout.row(align=True) + col = layout.column(align=True) + row = col.row(align=True) row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' @@ -1493,7 +1555,7 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel): col = layout.column() row = col.row() - #col.prop(vpaint, "mode", text="") + # col.prop(vpaint, "mode", text="") row.prop(vpaint, "use_normal") col.prop(vpaint, "use_spray") @@ -1531,7 +1593,7 @@ class VIEW3D_PT_tools_imagepaint_external(Panel, View3DPaintPanel): col.operator("image.save_dirty", text="Save All Edited") -class VIEW3D_PT_tools_projectpaint(View3DPanel, Panel): +class VIEW3D_PT_tools_projectpaint(View3DPaintPanel, Panel): bl_category = "Options" bl_context = "imagepaint" bl_label = "Project Paint" @@ -1551,6 +1613,7 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel, Panel): settings = toolsettings.image_paint col = layout.column() + col.prop(ipaint, "use_occlude") col.prop(ipaint, "use_backface_culling") @@ -1565,19 +1628,21 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel, Panel): split.prop(ipaint, "use_stencil_layer", text="Stencil") - row = split.row() - row.active = (ipaint.use_stencil_layer) + col = split.column() + col.active = (ipaint.use_stencil_layer) + row = col.row() stencil_text = mesh.uv_texture_stencil.name if mesh.uv_texture_stencil else "" row.menu("VIEW3D_MT_tools_projectpaint_stencil", text=stencil_text, translate=False) row.prop(ipaint, "invert_stencil", text="", icon='IMAGE_ALPHA') + col.template_ID(ipaint, "stencil_image", new="image.new") + col.prop(ipaint, "stencil_color") col = layout.column() col.active = (settings.brush.image_tool == 'CLONE') - col.prop(ipaint, "use_clone_layer", text="Clone from UV map") - clone_text = mesh.uv_texture_clone.name if mesh.uv_texture_clone else "" - col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + col.prop(ipaint, "use_clone_layer", text="Clone from paint slot") layout.prop(ipaint, "seam_bleed") + self.unified_paint_settings(layout, context) class VIEW3D_PT_imagepaint_options(View3DPaintPanel): diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 1ab5ec51de8..868d9768172 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -150,8 +150,10 @@ typedef DMDrawOption (*DMSetDrawOptions)(void *userData, int index); typedef DMDrawOption (*DMSetDrawOptionsTex)(struct MTFace *tface, const bool has_vcol, int matnr); typedef enum DMDrawFlag { - DM_DRAW_USE_COLORS = 1, - DM_DRAW_ALWAYS_SMOOTH = 2 + DM_DRAW_USE_COLORS = (1 << 0), + DM_DRAW_ALWAYS_SMOOTH = (1 << 1), + DM_DRAW_USE_ACTIVE_UV = (1 << 2), + DM_DRAW_USE_TEXPAINT_UV = (1 << 3), } DMDrawFlag; typedef enum DMForeachFlag { @@ -389,7 +391,7 @@ struct DerivedMesh { void (*drawFacesTex)(DerivedMesh *dm, DMSetDrawOptionsTex setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData); + void *userData, DMDrawFlag uvflag); /** Draw all faces with GLSL materials * o setMaterial is called for every different material nr @@ -423,7 +425,7 @@ struct DerivedMesh { void (*drawMappedFacesTex)(DerivedMesh *dm, DMSetDrawOptions setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData); + void *userData, DMDrawFlag uvflag); /** Draw mapped faces with GLSL materials * - setMaterial is called for every different material nr @@ -593,6 +595,8 @@ void DM_ensure_tessface(DerivedMesh *dm); void DM_update_tessface_data(DerivedMesh *dm); void DM_update_materials(DerivedMesh *dm, struct Object *ob); +struct MTFace *DM_paint_uvlayer_active_get(DerivedMesh *dm, int mat_nr); + /** interpolates vertex data from the vertices indexed by src_indices in the * source mesh using the given weights and stores the result in the vertex * indexed by dest_index in the dest mesh diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 19fa60f5827..5a53b19b345 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 271 -#define BLENDER_SUBVERSION 2 +#define BLENDER_SUBVERSION 3 /* 262 was the last editmesh release but it has compatibility code for bmesh data */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 5 diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 104e80e815c..d48753590bb 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -81,7 +81,11 @@ unsigned int *BKE_brush_gen_texture_cache(struct Brush *br, int half_side, bool /* radial control */ struct ImBuf *BKE_brush_gen_radial_control_imbuf(struct Brush *br, bool secondary); -/* unified strength and size */ +/* unified strength size and color */ + +float *BKE_brush_color_get(const struct Scene *scene, struct Brush *brush); +float *BKE_brush_secondary_color_get(const struct Scene *scene, struct Brush *brush); +void BKE_brush_color_set(struct Scene *scene, struct Brush *brush, const float color[3]); int BKE_brush_size_get(const struct Scene *scene, struct Brush *brush); void BKE_brush_size_set(struct Scene *scene, struct Brush *brush, int value); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 0372931dc49..0c7af12edc8 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -71,7 +71,7 @@ void id_clear_lib_data(struct Main *bmain, struct ID *id); struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 41 +#define MAX_LIBARRAY 43 int set_listbasepointers(struct Main *main, struct ListBase **lb); void BKE_libblock_free(struct Main *bmain, void *idv); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 82b03127237..ec654ea4b71 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -87,6 +87,8 @@ typedef struct Main { ListBase nodetree; ListBase brush; ListBase particle; + ListBase palettes; + ListBase paintcurves; ListBase wm; ListBase gpencil; ListBase movieclip; diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index 89d310753fc..e69299a36bf 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -86,6 +86,10 @@ short find_material_index(struct Object *ob, struct Material *ma); bool object_add_material_slot(struct Object *ob); bool object_remove_material_slot(struct Object *ob); +void BKE_texpaint_slot_refresh_cache(struct Material *ma, bool use_nodes); +void BKE_texpaint_slots_refresh_object(struct Object *ob, bool use_nodes); +void BKE_texpaint_slots_clear(struct Material *ma); + /* rna api */ void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user); void BKE_material_append_id(struct ID *id, struct Material *ma); diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 43813300850..0bdac6822f1 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -40,11 +40,15 @@ struct CurveMapping; struct MDisps; struct MeshElemMap; struct GridPaintMask; +struct Main; struct MFace; struct MultireModifierData; struct MVert; struct Object; struct Paint; +struct PaintCurve; +struct Palette; +struct PaletteColor; struct PBVH; struct Scene; struct Sculpt; @@ -52,6 +56,7 @@ struct StrokeCache; struct Tex; struct ImagePool; struct UnifiedPaintSettings; +struct wmOperator; enum OverlayFlags; @@ -91,6 +96,19 @@ OverlayControlFlags BKE_paint_get_overlay_flags(void); void BKE_paint_reset_overlay_invalid(OverlayControlFlags flag); void BKE_paint_set_overlay_override(enum OverlayFlags flag); +/* palettes */ +void BKE_palette_free(struct Palette *palette); +struct Palette *BKE_palette_add(struct Main *bmain, const char *name); +struct PaletteColor *BKE_palette_color_add(struct Palette *palette); +void BKE_palette_color_delete(struct Palette *palette); +bool BKE_palette_is_empty(const struct Palette *palette); +void BKE_palette_color_remove(struct Palette *palette, struct PaletteColor *color); +void BKE_palette_cleanup(struct Palette *palette); + +/* paint curves */ +struct PaintCurve *BKE_paint_curve_add(struct Main *bmain, const char *name); +void BKE_paint_curve_free(struct PaintCurve *pc); + void BKE_paint_init(struct Paint *p, const char col[3]); void BKE_paint_free(struct Paint *p); void BKE_paint_copy(struct Paint *src, struct Paint *tar); @@ -100,6 +118,9 @@ struct Paint *BKE_paint_get_active_from_context(const struct bContext *C); PaintMode BKE_paintmode_get_active_from_context(const struct bContext *C); struct Brush *BKE_paint_brush(struct Paint *paint); void BKE_paint_brush_set(struct Paint *paint, struct Brush *br); +struct Palette *BKE_paint_palette(struct Paint *paint); +void BKE_paint_palette_set(struct Paint *p, struct Palette *palette); +void BKE_paint_curve_set(struct Brush *br, struct PaintCurve *pc); /* testing face select mode * Texture paint could be removed since selected faces are not used @@ -117,7 +138,10 @@ bool paint_is_bmesh_face_hidden(struct BMFace *f); /* paint masks */ float paint_grid_paint_mask(const struct GridPaintMask *gpm, unsigned level, unsigned x, unsigned y); + +/* stroke related */ void paint_calculate_rake_rotation(struct UnifiedPaintSettings *ups, const float mouse_pos[2]); + /* Session data (mode-specific) */ typedef struct SculptSession { diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index d9af6ac3454..bdfaf9a1be9 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -37,6 +37,7 @@ #include "DNA_cloth_types.h" #include "DNA_key_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -501,11 +502,36 @@ void DM_update_materials(DerivedMesh *dm, Object *ob) dm->mat = MEM_callocN(totmat * sizeof(*dm->mat), "DerivedMesh.mat"); - for (i = 1; i < totmat; i++) { - dm->mat[i] = give_current_material(ob, i); + /* we leave last material as empty - rationale here is being able to index + * the materials by using the mf->mat_nr directly and leaving the last + * material as NULL in case no materials exist on mesh, so indexing will not fail */ + for (i = 0; i < totmat - 1; i++) { + dm->mat[i] = give_current_material(ob, i + 1); } } +MTFace *DM_paint_uvlayer_active_get(DerivedMesh *dm, int mat_nr) +{ + MTFace *tf_base; + + BLI_assert(mat_nr < dm->totmat); + + if (dm->mat[mat_nr] && dm->mat[mat_nr]->texpaintslot && + dm->mat[mat_nr]->texpaintslot[dm->mat[mat_nr]->paint_active_slot].uvname[0]) + { + tf_base = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, + dm->mat[mat_nr]->texpaintslot[dm->mat[mat_nr]->paint_active_slot].uvname); + /* This can fail if we have changed the name in the UV layer list and have assigned the old name in the material + * texture slot.*/ + if (!tf_base) + tf_base = CustomData_get_layer(&dm->faceData, CD_MTFACE); + } + else { + tf_base = CustomData_get_layer(&dm->faceData, CD_MTFACE); + } + + return tf_base; +} void DM_to_mesh(DerivedMesh *dm, Mesh *me, Object *ob, CustomDataMask mask) { @@ -3091,7 +3117,7 @@ static void navmesh_drawColored(DerivedMesh *dm) static void navmesh_DM_drawFacesTex(DerivedMesh *dm, DMSetDrawOptionsTex setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag UNUSED(flag)) { (void) setDrawOptions; (void) compareDrawOptions; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 967e89e0dd1..cfdb1aa7a96 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -83,6 +83,7 @@ static void brush_defaults(Brush *brush) brush->plane_trim = 0.5f; brush->clone.alpha = 0.5f; brush->normal_weight = 0.0f; + brush->fill_threshold = 0.2f; brush->flag |= BRUSH_ALPHA_PRESSURE; /* BRUSH PAINT TOOL SETTINGS */ @@ -90,6 +91,8 @@ static void brush_defaults(Brush *brush) brush->rgb[1] = 1.0f; brush->rgb[2] = 1.0f; + zero_v3(brush->secondary_rgb); + /* BRUSH STROKE SETTINGS */ brush->flag |= (BRUSH_SPACE | BRUSH_SPACE_ATTEN); brush->spacing = 10; /* how far each brush dot should be spaced as a percentage of brush diameter */ @@ -161,6 +164,9 @@ Brush *BKE_brush_copy(Brush *brush) if (brush->mask_mtex.tex) id_us_plus((ID *)brush->mask_mtex.tex); + if (brush->paint_curve) + id_us_plus((ID *)brush->paint_curve); + if (brush->icon_imbuf) brushn->icon_imbuf = IMB_dupImBuf(brush->icon_imbuf); @@ -180,11 +186,9 @@ Brush *BKE_brush_copy(Brush *brush) /* not brush itself */ void BKE_brush_free(Brush *brush) { - if (brush->mtex.tex) - brush->mtex.tex->id.us--; - - if (brush->mask_mtex.tex) - brush->mask_mtex.tex->id.us--; + id_us_min((ID *)brush->mtex.tex); + id_us_min((ID *)brush->mask_mtex.tex); + id_us_min((ID *)brush->paint_curve); if (brush->icon_imbuf) IMB_freeImBuf(brush->icon_imbuf); @@ -192,6 +196,9 @@ void BKE_brush_free(Brush *brush) BKE_previewimg_free(&(brush->preview)); curvemapping_free(brush->curve); + + if (brush->gradient) + MEM_freeN(brush->gradient); } static void extern_local_brush(Brush *brush) @@ -199,6 +206,7 @@ static void extern_local_brush(Brush *brush) id_lib_extern((ID *)brush->mtex.tex); id_lib_extern((ID *)brush->mask_mtex.tex); id_lib_extern((ID *)brush->clone.image); + id_lib_extern((ID *)brush->paint_curve); } void BKE_brush_make_local(Brush *brush) @@ -742,10 +750,23 @@ float BKE_brush_sample_masktex(const Scene *scene, Brush *br, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool); } + CLAMP(intensity, 0.0f, 1.0f); + + switch (br->mask_pressure) { + case BRUSH_MASK_PRESSURE_CUTOFF: + intensity = ((1.0f - intensity) < ups->size_pressure_value) ? 1.0f : 0.0f; + break; + case BRUSH_MASK_PRESSURE_RAMP: + intensity = ups->size_pressure_value + intensity * (1.0f - ups->size_pressure_value); + break; + default: + break; + } + return intensity; } -/* Unified Size and Strength */ +/* Unified Size / Strength / Color */ /* XXX: be careful about setting size and unprojected radius * because they depend on one another @@ -760,6 +781,29 @@ float BKE_brush_sample_masktex(const Scene *scene, Brush *br, * In any case, a better solution is needed to prevent * inconsistency. */ + +float *BKE_brush_color_get(const struct Scene *scene, struct Brush *brush) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + return (ups->flag & UNIFIED_PAINT_COLOR) ? ups->rgb : brush->rgb; +} + +float *BKE_brush_secondary_color_get(const struct Scene *scene, struct Brush *brush) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + return (ups->flag & UNIFIED_PAINT_COLOR) ? ups->secondary_rgb : brush->secondary_rgb; +} + +void BKE_brush_color_set(struct Scene *scene, struct Brush *brush, const float color[3]) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + if (ups->flag & UNIFIED_PAINT_COLOR) + copy_v3_v3(ups->rgb, color); + else + copy_v3_v3(brush->rgb, color); +} + void BKE_brush_size_set(Scene *scene, Brush *brush, int size) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 3f8edbcf1be..ca4a4b3196c 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -51,6 +51,7 @@ #include "BKE_editmesh.h" #include "BKE_curve.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -669,7 +670,7 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, DMSetDrawOptionsTex drawParams, DMSetDrawOptions drawParamsMapped, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag uvflag) { CDDerivedMesh *cddm = (CDDerivedMesh *) dm; MVert *mv = cddm->mvert; @@ -680,6 +681,7 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, MCol *mcol; int i, orig; int colType, startFace = 0; + bool use_tface = (uvflag & DM_DRAW_USE_ACTIVE_UV) != 0; /* double lookup */ const int *index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); @@ -718,14 +720,35 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, cdDM_update_normals_from_pbvh(dm); if (GPU_buffer_legacy(dm)) { + int mat_nr_cache = -1; + MTFace *tf_base = DM_get_tessface_data_layer(dm, CD_MTFACE); + MTFace *tf_stencil_base = NULL; + MTFace *tf_stencil = NULL; + + if (uvflag & DM_DRAW_USE_TEXPAINT_UV) { + int stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE); + tf_stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil); + } + DEBUG_VBO("Using legacy code. cdDM_drawFacesTex_common\n"); for (i = 0; i < dm->numTessFaceData; i++, mf++) { MVert *mvert; DMDrawOption draw_option; unsigned char *cp = NULL; + if (uvflag & DM_DRAW_USE_TEXPAINT_UV) { + if (mf->mat_nr != mat_nr_cache) { + tf_base = DM_paint_uvlayer_active_get(dm, mf->mat_nr); + + mat_nr_cache = mf->mat_nr; + } + } + + tf = tf_base ? tf_base + i : NULL; + tf_stencil = tf_stencil_base ? tf_stencil_base + i : NULL; + if (drawParams) { - draw_option = drawParams(tf ? &tf[i] : NULL, (mcol != NULL), mf->mat_nr); + draw_option = drawParams(use_tface ? tf : NULL, (mcol != NULL), mf->mat_nr); } else { if (index_mf_to_mpoly) { @@ -778,21 +801,24 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, } glBegin(mf->v4 ? GL_QUADS : GL_TRIANGLES); - if (tf) glTexCoord2fv(tf[i].uv[0]); + if (tf) glTexCoord2fv(tf->uv[0]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf->uv[0]); if (cp) glColor3ub(cp[3], cp[2], cp[1]); mvert = &mv[mf->v1]; if (lnors) glNormal3sv((const GLshort *)lnors[0][0]); else if (mf->flag & ME_SMOOTH) glNormal3sv(mvert->no); glVertex3fv(mvert->co); - if (tf) glTexCoord2fv(tf[i].uv[1]); + if (tf) glTexCoord2fv(tf->uv[1]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf->uv[1]); if (cp) glColor3ub(cp[7], cp[6], cp[5]); mvert = &mv[mf->v2]; if (lnors) glNormal3sv((const GLshort *)lnors[0][1]); else if (mf->flag & ME_SMOOTH) glNormal3sv(mvert->no); glVertex3fv(mvert->co); - if (tf) glTexCoord2fv(tf[i].uv[2]); + if (tf) glTexCoord2fv(tf->uv[2]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf->uv[2]); if (cp) glColor3ub(cp[11], cp[10], cp[9]); mvert = &mv[mf->v3]; if (lnors) glNormal3sv((const GLshort *)lnors[0][2]); @@ -800,7 +826,8 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, glVertex3fv(mvert->co); if (mf->v4) { - if (tf) glTexCoord2fv(tf[i].uv[3]); + if (tf) glTexCoord2fv(tf->uv[3]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf->uv[3]); if (cp) glColor3ub(cp[15], cp[14], cp[13]); mvert = &mv[mf->v4]; if (lnors) glNormal3sv((const GLshort *)lnors[0][3]); @@ -819,7 +846,10 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, else { /* use OpenGL VBOs or Vertex Arrays instead for better, faster rendering */ GPU_vertex_setup(dm); GPU_normal_setup(dm); - GPU_uv_setup(dm); + if (uvflag & DM_DRAW_USE_TEXPAINT_UV) + GPU_texpaint_uv_setup(dm); + else + GPU_uv_setup(dm); if (mcol) { GPU_color_setup(dm, colType); } @@ -839,7 +869,7 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, next_actualFace = dm->drawObject->triangle_to_mface[i + 1]; if (drawParams) { - draw_option = drawParams(tf ? &tf[actualFace] : NULL, (mcol != NULL), mf[actualFace].mat_nr); + draw_option = drawParams(use_tface && tf ? &tf[actualFace] : NULL, (mcol != NULL), mf[actualFace].mat_nr); } else { if (index_mf_to_mpoly) { @@ -895,9 +925,9 @@ static void cdDM_drawFacesTex_common(DerivedMesh *dm, static void cdDM_drawFacesTex(DerivedMesh *dm, DMSetDrawOptionsTex setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag uvflag) { - cdDM_drawFacesTex_common(dm, setDrawOptions, NULL, compareDrawOptions, userData); + cdDM_drawFacesTex_common(dm, setDrawOptions, NULL, compareDrawOptions, userData, uvflag); } static void cdDM_drawMappedFaces(DerivedMesh *dm, @@ -1123,9 +1153,9 @@ static void cdDM_drawMappedFaces(DerivedMesh *dm, static void cdDM_drawMappedFacesTex(DerivedMesh *dm, DMSetDrawOptions setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag flag) { - cdDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData); + cdDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData, flag); } static void cddm_draw_attrib_vertex(DMVertexAttribs *attribs, MVert *mvert, int a, int index, int vert, diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 136ca0098cb..4fa4031382b 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -2472,6 +2472,17 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id) BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH); } + if (ELEM(idtype, ID_MA, ID_TE)) { + const bool new_shading_nodes = BKE_scene_use_new_shading_nodes(sce); + for (obt = bmain->object.first; obt; obt = obt->id.next) { + if (obt->mode & OB_MODE_TEXTURE_PAINT) { + obt->recalc |= OB_RECALC_DATA; + BKE_texpaint_slots_refresh_object(obt, new_shading_nodes); + lib_id_recalc_data_tag(bmain, &obt->id); + } + } + } + if (idtype == ID_MC) { MovieClip *clip = (MovieClip *) id; diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 3a11b3431ae..40a4bc22ce9 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -911,7 +911,7 @@ static void emDM_drawFacesTex_common(DerivedMesh *dm, static void emDM_drawFacesTex(DerivedMesh *dm, DMSetDrawOptionsTex setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag UNUSED(flag)) { emDM_drawFacesTex_common(dm, setDrawOptions, NULL, compareDrawOptions, userData); } @@ -919,7 +919,7 @@ static void emDM_drawFacesTex(DerivedMesh *dm, static void emDM_drawMappedFacesTex(DerivedMesh *dm, DMSetDrawOptions setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag UNUSED(flag)) { emDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData); } diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 2e201a0b8e8..1b7a03ec80e 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -73,6 +73,8 @@ static IDType idtypes[] = { { ID_NT, "NodeTree", "node_groups", IDTYPE_FLAGS_ISLINKABLE }, { ID_OB, "Object", "objects", IDTYPE_FLAGS_ISLINKABLE }, { ID_PA, "ParticleSettings", "particles", 0 }, + { ID_PAL, "Palettes", "palettes", IDTYPE_FLAGS_ISLINKABLE }, + { ID_PC, "PaintCurve", "paint_curves", IDTYPE_FLAGS_ISLINKABLE }, { ID_SCE, "Scene", "scenes", IDTYPE_FLAGS_ISLINKABLE }, { ID_SCR, "Screen", "screens", 0 }, { ID_SEQ, "Sequence", "sequences", 0 }, /* not actually ID data */ diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index df343459d2f..8e07564dec4 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -104,6 +104,7 @@ #include "BKE_mask.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_packedFile.h" #include "BKE_speaker.h" @@ -515,6 +516,10 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->mask); case ID_LS: return &(mainlib->linestyle); + case ID_PAL: + return &(mainlib->palettes); + case ID_PC: + return &(mainlib->paintcurves); } return NULL; } @@ -596,6 +601,8 @@ int set_listbasepointers(Main *main, ListBase **lb) lb[a++] = &(main->text); lb[a++] = &(main->sound); lb[a++] = &(main->group); + lb[a++] = &(main->palettes); + lb[a++] = &(main->paintcurves); lb[a++] = &(main->brush); lb[a++] = &(main->script); lb[a++] = &(main->particle); @@ -731,6 +738,12 @@ static ID *alloc_libblock_notest(short type) case ID_LS: id = MEM_callocN(sizeof(FreestyleLineStyle), "Freestyle Line Style"); break; + case ID_PAL: + id = MEM_callocN(sizeof(Palette), "Palette"); + break; + case ID_PC: + id = MEM_callocN(sizeof(PaintCurve), "Paint Curve"); + break; } return id; } @@ -1007,6 +1020,12 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, bool do_id_user) case ID_LS: BKE_linestyle_free((FreestyleLineStyle *)id); break; + case ID_PAL: + BKE_palette_free((Palette *)id); + break; + case ID_PC: + BKE_paint_curve_free((PaintCurve *)id); + break; } /* avoid notifying on removed data */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index b8788a2bffb..b3e0f16f604 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -111,6 +111,9 @@ void BKE_material_free_ex(Material *ma, bool do_id_user) MEM_freeN(ma->nodetree); } + if (ma->texpaintslot) + MEM_freeN(ma->texpaintslot); + if (ma->gpumaterial.first) GPU_material_free(ma); } @@ -269,7 +272,9 @@ Material *localize_material(Material *ma) if (ma->ramp_col) man->ramp_col = MEM_dupallocN(ma->ramp_col); if (ma->ramp_spec) man->ramp_spec = MEM_dupallocN(ma->ramp_spec); - + + if (ma->texpaintslot) man->texpaintslot = MEM_dupallocN(man->texpaintslot); + man->preview = NULL; if (ma->nodetree) @@ -1301,6 +1306,114 @@ bool object_remove_material_slot(Object *ob) return true; } +void BKE_texpaint_slots_clear(struct Material *ma) +{ + + if (ma->texpaintslot) { + MEM_freeN(ma->texpaintslot); + ma->texpaintslot = NULL; + } + ma->tot_slots = 0; + ma->paint_active_slot = 0; + ma->paint_clone_slot = 0; +} + + +static bool get_mtex_slot_valid_texpaint(struct MTex *mtex) +{ + return (mtex && (mtex->texco == TEXCO_UV) && + mtex->tex && (mtex->tex->type == TEX_IMAGE) && + mtex->tex->ima); +} + +void BKE_texpaint_slot_refresh_cache(Material *ma, bool use_nodes) +{ + MTex **mtex; + short count = 0; + short index = 0, i; + + if (!ma) + return; + + if (ma->texpaintslot) { + MEM_freeN(ma->texpaintslot); + ma->texpaintslot = NULL; + } + + if (use_nodes) { + bNode *node, *active_node; + + if (!(ma->use_nodes && ma->nodetree)) + return; + + for (node = ma->nodetree->nodes.first; node; node = node->next) { + if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) + count++; + } + + ma->tot_slots = count; + + if (count == 0) { + ma->paint_active_slot = 0; + return; + } + ma->texpaintslot = MEM_callocN(sizeof(*ma->texpaintslot) * count, "texpaint_slots"); + + active_node = nodeGetActiveTexture(ma->nodetree); + + for (node = ma->nodetree->nodes.first; node; node = node->next) { + if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) { + if (active_node == node) + ma->paint_active_slot = index; + ma->texpaintslot[index++].ima = (Image *)node->id; + } + } + } + else { + for (mtex = ma->mtex, i = 0; i < MAX_MTEX; i++, mtex++) { + if (get_mtex_slot_valid_texpaint(*mtex)) { + count++; + } + } + + ma->tot_slots = count; + + if (count == 0) { + ma->paint_active_slot = 0; + return; + } + + ma->texpaintslot = MEM_callocN(sizeof(*ma->texpaintslot) * count, "texpaint_slots"); + + for (mtex = ma->mtex, i = 0; i < MAX_MTEX; i++, mtex++) { + if (get_mtex_slot_valid_texpaint(*mtex)) { + ma->texpaintslot[index].ima = (*mtex)->tex->ima; + BLI_strncpy(ma->texpaintslot[index++].uvname, (*mtex)->uvname, 64); + } + } + } + + if (ma->paint_active_slot >= count) { + ma->paint_active_slot = count - 1; + } + + if (ma->paint_clone_slot >= count) { + ma->paint_clone_slot = count - 1; + } + + return; +} + +void BKE_texpaint_slots_refresh_object(struct Object *ob, bool use_nodes) +{ + int i; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + BKE_texpaint_slot_refresh_cache(ma, use_nodes); + } +} + /* r_col = current value, col = new value, (fac == 0) is no change */ void ramp_blend(int type, float r_col[3], const float fac, const float col[3]) diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 9a144b5461a..48211f2f627 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -45,8 +45,10 @@ #include "BLI_bitmap.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_listbase.h" #include "BKE_brush.h" +#include "BKE_main.h" #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_depsgraph.h" @@ -269,6 +271,105 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +void BKE_paint_curve_free(PaintCurve *pc) +{ + if (pc->points) { + MEM_freeN(pc->points); + pc->points = NULL; + pc->tot_points = 0; + } +} + +PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) +{ + PaintCurve *pc; + + pc = BKE_libblock_alloc(bmain, ID_PC, name); + + return pc; +} + +Palette *BKE_paint_palette(Paint *p) +{ + return p ? p->palette : NULL; +} + +void BKE_paint_palette_set(Paint *p, Palette *palette) +{ + if (p) { + id_us_min((ID *)p->palette); + id_us_plus((ID *)palette); + p->palette = palette; + } +} + +void BKE_paint_curve_set(Brush *br, PaintCurve *pc) +{ + if (br) { + id_us_min((ID *)br->paint_curve); + id_us_plus((ID *)pc); + br->paint_curve = pc; + } +} + +/* remove colour from palette. Must be certain color is inside the palette! */ +void BKE_palette_color_remove(Palette *palette, PaletteColor *color) +{ + BLI_remlink(&palette->colors, color); + BLI_addhead(&palette->deleted, color); +} + +void BKE_palette_cleanup(Palette *palette) +{ + BLI_freelistN(&palette->deleted); +} + + +Palette *BKE_palette_add(Main *bmain, const char *name) +{ + Palette *palette; + + palette = BKE_libblock_alloc(bmain, ID_PAL, name); + + /* enable fake user by default */ + palette->id.flag |= LIB_FAKEUSER; + + return palette; +} + +void BKE_palette_free(Palette *palette) +{ + BLI_freelistN(&palette->colors); +} + +PaletteColor *BKE_palette_color_add(Palette *palette) +{ + PaletteColor *color = MEM_callocN(sizeof(*color), "Pallete Color"); + BLI_addtail(&palette->colors, color); + palette->active_color = BLI_countlist(&palette->colors) - 1; + return color; +} + +void BKE_palette_color_delete(struct Palette *palette) +{ + PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); + + if(color) { + if ((color == palette->colors.last) && (palette->colors.last != palette->colors.first)) + palette->active_color--; + + BLI_remlink(&palette->colors, color); + BLI_addhead(&palette->deleted, 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) { @@ -318,6 +419,7 @@ void BKE_paint_init(Paint *p, const char col[3]) void BKE_paint_free(Paint *paint) { id_us_min((ID *)paint->brush); + id_us_min((ID *)paint->palette); } /* called when copying scene settings, so even if 'src' and 'tar' are the same @@ -328,6 +430,7 @@ void BKE_paint_copy(Paint *src, Paint *tar) { tar->brush = src->brush; id_us_plus((ID *)tar->brush); + id_us_plus((ID *)tar->palette); } /* returns non-zero if any of the face's vertices diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 11dc9150d8f..79c348ac181 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -41,6 +41,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -2291,18 +2292,23 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, DMSetDrawOptionsTex drawParams, DMSetDrawOptions drawParamsMapped, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag flag) { CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm; CCGSubSurf *ss = ccgdm->ss; CCGKey key; MCol *mcol = dm->getTessFaceDataArray(dm, CD_PREVIEW_MCOL); MTFace *tf = DM_get_tessface_data_layer(dm, CD_MTFACE); + MTFace *tf_stencil_base = NULL; + MTFace *tf_stencil = NULL; + MTFace *tf_base; short (*lnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL); DMFlagMat *faceFlags = ccgdm->faceFlags; DMDrawOption draw_option; int i, totface, gridSize = ccgSubSurf_getGridSize(ss); int gridFaces = gridSize - 1; + int gridOffset = 0; + int mat_nr_cache = -1; (void) compareDrawOptions; @@ -2316,6 +2322,12 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, mcol = dm->getTessFaceDataArray(dm, CD_TEXTURE_MCOL); totface = ccgSubSurf_getNumFaces(ss); + + if (flag & DM_DRAW_USE_TEXPAINT_UV) { + int stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE); + tf_stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil); + } + for (i = 0; i < totface; i++) { CCGFace *f = ccgdm->faceMap[i].face; int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f); @@ -2334,6 +2346,18 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, mat_nr = 0; } + /* texture painting, handle the correct uv layer here */ + if (flag & DM_DRAW_USE_TEXPAINT_UV) { + if (mat_nr != mat_nr_cache) { + tf_base = DM_paint_uvlayer_active_get(dm, mat_nr); + + mat_nr_cache = mat_nr; + } + tf = tf_base + gridOffset; + tf_stencil = tf_stencil_base + gridOffset; + gridOffset += gridFaces * gridFaces * numVerts; + } + if (drawParams) draw_option = drawParams(tf, (mcol != NULL), mat_nr); else if (index != ORIGINDEX_NONE) @@ -2374,26 +2398,31 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, float *d_co = CCG_grid_elem_co(&key, faceGridData, x, y + 1); if (tf) glTexCoord2fv(tf->uv[1]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[1]); if (cp) glColor3ub(cp[7], cp[6], cp[5]); glNormal3sv(ln[0][1]); glVertex3fv(d_co); if (tf) glTexCoord2fv(tf->uv[2]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[2]); if (cp) glColor3ub(cp[11], cp[10], cp[9]); glNormal3sv(ln[0][2]); glVertex3fv(c_co); if (tf) glTexCoord2fv(tf->uv[3]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[3]); if (cp) glColor3ub(cp[15], cp[14], cp[13]); glNormal3sv(ln[0][3]); glVertex3fv(b_co); if (tf) glTexCoord2fv(tf->uv[0]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[0]); if (cp) glColor3ub(cp[3], cp[2], cp[1]); glNormal3sv(ln[0][0]); glVertex3fv(a_co); if (tf) tf++; + if (tf_stencil) tf_stencil++; if (cp) cp += 16; ln++; } @@ -2409,17 +2438,20 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, b = CCG_grid_elem(&key, faceGridData, x, y + 1); if (tf) glTexCoord2fv(tf->uv[0]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[0]); if (cp) glColor3ub(cp[3], cp[2], cp[1]); glNormal3fv(CCG_elem_no(&key, a)); glVertex3fv(CCG_elem_co(&key, a)); if (tf) glTexCoord2fv(tf->uv[1]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[1]); if (cp) glColor3ub(cp[7], cp[6], cp[5]); glNormal3fv(CCG_elem_no(&key, b)); glVertex3fv(CCG_elem_co(&key, b)); if (x != gridFaces - 1) { if (tf) tf++; + if (tf_stencil) tf_stencil++; if (cp) cp += 16; } } @@ -2428,16 +2460,19 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, b = CCG_grid_elem(&key, faceGridData, x, y + 1); if (tf) glTexCoord2fv(tf->uv[3]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[3]); if (cp) glColor3ub(cp[15], cp[14], cp[13]); glNormal3fv(CCG_elem_no(&key, a)); glVertex3fv(CCG_elem_co(&key, a)); if (tf) glTexCoord2fv(tf->uv[2]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[2]); if (cp) glColor3ub(cp[11], cp[10], cp[9]); glNormal3fv(CCG_elem_no(&key, b)); glVertex3fv(CCG_elem_co(&key, b)); if (tf) tf++; + if (tf_stencil) tf_stencil++; if (cp) cp += 16; glEnd(); @@ -2456,22 +2491,27 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, ccgDM_glNormalFast(a_co, b_co, c_co, d_co); if (tf) glTexCoord2fv(tf->uv[1]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[1]); if (cp) glColor3ub(cp[7], cp[6], cp[5]); glVertex3fv(d_co); if (tf) glTexCoord2fv(tf->uv[2]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[2]); if (cp) glColor3ub(cp[11], cp[10], cp[9]); glVertex3fv(c_co); if (tf) glTexCoord2fv(tf->uv[3]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[3]); if (cp) glColor3ub(cp[15], cp[14], cp[13]); glVertex3fv(b_co); if (tf) glTexCoord2fv(tf->uv[0]); + if (tf_stencil) glMultiTexCoord2fv(GL_TEXTURE1, tf_stencil->uv[0]); if (cp) glColor3ub(cp[3], cp[2], cp[1]); glVertex3fv(a_co); if (tf) tf++; + if (tf_stencil) tf_stencil++; if (cp) cp += 16; } } @@ -2484,17 +2524,17 @@ static void ccgDM_drawFacesTex_common(DerivedMesh *dm, static void ccgDM_drawFacesTex(DerivedMesh *dm, DMSetDrawOptionsTex setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag flag) { - ccgDM_drawFacesTex_common(dm, setDrawOptions, NULL, compareDrawOptions, userData); + ccgDM_drawFacesTex_common(dm, setDrawOptions, NULL, compareDrawOptions, userData, flag); } static void ccgDM_drawMappedFacesTex(DerivedMesh *dm, DMSetDrawOptions setDrawOptions, DMCompareDrawOptions compareDrawOptions, - void *userData) + void *userData, DMDrawFlag flag) { - ccgDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData); + ccgDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData, flag); } static void ccgDM_drawUVEdges(DerivedMesh *dm) diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 105065e85cf..77f62771360 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -474,7 +474,8 @@ void BKE_texture_free(Tex *tex) void default_tex(Tex *tex) { - tex->type = TEX_CLOUDS; + tex->type = TEX_IMAGE; + tex->ima = NULL; tex->stype = 0; tex->flag = TEX_CHECKER_ODD; tex->imaflag = TEX_INTERPOL | TEX_MIPMAP | TEX_USEALPHA; @@ -592,7 +593,7 @@ Tex *add_texture(Main *bmain, const char *name) void default_mtex(MTex *mtex) { - mtex->texco = TEXCO_ORCO; + mtex->texco = TEXCO_UV; mtex->mapto = MAP_COL; mtex->object = NULL; mtex->projx = PROJ_X; diff --git a/source/blender/blenlib/BLI_math_color_blend.h b/source/blender/blenlib/BLI_math_color_blend.h index d7e9bf50eae..2535a31ccc4 100644 --- a/source/blender/blenlib/BLI_math_color_blend.h +++ b/source/blender/blenlib/BLI_math_color_blend.h @@ -49,6 +49,24 @@ MINLINE void blend_color_lighten_byte(unsigned char dst[4], const unsigned char MINLINE void blend_color_darken_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4]); MINLINE void blend_color_erase_alpha_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4]); MINLINE void blend_color_add_alpha_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4]); + +MINLINE void blend_color_overlay_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_hardlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_burn_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_linearburn_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_dodge_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_screen_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_softlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_pinlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_linearlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_vividlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_difference_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_exclusion_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_color_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_hue_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_saturation_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); +MINLINE void blend_color_luminosity_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]); + MINLINE void blend_color_interpolate_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], float t); MINLINE void blend_color_mix_float(float dst[4], const float src1[4], const float src2[4]); @@ -59,6 +77,24 @@ MINLINE void blend_color_lighten_float(float dst[4], const float src1[4], const MINLINE void blend_color_darken_float(float dst[4], const float src1[4], const float src2[4]); MINLINE void blend_color_erase_alpha_float(float dst[4], const float src1[4], const float src2[4]); MINLINE void blend_color_add_alpha_float(float dst[4], const float src1[4], const float src2[4]); + +MINLINE void blend_color_overlay_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_hardlight_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_burn_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_linearburn_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_dodge_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_screen_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_softlight_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_pinlight_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_linearlight_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_vividlight_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_difference_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_exclusion_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_color_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_hue_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_saturation_float(float dst[4], const float src1[4], const float src2[2]); +MINLINE void blend_color_luminosity_float(float dst[4], const float src1[4], const float src2[2]); + MINLINE void blend_color_interpolate_float(float dst[4], const float src1[4], const float src2[4], float t); #if BLI_MATH_DO_INLINE diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 942097e1ed6..6885a5aa351 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -65,6 +65,7 @@ MINLINE void swap_v4_v4(float a[4], float b[4]); MINLINE void copy_v2_v2_char(char r[2], const char a[2]); MINLINE void copy_v3_v3_char(char r[3], const char a[3]); MINLINE void copy_v4_v4_char(char r[4], const char a[4]); + /* short */ MINLINE void copy_v2_v2_short(short r[2], const short a[2]); MINLINE void copy_v3_v3_short(short r[3], const short a[3]); @@ -231,6 +232,7 @@ MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_R MINLINE bool compare_v2v2(const float a[2], const float b[2], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_len_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT; +MINLINE bool compare_len_squared_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_v4v4(const float a[4], const float b[4], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/math_color_blend_inline.c b/source/blender/blenlib/intern/math_color_blend_inline.c index 4810fe757fa..1985e061172 100644 --- a/source/blender/blenlib/intern/math_color_blend_inline.c +++ b/source/blender/blenlib/intern/math_color_blend_inline.c @@ -30,11 +30,16 @@ #include "BLI_math_base.h" #include "BLI_math_color.h" #include "BLI_math_color_blend.h" +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #ifndef __MATH_COLOR_BLEND_INLINE_C__ #define __MATH_COLOR_BLEND_INLINE_C__ +/* don't add any saturation to a completly black and white image */ +#define EPS_SATURATION 0.0005f +#define EPS_ALPHA 0.0005f + /***************************** Color Blending ******************************** * * - byte colors are assumed to be straight alpha @@ -67,10 +72,7 @@ MINLINE void blend_color_mix_byte(unsigned char dst[4], const unsigned char src1 } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -92,10 +94,7 @@ MINLINE void blend_color_add_byte(unsigned char dst[4], const unsigned char src1 } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -117,10 +116,7 @@ MINLINE void blend_color_sub_byte(unsigned char dst[4], const unsigned char src1 } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -143,10 +139,7 @@ MINLINE void blend_color_mul_byte(unsigned char dst[4], const unsigned char src1 } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -169,10 +162,7 @@ MINLINE void blend_color_lighten_byte(unsigned char dst[4], const unsigned char } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -195,10 +185,7 @@ MINLINE void blend_color_darken_byte(unsigned char dst[4], const unsigned char s } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -215,10 +202,7 @@ MINLINE void blend_color_erase_alpha_byte(unsigned char dst[4], const unsigned c } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -235,11 +219,547 @@ MINLINE void blend_color_add_alpha_byte(unsigned char dst[4], const unsigned cha } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + +MINLINE void blend_color_overlay_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + if (src1[0] > 127) + temp = 255 - ((255 - 2 * (src1[0] - 127)) * (255 - src2[0]) / 255); + else + temp = (2 * src1[0] * src2[0]) >> 8; + temp = (temp * fac + src1[0] * mfac) / 255; + if (temp < 255) + dst[0] = temp; + else + dst[0] = 255; + if (src1[1] > 127) + temp = 255 - ((255 - 2 * (src1[1] - 127)) * (255 - src2[1]) / 255); + else + temp = (2 * src1[1] * src2[1]) / 255; + + temp = (temp * fac + src1[1] * mfac) / 255; + if (temp < 255) + dst[1] = temp; + else + dst[1] = 255; + + if (src1[2] > 127) + temp = 255 - ((255 - 2 * (src1[2] - 127)) * (255 - src2[2]) / 255); + else + temp = (2 * src1[2] * src2[2]) / 255; + + temp = (temp * fac + src1[2] * mfac) / 255; + if (temp < 255) + dst[2] = temp; + else + dst[2] = 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_hardlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + if (src2[0] > 127) + temp = 255 - ((255 - 2 * (src2[0] - 127)) * (255 - src1[0]) / 255); + else + temp = (2 * src2[0] * src1[0]) >> 8; + temp = (temp * fac + src1[0] * mfac) / 255; + if (temp < 255) dst[0] = temp; else dst[0] = 255; + + + if (src2[1] > 127) + temp = 255 - ((255 - 2 * (src2[1] - 127)) * (255 - src1[1]) / 255); + else + temp = (2 * src2[1] * src1[1]) / 255; + temp = (temp * fac + src1[1] * mfac) / 255; + if (temp < 255) dst[1] = temp; else dst[1] = 255; + + + if (src2[2] > 127) + temp = 255 - ((255 - 2 * (src2[2] - 127)) * (255 - src1[2]) / 255); + else + temp = (2 * src2[2] * src1[2]) / 255; + + temp = (temp * fac + src1[2] * mfac) / 255; + if (temp < 255) dst[2] = temp; else dst[2] = 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_burn_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + + if (src2[0] == 0) + temp = 0; + else + temp = 255 - ((255 - src1[0]) * 255) / src2[0]; + if (temp < 0) + temp = 0; + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + + if (src2[1] == 0) + temp = 0; + else + temp = 255 - ((255 - src1[1]) * 255) / src2[1]; + if (temp < 0) + temp = 0; + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + + if (src2[2] == 0) + temp = 0; + else + temp = 255 - ((255 - src1[2]) * 255) / src2[2]; + if (temp < 0) + temp = 0; + dst[2] = (temp * fac + src1[2] * mfac) / 255; + + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_linearburn_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + temp = src1[0] + src2[0] - 255; + if (temp < 0) temp = 0; + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + temp = src1[1] + src2[1] - 255; + if (temp < 0) temp = 0; + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + temp = src1[2] + src2[2] - 255; + if (temp < 0) temp = 0; + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_dodge_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + if (src2[0] == 255) temp = 255; + else temp = (src1[0] * 255) / (255 - src2[0]); + if (temp > 255) temp = 255; + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + if (src2[1] == 255) temp = 255; + else temp = (src1[1] * 255) / (255 - src2[1]); + if (temp > 255) temp = 255; + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + if (src2[2] == 255) temp = 255; + else temp = (src1[2] * 255) / (255 - src2[2]); + if (temp > 255) temp = 255; + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + +MINLINE void blend_color_screen_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + temp = 255 - (((255 - src1[0]) * (255 - src2[0])) / 255); + if (temp < 0) temp = 0; + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + temp = 255 - (((255 - src1[1]) * (255 - src2[1])) / 255); + if (temp < 0) temp = 0; + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + temp = 255 - (((255 - src1[2]) * (255 - src2[2])) / 255); + if (temp < 0) temp = 0; + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_softlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + temp = ((unsigned char)((src1[0] < 127) ? ((2 * ((src2[0] / 2) + 64)) * (src1[0])) / 255 : (255 - (2 * (255 - ((src2[0] / 2) + 64)) * (255 - src1[0]) / 255)))); + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + temp = ((unsigned char)((src1[1] < 127) ? ((2 * ((src2[1] / 2) + 64)) * (src1[1])) / 255 : (255 - (2 * (255 - ((src2[1] / 2) + 64)) * (255 - src1[1]) / 255)))); + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + temp = ((unsigned char)((src1[2] < 127) ? ((2 * ((src2[2] / 2) + 64)) * (src1[2])) / 255 : (255 - (2 * (255 - ((src2[2] / 2) + 64)) * (255 - src1[2]) / 255)))); + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_pinlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + if (src2[0] > 127) { + temp = 2 * (src2[0] - 127); + if (src1[0] > temp) + temp = src1[0]; + } + else { + temp = 2 * src2[0]; + if (src1[0] < temp) + temp = src1[0]; + } + + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + + if (src2[1] > 127) { + temp = 2 * (src2[1] - 127); + if (src1[1] > temp) temp = src1[1]; + } + else { + temp = 2 * src2[1]; + if (src1[1] < temp) temp = src1[1]; + } + + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + + if (src2[2] > 127) { + temp = 2 * (src2[2] - 127); + if (src1[2] > temp) temp = src1[2]; + } + else { + temp = 2 * src2[2]; + if (src1[2] < temp) temp = src1[2]; + } + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_linearlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + if (src2[0] > 127) { + temp = src1[0] + 2 * (src2[0] - 127); + if (temp > 255) + temp = 255; + } + else { + temp = src1[0] + 2 * src2[0] - 255; + if (temp < 0) temp = 0; + } + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + if (src2[1] > 127) { + temp = src1[1] + 2 * (src2[1] - 127); + if (temp > 255) + temp = 255; + } + else { + temp = src1[1] + 2 * src2[1] - 255; + if (temp < 0) temp = 0; + } + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + if (src2[2] > 127) { + temp = src1[2] + 2 * (src2[2] - 127); + if (temp > 255) + temp = 255; + } + else { + temp = src1[2] + 2 * src2[2] - 255; + if (temp < 0) temp = 0; + } + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_vividlight_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + + if (src2[0] == 255) + temp = 255; + else if (src2[0] == 0) + temp = 0; + else if (src2[0] > 127) { + temp = ((src1[0]) * 255) / (2 * (255 - src2[0])); + if (temp > 255) temp = 255; + } + else { + temp = 255 - ((255 - src1[0]) * 255 / (2 * src2[0])); + if (temp < 0) temp = 0; + } + + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + if (src2[1] == 255) + temp = 255; + else if (src2[1] == 0) + temp = 0; + else if (src2[1] > 127) { + temp = ((src1[1]) * 255) / (2 * (255 - src2[1])); + if (temp > 255) temp = 255; + } + else { + temp = 255 - ((255 - src1[1]) * 255 / (2 * src2[1])); + if (temp < 0) temp = 0; + } + + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + if (src2[2] == 255) + temp = 255; + else if (src2[2] == 0) + temp = 0; + else if (src2[2] > 127) { + temp = ((src1[2]) * 255) / (2 * (255 - src2[2])); + if (temp > 255) temp = 255; + } + else { + temp = 255 - ((255 - src1[2]) * 255 / (2 * src2[2])); + if (temp < 0) temp = 0; + } + + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + + +MINLINE void blend_color_difference_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + temp = src1[0] - src2[0]; + if (temp < 0) temp = -temp; + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + temp = src1[1] - src2[1]; + if (temp < 0) temp = -temp; + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + temp = src1[2] - src2[2]; + if (temp < 0) temp = -temp; + dst[2] = (temp * fac + src1[2] * mfac) / 255; + + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + + +MINLINE void blend_color_exclusion_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int temp; + int mfac = 255 - fac; + temp = 127 - ((2 * (src1[0] - 127) * (src2[0] - 127)) / 255); + dst[0] = (temp * fac + src1[0] * mfac) / 255; + + temp = 127 - ((2 * (src1[1] - 127) * (src2[1] - 127)) / 255); + dst[1] = (temp * fac + src1[1] * mfac) / 255; + + temp = 127 - ((2 * (src1[2] - 127) * (src2[2] - 127)) / 255); + dst[2] = (temp * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + +MINLINE void blend_color_color_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int mfac = 255 - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0] / 255.0f, src1[1] / 255.0f, src1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(src2[0] / 255.0f, src2[1] / 255.0f, src2[2] / 255.0f, &h2, &s2, &v2); + + + h1 = h2; + s1 = s2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = ((int)(r * 255.0f) * fac + src1[0] * mfac) / 255; + dst[1] = ((int)(g * 255.0f) * fac + src1[1] * mfac) / 255; + dst[2] = ((int)(b * 255.0f) * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + +MINLINE void blend_color_hue_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int mfac = 255 - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0] / 255.0f, src1[1] / 255.0f, src1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(src2[0] / 255.0f, src2[1] / 255.0f, src2[2] / 255.0f, &h2, &s2, &v2); + + + h1 = h2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = ((int)(r * 255.0f) * fac + src1[0] * mfac) / 255; + dst[1] = ((int)(g * 255.0f) * fac + src1[1] * mfac) / 255; + dst[2] = ((int)(b * 255.0f) * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } + +} + +MINLINE void blend_color_saturation_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int mfac = 255 - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0] / 255.0f, src1[1] / 255.0f, src1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(src2[0] / 255.0f, src2[1] / 255.0f, src2[2] / 255.0f, &h2, &s2, &v2); + + if (s1 > EPS_SATURATION) { + s1 = s2; + } + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = ((int)(r * 255.0f) * fac + src1[0] * mfac) / 255; + dst[1] = ((int)(g * 255.0f) * fac + src1[1] * mfac) / 255; + dst[2] = ((int)(b * 255.0f) * fac + src1[2] * mfac) / 255; + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); + } +} + +MINLINE void blend_color_luminosity_byte(unsigned char dst[4], unsigned const char src1[4], unsigned const char src2[4]) +{ + const unsigned char fac = src2[3]; + if (fac != 0) { + int mfac = 255 - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0] / 255.0f, src1[1] / 255.0f, src1[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(src2[0] / 255.0f, src2[1] / 255.0f, src2[2] / 255.0f, &h2, &s2, &v2); + + + v1 = v2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = ((int)(r * 255.0f) * fac + src1[0] * mfac) / 255; + dst[1] = ((int)(g * 255.0f) * fac + src1[1] * mfac) / 255; + dst[2] = ((int)(b * 255.0f) * fac + src1[2] * mfac) / 255; + + } + else { + /* no op */ + copy_v4_v4_char((char *)dst, (char *)src1); } + } MINLINE void blend_color_interpolate_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], float ft) @@ -257,10 +777,7 @@ MINLINE void blend_color_interpolate_byte(unsigned char dst[4], const unsigned c dst[3] = (unsigned char)divide_round_i(tmp, 255); } else { - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4_char((char *)dst, (char *)src1); } } @@ -280,10 +797,7 @@ MINLINE void blend_color_mix_float(float dst[4], const float src1[4], const floa } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -298,10 +812,7 @@ MINLINE void blend_color_add_float(float dst[4], const float src1[4], const floa } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -316,10 +827,7 @@ MINLINE void blend_color_sub_float(float dst[4], const float src1[4], const floa } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -337,10 +845,7 @@ MINLINE void blend_color_mul_float(float dst[4], const float src1[4], const floa } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -360,10 +865,7 @@ MINLINE void blend_color_lighten_float(float dst[4], const float src1[4], const } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -383,10 +885,7 @@ MINLINE void blend_color_darken_float(float dst[4], const float src1[4], const f } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -397,8 +896,9 @@ MINLINE void blend_color_erase_alpha_float(float dst[4], const float src1[4], co float alpha = max_ff(src1[3] - src2[3], 0.0f); float map_alpha; - if (alpha <= 0.0005f) + if (alpha <= EPS_ALPHA) { alpha = 0.0f; + } map_alpha = alpha / src1[3]; @@ -409,10 +909,7 @@ MINLINE void blend_color_erase_alpha_float(float dst[4], const float src1[4], co } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); } } @@ -423,8 +920,9 @@ MINLINE void blend_color_add_alpha_float(float dst[4], const float src1[4], cons float alpha = min_ff(src1[3] + src2[3], 1.0f); float map_alpha; - if (alpha >= 1.0f - 0.0005f) + if (alpha >= 1.0f - EPS_ALPHA) { alpha = 1.0f; + } map_alpha = (src1[3] > 0.0f) ? alpha / src1[3] : 1.0f; @@ -435,13 +933,543 @@ MINLINE void blend_color_add_alpha_float(float dst[4], const float src1[4], cons } else { /* no op */ - dst[0] = src1[0]; - dst[1] = src1[1]; - dst[2] = src1[2]; - dst[3] = src1[3]; + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_overlay_float(float dst[3], const float src1[3], const float src2[3]) +{ + float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + + float temp; + float fac = src2[3]; + float mfac = 1.0f - fac; + + if (src1[0] > 0.5f) + temp = 1 - (1 - 2 * (src1[0] - 0.5f)) * (1 - src2[0]); + else + temp = 2 * src1[0] * src2[0]; + temp = temp * fac + src1[0] * mfac; + if (temp < 1.0f) + dst[0] = temp; + else + dst[0] = 1.0f; + + + if (src1[1] > 0.5f) + temp = 1.0f - (1.0f - 2.0f * (src1[1] - 0.5f)) * (1.0f - src2[1]); + else + temp = 2.0f * src1[1] * src2[1]; + temp = temp * fac + src1[1] * mfac; + if (temp < 1.0f) + dst[1] = temp; + else + dst[1] = 1.0f; + + if (src1[2] > 0.5f) + temp = 1 - (1.0f - 2.0f * (src1[2] - 0.5f)) * (1.0f - src2[2]); + else + temp = 2.0f * src1[2] * src2[2]; + temp = temp * fac + src1[2] * mfac; + if (temp < 1.0f) + dst[2] = temp; + else + dst[2] = 1.0f; + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_hardlight_float(float dst[4], const float src1[4], const float src2[2]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + if (src2[0] > 0.5f) + temp = 1 - ((1.0f - 2.0f * (src2[0] - 0.5f)) * (1.0f - src1[0])); + else + temp = 2.0f * src2[0] * src1[0]; + temp = (temp * fac + src1[0] * mfac) / 1.0f; + if (temp < 1.0f) dst[0] = temp; else dst[0] = 1.0f; + + if (src2[1] > 0.5f) + temp = 1 - ((1.0f - 2.0f * (src2[1] - 0.5f)) * (1.0f - src1[1])); + else + temp = 2.0f * src2[1] * src1[1]; + temp = (temp * fac + src1[1] * mfac) / 1.0f; + if (temp < 1.0f) dst[1] = temp; else dst[1] = 1.0f; + + if (src2[2] > 0.5f) + temp = 1 - ((1.0f - 2.0f * (src2[2] - 0.5f)) * (1.0f - src1[2])); + else + temp = 2.0f * src2[2] * src1[2]; + temp = (temp * fac + src1[2] * mfac) / 1.0f; + + if (temp < 1.0f) + dst[2] = temp; + else + dst[2] = 1.0f; + + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_burn_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + if (src2[0] == 0.0f) + temp = 0.0f; + else + temp = 1.0f - ((1.0f - src1[0]) / src2[0]); + if (temp < 0.0f) + temp = 0.0f; + dst[0] = (temp * fac + src1[0] * mfac); + + if (src2[1] == 0.0f) + temp = 0.0f; + else + temp = 1.0f - ((1.0f - src1[1]) / src2[1]); + if (temp < 0.0f) + temp = 0.0f; + dst[1] = (temp * fac + src1[1] * mfac); + + if (src2[2] == 0.0f) + temp = 0.0f; + else + temp = 1.0f - ((1.0f - src1[2]) / src2[2]); + if (temp < 0.0f) + temp = 0.0f; + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); } } +MINLINE void blend_color_linearburn_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + temp = src1[0] + src2[0] - 1.0f; + if (temp < 0) temp = 0; + dst[0] = (temp * fac + src1[0] * mfac); + + temp = src1[1] + src2[1] - 1.0f; + if (temp < 0) temp = 0; + dst[1] = (temp * fac + src1[1] * mfac); + + temp = src1[2] + src2[2] - 1.0f; + if (temp < 0) temp = 0; + dst[2] = (temp * fac + src1[2] * mfac); + + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_dodge_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + if (src2[0] >= 1.0f) temp = 1.0f; + else temp = (src1[0]) / (1.0f - src2[0]); + if (temp > 1.0f) temp = 1.0f; + dst[0] = (temp * fac + src1[0] * mfac); + + if (src2[1] >= 1.0f) temp = 1.0f; + else temp = (src1[1]) / (1.0f - src2[1]); + if (temp > 1.0f) temp = 1.0f; + dst[1] = (temp * fac + src1[1] * mfac); + + if (src2[2] >= 1.0f) temp = 1.0f; + else temp = (src1[2]) / (1.0f - src2[2]); + if (temp > 1.0f) temp = 1.0f; + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_screen_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + temp = 1.0f - ((1.0f - src1[0]) * (1.0f - src2[0])); + if (temp < 0) temp = 0; + dst[0] = (temp * fac + src1[0] * mfac); + + temp = 1.0f - ((1.0f - src1[1]) * (1.0f - src2[1])); + if (temp < 0) temp = 0; + dst[1] = (temp * fac + src1[1] * mfac); + + temp = 1.0f - ((1.0f - src1[2]) * (1.0f - src2[2])); + if (temp < 0) temp = 0; + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_softlight_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + temp = (((src1[0] < 0.5f) ? ((src2[0] + 0.5f) * (src1[0])) : (1.0f - ((1.0f - ((src2[0]) + 0.5f)) * (1.0f - src1[0]))))); + dst[0] = (temp * fac + src1[0] * mfac); + + temp = (((src1[1] < 0.5f) ? ((src2[1] + 0.5f) * (src1[1])) : (1.0f - ((1.0f - ((src2[1]) + 0.5f)) * (1.0f - src1[1]))))); + dst[1] = (temp * fac + src1[1] * mfac); + + temp = (((src1[2] < 0.5f) ? ((src2[2] + 0.5f) * (src1[2])) : (1.0f - ((1.0f - ((src2[2]) + 0.5f)) * (1.0f - src1[2]))))); + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_pinlight_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + if (src2[0] > 0.5f) { + temp = 2 * (src2[0] - 0.5f); + if (src1[0] > temp) + temp = src1[0]; + } + else { + temp = 2 * src2[0]; + if (src1[0] < temp) + temp = src1[0]; + } + + dst[0] = (temp * fac + src1[0] * mfac); + + + if (src2[1] > 0.5f) { + temp = 2 * (src2[1] - 0.5f); + if (src1[1] > temp) temp = src1[1]; + } + else { + temp = 2 * src2[1]; + if (src1[1] < temp) temp = src1[1]; + } + + dst[1] = (temp * fac + src1[1] * mfac); + + + if (src2[2] > 0.5f) { + temp = 2 * (src2[2] - 0.5f); + if (src1[2] > temp) temp = src1[2]; + } + else { + temp = 2 * src2[2]; + if (src1[2] < temp) temp = src1[2]; + } + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_linearlight_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + if (src2[0] > 0.5f) { + temp = src1[0] + 2 * (src2[0] - 0.5f); + if (temp > 1.0f) + temp = 1.0f; + } + else { + temp = src1[0] + 2 * src2[0] - 1.0f; + if (temp < 0) temp = 0; + } + dst[0] = (temp * fac + src1[0] * mfac); + + if (src2[1] > 0.5f) { + temp = src1[1] + 2 * (src2[1] - 0.5f); + if (temp > 1.0f) + temp = 1.0f; + } + else { + temp = src1[1] + 2 * src2[1] - 1.0f; + if (temp < 0) temp = 0; + } + dst[1] = (temp * fac + src1[1] * mfac); + + if (src2[2] > 0.5f) { + temp = src1[2] + 2 * (src2[2] - 0.5f); + if (temp > 1.0f) + temp = 1.0f; + } + else { + temp = src1[2] + 2 * src2[2] - 1.0f; + if (temp < 0) temp = 0; + } + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_vividlight_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + + if (src2[0] == 1.0f) + temp = 1.0f; + else if (src2[0] == 0) + temp = 0; + else if (src2[0] > 0.5f) { + temp = ((src1[0]) * 1.0f) / (2 * (1.0f - src2[0])); + if (temp > 1.0f) temp = 1.0f; + } + else { + temp = 1.0f - ((1.0f - src1[0]) * 1.0f / (2 * src2[0])); + if (temp < 0) temp = 0; + } + + dst[0] = (temp * fac + src1[0] * mfac); + + if (src2[1] == 1.0f) + temp = 1.0f; + else if (src2[1] == 0) + temp = 0; + else if (src2[1] > 0.5f) { + temp = ((src1[1]) * 1.0f) / (2 * (1.0f - src2[1])); + if (temp > 1.0f) temp = 1.0f; + } + else { + temp = 1.0f - ((1.0f - src1[1]) * 1.0f / (2 * src2[1])); + if (temp < 0) temp = 0; + } + + dst[1] = (temp * fac + src1[1] * mfac); + + if (src2[2] == 1.0f) + temp = 1.0f; + else if (src2[2] == 0) + temp = 0; + else if (src2[2] > 0.5f) { + temp = ((src1[2]) * 1.0f) / (2 * (1.0f - src2[2])); + if (temp > 1.0f) temp = 1.0f; + } + else { + temp = 1.0f - ((1.0f - src1[2]) * 1.0f / (2 * src2[2])); + if (temp < 0) temp = 0; + } + + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_difference_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + temp = src1[0] - src2[0]; + if (temp < 0) temp = -temp; + dst[0] = (temp * fac + src1[0] * mfac); + + temp = src1[1] - src2[1]; + if (temp < 0) temp = -temp; + dst[1] = (temp * fac + src1[1] * mfac); + + temp = src1[2] - src2[2]; + if (temp < 0) temp = -temp; + dst[2] = (temp * fac + src1[2] * mfac); + + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_exclusion_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float temp; + float mfac = 1.0f - fac; + temp = 0.5f - ((2 * (src1[0] - 0.5f) * (src2[0] - 0.5f))); + dst[0] = (temp * fac + src1[0] * mfac); + + temp = 0.5f - ((2 * (src1[1] - 0.5f) * (src2[1] - 0.5f))); + dst[1] = (temp * fac + src1[1] * mfac); + + temp = 0.5f - ((2 * (src1[2] - 0.5f) * (src2[2] - 0.5f))); + dst[2] = (temp * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } + +} + +MINLINE void blend_color_color_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float mfac = 1.0f - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0], src1[1], src1[2], &h1, &s1, &v1); + rgb_to_hsv(src2[0], src2[1], src2[2], &h2, &s2, &v2); + + + h1 = h2; + s1 = s2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = (r * fac + src1[0] * mfac); + dst[1] = (g * fac + src1[1] * mfac); + dst[2] = (b * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + +MINLINE void blend_color_hue_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float mfac = 1.0f - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0], src1[1], src1[2], &h1, &s1, &v1); + rgb_to_hsv(src2[0], src2[1], src2[2], &h2, &s2, &v2); + + + h1 = h2; + + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = (r * fac + src1[0] * mfac); + dst[1] = (g * fac + src1[1] * mfac); + dst[2] = (b * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_saturation_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float mfac = 1.0f - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0], src1[1], src1[2], &h1, &s1, &v1); + rgb_to_hsv(src2[0], src2[1], src2[2], &h2, &s2, &v2); + + if (s1 > EPS_SATURATION) { + s1 = s2; + } + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = (r * fac + src1[0] * mfac); + dst[1] = (g * fac + src1[1] * mfac); + dst[2] = (b * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + +MINLINE void blend_color_luminosity_float(float dst[3], const float src1[3], const float src2[3]) +{ + const float fac = src2[3]; + if (fac != 0.0f && fac < 1.0f) { + float mfac = 1.0f - fac; + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(src1[0], src1[1], src1[2], &h1, &s1, &v1); + rgb_to_hsv(src2[0], src2[1], src2[2], &h2, &s2, &v2); + + + v1 = v2; + hsv_to_rgb(h1, s1, v1, &r, &g, &b); + + dst[0] = (r * fac + src1[0] * mfac); + dst[1] = (g * fac + src1[1] * mfac); + dst[2] = (b * fac + src1[2] * mfac); + } + else { + /* no op */ + copy_v4_v4(dst, src1); + } +} + + MINLINE void blend_color_interpolate_float(float dst[4], const float src1[4], const float src2[4], float t) { /* interpolation, colors are premultiplied so it goes fine */ @@ -453,4 +1481,7 @@ MINLINE void blend_color_interpolate_float(float dst[4], const float src1[4], co dst[3] = mt * src1[3] + t * src2[3]; } +#undef EPS_SATURATION +#undef EPS_ALPHA + #endif /* __MATH_COLOR_BLEND_INLINE_C__ */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 419f72b5fd6..d5eebdb12d2 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1824,6 +1824,7 @@ static void lib_link_brush(FileData *fd, Main *main) brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex); brush->mask_mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mask_mtex.tex); brush->clone.image = newlibadr_us(fd, brush->id.lib, brush->clone.image); + brush->paint_curve = newlibadr_us(fd, brush->id.lib, brush->paint_curve); } } } @@ -1834,6 +1835,8 @@ 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) direct_link_curvemapping(fd, brush->curve); else @@ -1843,6 +1846,43 @@ static void direct_link_brush(FileData *fd, Brush *brush) brush->icon_imbuf = NULL; } +/* ************ READ Palette *************** */ +static void lib_link_palette(FileData *UNUSED(fd), Main *main) +{ + Palette *palette; + + /* only link ID pointers */ + for (palette = main->palettes.first; palette; palette = palette->id.next) { + if (palette->id.flag & LIB_NEED_LINK) { + palette->id.flag -= LIB_NEED_LINK; + } + } +} + +static void direct_link_palette(FileData *fd, Palette *palette) +{ + /* palette itself has been read */ + link_list(fd, &palette->colors); +} + +static void lib_link_paint_curve(FileData *UNUSED(fd), Main *main) +{ + PaintCurve *pc; + + /* only link ID pointers */ + for (pc = main->paintcurves.first; pc; pc = pc->id.next) { + if (pc->id.flag & LIB_NEED_LINK) { + pc->id.flag -= LIB_NEED_LINK; + } + } +} + +static void direct_link_paint_curve(FileData *fd, PaintCurve *pc) +{ + pc->points = newdataadr(fd, pc->points); +} + + static void direct_link_script(FileData *UNUSED(fd), Script *script) { script->id.us = 1; @@ -3516,7 +3556,8 @@ static void direct_link_material(FileData *fd, Material *ma) for (a = 0; a < MAX_MTEX; a++) { ma->mtex[a] = newdataadr(fd, ma->mtex[a]); } - + ma->texpaintslot = NULL; + ma->ramp_col = newdataadr(fd, ma->ramp_col); ma->ramp_spec = newdataadr(fd, ma->ramp_spec); @@ -5059,6 +5100,7 @@ static void link_paint(FileData *fd, Scene *sce, Paint *p) { if (p) { p->brush = newlibadr_us(fd, sce->id.lib, p->brush); + p->palette = newlibadr_us(fd, sce->id.lib, p->palette); p->paint_cursor = NULL; } } @@ -5107,6 +5149,10 @@ static void lib_link_scene(FileData *fd, Main *main) sce->toolsettings->sculpt->gravity_object = newlibadr_us(fd, sce->id.lib, sce->toolsettings->sculpt->gravity_object); + if (sce->toolsettings->imapaint.stencil) + sce->toolsettings->imapaint.stencil = + newlibadr_us(fd, sce->id.lib, sce->toolsettings->imapaint.stencil); + sce->toolsettings->skgen_template = newlibadr(fd, sce->id.lib, sce->toolsettings->skgen_template); for (base = sce->base.first; base; base = next) { @@ -7138,6 +7184,8 @@ static const char *dataname(short id_code) case ID_NT: return "Data from NT"; case ID_BR: return "Data from BR"; case ID_PA: return "Data from PA"; + case ID_PAL: return "Data from PAL"; + case ID_PC: return "Data from PCRV"; case ID_GD: return "Data from GD"; case ID_WM: return "Data from WM"; case ID_MC: return "Data from MC"; @@ -7323,6 +7371,12 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID case ID_LS: direct_link_linestyle(fd, (FreestyleLineStyle *)id); break; + case ID_PAL: + direct_link_palette(fd, (Palette *)id); + break; + case ID_PC: + direct_link_paint_curve(fd, (PaintCurve *)id); + break; } oldnewmap_free_unused(fd->datamap); @@ -7511,6 +7565,8 @@ static void lib_link_all(FileData *fd, Main *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_paint_curve(fd, main); lib_link_particlesettings(fd, main); lib_link_movieclip(fd, main); lib_link_mask(fd, main); @@ -8050,6 +8106,7 @@ static void expand_brush(FileData *fd, Main *mainvar, Brush *brush) expand_doit(fd, mainvar, brush->mtex.tex); expand_doit(fd, mainvar, brush->mask_mtex.tex); expand_doit(fd, mainvar, brush->clone.image); + expand_doit(fd, mainvar, brush->paint_curve); } static void expand_material(FileData *fd, Main *mainvar, Material *ma) diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index dde16c8d44f..be6f985d701 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -34,6 +34,7 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include "DNA_brush_types.h" #include "DNA_constraint_types.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" @@ -309,6 +310,13 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) mat->line_col[3] = mat->alpha; } } + + if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { + Scene *scene; + for (scene = main->scene.first; scene; scene = scene->id.next) { + scene->r.preview_start_resolution = 64; + } + } } if (!MAIN_VERSION_ATLEAST(main, 271, 2)) { @@ -334,6 +342,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } + if (!MAIN_VERSION_ATLEAST(main, 271, 3)) { + Scene *sce; + Brush *br; + + for (sce = main->scene.first; sce; sce = sce->id.next) { + sce->toolsettings->imapaint.slot_xresolution_default = 1024; + sce->toolsettings->imapaint.slot_yresolution_default = 1024; + } + + for (br = main->brush.first; br; br = br->id.next) { + br->fill_threshold = 0.2f; + } + } + if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { Scene *scene; for (scene = main->scene.first; scene; scene = scene->id.next) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 759e17295bc..13fa6f65d0c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2924,6 +2924,38 @@ static void write_brushes(WriteData *wd, ListBase *idbase) if (brush->curve) write_curvemapping(wd, brush->curve); + if (brush->curve) + writestruct(wd, DATA, "ColorBand", 1, brush->gradient); + } + } +} + +static void write_palettes(WriteData *wd, ListBase *idbase) +{ + Palette *palette; + + for (palette = idbase->first; palette; palette = palette->id.next) { + if (palette->id.us > 0 || wd->current) { + PaletteColor *color; + writestruct(wd, ID_PAL, "Palette", 1, palette); + if (palette->id.properties) IDP_WriteProperty(palette->id.properties, wd); + + for (color = palette->colors.first; color; color= color->next) + writestruct(wd, DATA, "PaletteColor", 1, color); + } + } +} + +static void write_paintcurves(WriteData *wd, ListBase *idbase) +{ + PaintCurve *pc; + + for (pc = idbase->first; pc; pc = pc->id.next) { + if (pc->id.us > 0 || wd->current) { + writestruct(wd, ID_PC, "PaintCurve", 1, pc); + + writestruct(wd, DATA, "PaintCurvePoint", pc->tot_points, pc->points); + if (pc->id.properties) IDP_WriteProperty(pc->id.properties, wd); } } } @@ -3399,6 +3431,8 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil write_particlesettings(wd, &mainvar->particle); write_nodetrees(wd, &mainvar->nodetree); write_brushes (wd, &mainvar->brush); + write_palettes (wd, &mainvar->palettes); + write_paintcurves (wd, &mainvar->paintcurves); write_scripts (wd, &mainvar->script); write_gpencils (wd, &mainvar->gpencil); write_linestyles(wd, &mainvar->linestyle); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 3fc6e2e6f0d..2a84ca7f297 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -93,6 +93,8 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/soften.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/subtract.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/texdraw.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/texfill.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/texmask.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/thumb.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/twist.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/vertexdraw.png SRC) diff --git a/source/blender/editors/datafiles/SConscript b/source/blender/editors/datafiles/SConscript index 47819d0e33c..6bc8f21e384 100644 --- a/source/blender/editors/datafiles/SConscript +++ b/source/blender/editors/datafiles/SConscript @@ -77,6 +77,8 @@ sources.extend(( os.path.join(env['DATA_SOURCES'], "soften.png.c"), os.path.join(env['DATA_SOURCES'], "subtract.png.c"), os.path.join(env['DATA_SOURCES'], "texdraw.png.c"), + os.path.join(env['DATA_SOURCES'], "texfill.png.c"), + os.path.join(env['DATA_SOURCES'], "texmask.png.c"), os.path.join(env['DATA_SOURCES'], "thumb.png.c"), os.path.join(env['DATA_SOURCES'], "twist.png.c"), os.path.join(env['DATA_SOURCES'], "vertexdraw.png.c"), diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index 9022a1481aa..661ab58b98c 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -150,6 +150,12 @@ extern char datatoc_subtract_png[]; extern int datatoc_texdraw_png_size; extern char datatoc_texdraw_png[]; +extern int datatoc_texfill_png_size; +extern char datatoc_texfill_png[]; + +extern int datatoc_texmask_png_size; +extern char datatoc_texmask_png[]; + extern int datatoc_thumb_png_size; extern char datatoc_thumb_png[]; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index b15a83809f5..db13c628ade 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -69,8 +69,11 @@ void ED_image_point_pos__reverse(struct SpaceImage *sima, struct ARegion *ar, co bool ED_space_image_show_render(struct SpaceImage *sima); bool ED_space_image_show_paint(struct SpaceImage *sima); bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit); +bool ED_space_image_show_texpaint(struct SpaceImage *sima, struct Object *ob); bool ED_space_image_show_uvshadow(struct SpaceImage *sima, struct Object *obedit); +bool ED_space_image_paint_curve(const struct bContext *C); + bool ED_space_image_check_show_maskedit(struct Scene *scene, struct SpaceImage *sima); int ED_space_image_maskedit_poll(struct bContext *C); int ED_space_image_maskedit_mask_poll(struct bContext *C); diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index d7e84d8f50d..decd79fcc7b 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -28,9 +28,11 @@ struct bContext; struct RegionView3D; struct wmKeyConfig; +struct wmOperator; /* paint_ops.c */ void ED_operatortypes_paint(void); +void ED_operatormacros_paint(void); void ED_keymap_paint(struct wmKeyConfig *keyconf); /* paint_undo.c */ @@ -41,6 +43,7 @@ enum { typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); typedef void (*UndoFreeCb)(struct ListBase *lb); +typedef bool (*UndoCleanupCb)(struct bContext *C, struct ListBase *lb); int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name); void ED_undo_paint_step_num(struct bContext *C, int type, int num); @@ -48,7 +51,7 @@ const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, int *ac void ED_undo_paint_free(void); int ED_undo_paint_valid(int type, const char *name); bool ED_undo_paint_empty(int type); -void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free); +void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup); void ED_undo_paint_push_end(int type); /* paint_image.c */ @@ -57,5 +60,6 @@ void ED_image_undo_restore(struct bContext *C, struct ListBase *lb); void ED_image_undo_free(struct ListBase *lb); void ED_imapaint_clear_partial_redraw(void); void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op); #endif /* __ED_PAINT_H__ */ diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 41ff9b88da9..daa6864b5aa 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -98,6 +98,7 @@ enum TfmMode { #define CTX_NDOF (1 << 5) #define CTX_MOVIECLIP (1 << 6) #define CTX_MASK (1 << 7) +#define CTX_PAINT_CURVE (1 << 8) /* Standalone call to get the transformation center corresponding to the current situation * returns 1 if successful, 0 otherwise (usually means there's no selection) diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 76839747076..ed68dd72c64 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -84,6 +84,7 @@ typedef struct ViewDepths { float *ED_view3d_cursor3d_get(struct Scene *scene, struct View3D *v3d); void ED_view3d_cursor3d_position(struct bContext *C, float fp[3], const int mval[2]); +void ED_view3d_cursor3d_update(struct bContext *C, const int mval[2]); struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index c776026a811..e06b8e62f27 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -976,6 +976,8 @@ DEF_ICON(BRUSH_SNAKE_HOOK) DEF_ICON(BRUSH_SOFTEN) DEF_ICON(BRUSH_SUBTRACT) DEF_ICON(BRUSH_TEXDRAW) +DEF_ICON(BRUSH_TEXFILL) +DEF_ICON(BRUSH_TEXMASK) DEF_ICON(BRUSH_THUMB) DEF_ICON(BRUSH_ROTATE) DEF_ICON(BRUSH_VERTEXDRAW) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1565583b574..c73efa13f29 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -75,6 +75,9 @@ struct ImBuf; struct bNodeTree; struct bNode; struct bNodeSocket; +struct wmDropBox; +struct wmDrag; +struct wmEvent; typedef struct uiBut uiBut; typedef struct uiBlock uiBlock; @@ -288,6 +291,9 @@ typedef enum { #define UI_GRAD_V_ALT 9 #define UI_GRAD_L_ALT 10 +#define UI_PALETTE_COLOR 20 +#define UI_PALETTE_COLOR_ACTIVE 1 + /* Drawing * * Functions to draw various shapes, taking theme settings into account. @@ -437,6 +443,7 @@ void uiButSetDragValue(uiBut *but); void uiButSetDragImage(uiBut *but, const char *path, int icon, struct ImBuf *ima, float scale); bool UI_but_active_drop_name(struct bContext *C); +bool UI_but_active_drop_color(struct bContext *C); void uiButSetFlag(uiBut *but, int flag); void uiButClearFlag(uiBut *but, int flag); @@ -847,6 +854,7 @@ void uiTemplateVectorscope(uiLayout *layout, struct PointerRNA *ptr, const char void uiTemplateCurveMapping(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int type, int levels, int brush, int neg_slope); void uiTemplateColorPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int value_slider, int lock, int lock_luminosity, int cubic); +void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color); void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_layer); void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char *propname, @@ -916,7 +924,14 @@ void uiItemMenuEnumO(uiLayout *layout, struct bContext *C, const char *opname, c void uiItemMenuEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name, int icon); /* UI Operators */ +typedef struct uiDragColorHandle { + float color[3]; + bool gamma_corrected; +} uiDragColorHandle; + void UI_buttons_operatortypes(void); +void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); +int UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event); /* Helpers for Operators */ uiBut *uiContextActiveButton(const struct bContext *C); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 2e78940a813..da365355e95 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -237,6 +237,9 @@ enum { TH_STITCH_PREVIEW_UNSTITCHABLE, TH_STITCH_PREVIEW_ACTIVE, + TH_PAINT_CURVE_HANDLE, + TH_PAINT_CURVE_PIVOT, + TH_UV_SHADOW, TH_UV_OTHERS, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 7dfb17b3111..76c5861ce0c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -38,6 +38,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" #include "DNA_sensor_types.h" #include "DNA_controller_types.h" #include "DNA_actuator_types.h" @@ -60,6 +61,7 @@ #include "PIL_time.h" #include "BKE_blender.h" +#include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_idprop.h" @@ -1189,7 +1191,7 @@ static bool ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, const wmEvent *eve BLI_rcti_rctf_copy(&rect, &but->rect); - if (but->imb) { + if (but->imb || but->type == COLOR) { /* use button size itself */ } else if (but->drawflag & UI_BUT_ICON_LEFT) { @@ -1242,10 +1244,42 @@ static bool ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data, } else #endif - { + if (but->type == COLOR) { + bool valid = false; + uiDragColorHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__); + + /* TODO support more button pointer types */ + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, drag_info->color); + drag_info->gamma_corrected = true; + valid = true; + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, drag_info->color); + drag_info->gamma_corrected = false; + valid = true; + } + else if (but->pointype == UI_BUT_POIN_FLOAT) { + copy_v3_v3(drag_info->color, (float *)but->poin); + valid = true; + } + else if (but->pointype == UI_BUT_POIN_CHAR) { + rgba_uchar_to_float(drag_info->color, (unsigned char *)but->poin); + valid = true; + } + + if (valid) { + WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, 0.0, WM_DRAG_FREE_DATA); + } + else { + MEM_freeN(drag_info); + return false; + } + } + else { wmDrag *drag; - drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but)); + drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but), WM_DRAG_NOP); if (but->imb) WM_event_drag_image(drag, but->imb, but->imb_scale, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect)); } @@ -4053,7 +4087,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co } } #ifdef USE_DRAG_TOGGLE - if (event->type == LEFTMOUSE && ui_is_but_drag_toggle(but)) { + if (event->type == LEFTMOUSE && event->val == KM_PRESS && (ui_is_but_drag_toggle(but))) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->x; data->dragstarty = event->y; @@ -4206,6 +4240,24 @@ static bool ui_numedit_but_NORMAL(uiBut *but, uiHandleButtonData *data, static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { + /* first handle click on icondrag type button */ + if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + if (ui_but_mouse_inside_icon(but, data->region, event)) { + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->x; + data->dragstarty = event->y; + return WM_UI_HANDLER_BREAK; + } + } +#ifdef USE_DRAG_TOGGLE + if (event->type == LEFTMOUSE && event->val == KM_PRESS) { + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->x; + data->dragstarty = event->y; + return WM_UI_HANDLER_BREAK; + } +#endif + /* regular open menu */ if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; @@ -4233,6 +4285,81 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co ui_apply_button(C, but->block, but, data, true); return WM_UI_HANDLER_BREAK; } + else if ((int)(but->a1) == UI_PALETTE_COLOR && + event->type == DELKEY && event->val == KM_PRESS) + { + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active(scene); + Palette *palette = BKE_paint_palette(paint); + PaletteColor *color = but->rnapoin.data; + + BKE_palette_color_remove(palette, color); + + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + } + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + + /* this function also ends state */ + if (ui_but_start_drag(C, but, data, event)) { + return WM_UI_HANDLER_BREAK; + } + + /* outside icon quit, not needed if drag activated */ + if (0 == ui_but_mouse_inside_icon(but, data->region, event)) { + button_activate_state(C, but, BUTTON_STATE_EXIT); + data->cancel = true; + return WM_UI_HANDLER_BREAK; + } + + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { + if ((int)(but->a1) == UI_PALETTE_COLOR) { + Palette *palette = but->rnapoin.id.data; + PaletteColor *color = but->rnapoin.data; + palette->active_color = BLI_findindex(&palette->colors, color); + + if( !event->ctrl) { + float color[3]; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active(scene); + Brush *brush = BKE_paint_brush(paint); + + if (brush->flag & BRUSH_USE_GRADIENT) { + float *target = &brush->gradient->data[brush->gradient->cur].r; + + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); + ui_block_to_scene_linear_v3(but->block, target); + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); + } + } + else { + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); + BKE_brush_color_set(scene, brush, color); + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); + ui_block_to_display_space_v3(but->block, color); + BKE_brush_color_set(scene, brush, color); + } + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); + } + else { + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + } + } + else { + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + } + return WM_UI_HANDLER_BREAK; + } + } return WM_UI_HANDLER_CONTINUE; @@ -6250,7 +6377,7 @@ static bool ui_but_contains_pt(uiBut *but, float mx, float my) return BLI_rctf_isect_pt(&but->rect, mx, my); } -static uiBut *ui_but_find_activated(ARegion *ar) +uiBut *ui_but_find_activated(ARegion *ar) { uiBlock *block; uiBut *but; @@ -6305,6 +6432,17 @@ bool UI_but_active_drop_name(bContext *C) return 0; } +bool UI_but_active_drop_color(bContext *C) +{ + ARegion *ar = CTX_wm_region(C); + uiBut *but = ui_but_find_activated(ar); + + if (but && but->type == COLOR) + return true; + + return false; +} + static void ui_blocks_set_tooltips(ARegion *ar, const bool enable) { uiBlock *block; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 546b2b85af5..51dd9166e46 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -508,6 +508,8 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_SOFTEN, soften); INIT_BRUSH_ICON(ICON_BRUSH_SUBTRACT, subtract); INIT_BRUSH_ICON(ICON_BRUSH_TEXDRAW, texdraw); + INIT_BRUSH_ICON(ICON_BRUSH_TEXFILL, texfill); + INIT_BRUSH_ICON(ICON_BRUSH_TEXMASK, texmask); INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb); INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index cd3b6390184..830bca393c4 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -186,6 +186,7 @@ struct uiBut { * (type == LABEL), Use (a1 == 1.0f) to use a2 as a blending factor (wow, this is imaginative!). * (type == SCROLL) Use as scroll size. * (type == SEARCH_MENU) Use as number or rows. + * (type == COLOR) Use as indication of color palette */ float a1; @@ -193,6 +194,7 @@ struct uiBut { * (type == NUM), Use to store RNA 'precision' value, for dragging and click-step. * (type == LABEL), If (a1 == 1.0f) use a2 as a blending factor. * (type == SEARCH_MENU) Use as number or columns. + * (type == COLOR) Use as indication of active palette color */ float a2; @@ -556,6 +558,8 @@ extern void ui_button_active_free(const struct bContext *C, uiBut *but); extern bool ui_button_is_active(struct ARegion *ar) ATTR_WARN_UNUSED_RESULT; extern int ui_button_open_menu_direction(uiBut *but); extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); +extern uiBut *ui_but_find_activated(struct ARegion *ar); + void ui_button_clipboard_free(void); void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa); uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 877a993e0ac..32b073ba269 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -35,6 +35,7 @@ #include "DNA_text_types.h" /* for UI_OT_reports_to_text */ #include "BLI_blenlib.h" +#include "BLI_math_color.h" #include "BLF_api.h" #include "BLF_translation.h" @@ -44,6 +45,7 @@ #include "BKE_global.h" #include "BKE_text.h" /* for UI_OT_reports_to_text */ #include "BKE_report.h" +#include "BKE_paint.h" #include "RNA_access.h" #include "RNA_define.h" @@ -55,6 +57,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_paint.h" + /* only for UI_OT_editsource */ #include "ED_screen.h" #include "BKE_main.h" @@ -810,6 +814,91 @@ static void UI_OT_reloadtranslation(wmOperatorType *ot) ot->exec = reloadtranslation_exec; } +int UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + /* should only return true for regions that include buttons, for now + * return true always */ + if (drag->type == WM_DRAG_COLOR) { + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *ar = CTX_wm_region(C); + + if (UI_but_active_drop_color(C)) + return 1; + + if (sima && (sima->mode == SI_MODE_PAINT) && + sima->image && (ar && ar->regiontype == RGN_TYPE_WINDOW)) + { + return 1; + } + } + + return 0; +} + +void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop) +{ + uiDragColorHandle *drag_info = (uiDragColorHandle *)drag->poin; + + RNA_float_set_array(drop->ptr, "color", drag_info->color); + RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected); +} + +static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + ARegion *ar = CTX_wm_region(C); + uiBut *but = NULL; + float color[3]; + bool gamma; + + RNA_float_get_array(op->ptr, "color", color); + gamma = RNA_boolean_get(op->ptr, "gamma"); + + /* find button under mouse, check if it has RNA color property and + * if it does copy the data */ + but = ui_but_find_activated(ar); + + if (but && but->type == COLOR && but->rnaprop) { + if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + if (!gamma) + ui_block_to_display_space_v3(but->block, color); + RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); + RNA_property_update(C, &but->rnapoin, but->rnaprop); + } + else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + if (gamma) + ui_block_to_scene_linear_v3(but->block, color); + RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); + RNA_property_update(C, &but->rnapoin, but->rnaprop); + } + } + else { + if (gamma) { + srgb_to_linearrgb_v3_v3(color, color); + } + + ED_imapaint_bucket_fill(C, color, op); + } + + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + + +static void UI_OT_drop_color(wmOperatorType *ot) +{ + ot->name = "Drop Color"; + ot->idname = "UI_OT_drop_color"; + ot->description = "Drop colors to buttons"; + + ot->invoke = drop_color_invoke; + + RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0); + RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected "); +} + + + /* ********************************************************* */ /* Registration */ @@ -821,7 +910,7 @@ void UI_buttons_operatortypes(void) WM_operatortype_append(UI_OT_unset_property_button); WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_reports_to_textblock); /* XXX: temp? */ - + WM_operatortype_append(UI_OT_drop_color); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 33412b4df78..b2b3493d4fc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -36,6 +36,7 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" +#include "DNA_brush_types.h" #include "BLI_utildefines.h" #include "BLI_string.h" @@ -60,6 +61,7 @@ #include "BKE_object.h" #include "BKE_packedFile.h" #include "BKE_particle.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_sca.h" #include "BKE_screen.h" @@ -349,6 +351,8 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_BR: return N_("Browse Brush to be linked"); case ID_PA: return N_("Browse Particle Settings to be linked"); case ID_GD: return N_("Browse Grease Pencil Data to be linked"); + case ID_PAL: return N_("Browse Palette Data to be linked"); + case ID_PC: return N_("Browse Paint Curve Data to be linked"); } } return N_("Browse ID data to be linked"); @@ -2363,6 +2367,61 @@ void uiTemplateColorPicker(uiLayout *layout, PointerRNA *ptr, const char *propna } } +void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, int UNUSED(colors)) +{ + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + PointerRNA cptr; + Palette *palette; + PaletteColor *color; + uiBlock *block; + uiLayout *col; + int row_cols = 0, col_id = 0; + int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); + + if (!prop) { + RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); + return; + } + + cptr = RNA_property_pointer_get(ptr, prop); + if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Palette)) + return; + + block = uiLayoutGetBlock(layout); + + palette = cptr.data; + + /* first delete any pending colors */ + BKE_palette_cleanup(palette); + + color = palette->colors.first; + + col = uiLayoutColumn(layout, true); + uiLayoutRow(col, true); + uiDefIconButO(block, BUT, "PALETTE_OT_color_add", WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); + uiDefIconButO(block, BUT, "PALETTE_OT_color_delete", WM_OP_INVOKE_DEFAULT, ICON_ZOOMOUT, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); + + col = uiLayoutColumn(layout, true); + uiLayoutRow(col, true); + + for (; color; color = color->next) { + PointerRNA ptr; + + if (row_cols >= cols_per_row) { + uiLayoutRow(col, true); + row_cols = 0; + } + + RNA_pointer_create(&palette->id, &RNA_PaletteColor, color, &ptr); + uiDefButR(block, COLOR, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, &ptr, "color", -1, 0.0, 1.0, + UI_PALETTE_COLOR, (col_id == palette->active_color) ? UI_PALETTE_COLOR_ACTIVE : 0.0, ""); + + row_cols++; + col_id++; + } +} + + /********************* Layer Buttons Template ************************/ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e8f8f1541b7..23f185befb9 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -33,6 +33,7 @@ #include <string.h> #include <assert.h> +#include "DNA_brush_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" @@ -2825,6 +2826,17 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat widgetbase_draw(&wtb, wcol); + if (but->a1 == UI_PALETTE_COLOR && but->a2 == UI_PALETTE_COLOR_ACTIVE) { + float width = rect->xmax - rect->xmin; + float height = rect->ymax - rect->ymin; + + glColor4ubv((unsigned char *)wcol->outline); + glBegin(GL_TRIANGLES); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.9f * height); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.5f * height); + glVertex2f(rect->xmin + 0.5f * width, rect->ymin + 0.9f * height); + glEnd(); + } } static void widget_normal(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 372ced0a6fd..65c01781d6d 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -35,21 +35,25 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" #include "DNA_curve_types.h" -#include "DNA_userdef_types.h" +#include "DNA_mesh_types.h" /* init_userdef_factory */ +#include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_mesh_types.h" /* init_userdef_factory */ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BKE_brush.h" #include "BKE_DerivedMesh.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_texture.h" +#include "BKE_library.h" #include "BIF_gl.h" @@ -537,6 +541,13 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->preview_stitch_active; break; + case TH_PAINT_CURVE_HANDLE: + cp = ts->paint_curve_handle; + break; + case TH_PAINT_CURVE_PIVOT: + cp = ts->paint_curve_pivot; + break; + case TH_UV_OTHERS: cp = ts->uv_others; break; @@ -871,6 +882,8 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tv3d.title, 0, 0, 0, 255); rgba_char_args_set(btheme->tv3d.freestyle_edge_mark, 0x7f, 0xff, 0x7f, 255); rgba_char_args_set(btheme->tv3d.freestyle_face_mark, 0x7f, 0xff, 0x7f, 51); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); btheme->tv3d.facedot_size = 4; @@ -2427,6 +2440,16 @@ void init_userdef_do_versions(void) } } + if (U.versionfile < 272|| (U.versionfile == 272 && U.subversionfile < 2)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tima.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tima.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); + } + } + { bTheme *btheme; for (btheme = U.themes.first; btheme; btheme = btheme->next) { @@ -2470,4 +2493,34 @@ void init_userdef_factory(void) me->flag &= ~ME_TWOSIDED; } } + + { + Brush *br; + br = BKE_brush_add(G.main, "Fill"); + br->imagepaint_tool = PAINT_TOOL_FILL; + br->ob_mode = OB_MODE_TEXTURE_PAINT; + + br = (Brush *)BKE_libblock_find_name(ID_BR, "Mask"); + if (br) { + br->imagepaint_tool = PAINT_TOOL_MASK; + br->ob_mode |= OB_MODE_TEXTURE_PAINT; + } + } + + { + Scene *scene; + + for (scene = G.main->scene.first; scene; scene = scene->id.next) { + if (scene->toolsettings) { + ToolSettings *ts = scene->toolsettings; + + if (ts->sculpt) { + Sculpt *sculpt = ts->sculpt; + sculpt->paint.symmetry_flags |= PAINT_SYMM_X; + sculpt->flags |= SCULPT_DYNTOPO_COLLAPSE; + sculpt->detail_size = 12; + } + } + } + } } diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 94d8d78de1a..f11af9b4d51 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -165,6 +165,7 @@ void ED_render_engine_changed(Main *bmain) bScreen *sc; ScrArea *sa; Scene *scene; + Material *ma; for (sc = bmain->screen.first; sc; sc = sc->id.next) for (sa = sc->areabase.first; sa; sa = sa->next) @@ -174,6 +175,11 @@ void ED_render_engine_changed(Main *bmain) for (scene = bmain->scene.first; scene; scene = scene->id.next) ED_render_id_flush_update(bmain, &scene->id); + + /* reset texture painting */ + for (ma = bmain->mat.first; ma; ma = ma->id.next) { + BKE_texpaint_slots_clear(ma); + } } /***************************** Updates *********************************** diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 3cdf2222934..7d2299063fa 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4154,7 +4154,8 @@ void ED_keymap_screen(wmKeyConfig *keyconf) /* dropbox for entire window */ lb = WM_dropboxmap_find("Window", 0, 0); WM_dropbox_add(lb, "WM_OT_open_mainfile", open_file_drop_poll, open_file_drop_copy); - + WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy); + keymap_modal_set(keyconf); } diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 79ce4f879b7..18db57c9f21 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -40,6 +40,7 @@ set(INC_SYS set(SRC paint_cursor.c + paint_curve.c paint_hide.c paint_image.c paint_image_2d.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index b5b0ddd6f70..7b9ede38b39 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -44,6 +44,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_curve.h" #include "BKE_image.h" #include "BKE_node.h" #include "BKE_paint.h" @@ -58,6 +59,8 @@ #include "ED_view3d.h" +#include "UI_resources.h" + #include "paint_intern.h" /* still needed for sculpt_stroke_get_location, should be * removed eventually (TODO) */ @@ -791,6 +794,138 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush, glPopAttrib(); } + +BLI_INLINE void draw_tri_point(float *co, float width, bool selected) +{ + float w = width / 2.0f; + if (selected) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + UI_ThemeColor4(TH_PAINT_CURVE_PIVOT); + + glLineWidth(3.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0], co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); + + glColor4f(1.0, 1.0, 1.0, 0.5); + glLineWidth(1.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0], co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); +} + +BLI_INLINE void draw_rect_point(float *co, float width, bool selected) +{ + float w = width / 2.0f; + if (selected) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + UI_ThemeColor4(TH_PAINT_CURVE_HANDLE); + glLineWidth(3.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0] + w, co[1] + w); + glVertex2f(co[0] - w, co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); + + glColor4f(1.0, 1.0, 1.0, 0.5); + glLineWidth(1.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0] + w, co[1] + w); + glVertex2f(co[0] - w, co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); +} + + +BLI_INLINE void draw_bezier_handle_lines(BezTriple *bez) +{ + short line1[] = {0, 1}; + short line2[] = {1, 2}; + + glVertexPointer(2, GL_FLOAT, 3 * sizeof(float), bez->vec); + glColor4f(0.0, 0.0, 0.0, 0.5); + glLineWidth(3.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); + + glLineWidth(1.0); + if (bez->f1 || bez->f2) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + glColor4f(1.0, 1.0, 1.0, 0.5); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, line1); + if (bez->f3 || bez->f2) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + glColor4f(1.0, 1.0, 1.0, 0.5); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, line2); +} + +static void paint_draw_curve_cursor(Brush *brush) +{ + if (brush->paint_curve && brush->paint_curve->points) { + int i; + PaintCurve *pc = brush->paint_curve; + PaintCurvePoint *cp = pc->points; + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glEnableClientState(GL_VERTEX_ARRAY); + + /* draw the bezier handles and the curve segment between the current and next point */ + for (i = 0; i < pc->tot_points - 1; i++, cp++) { + int j; + PaintCurvePoint *cp_next = cp + 1; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + /* use color coding to distinguish handles vs curve segments */ + draw_bezier_handle_lines(&cp->bez); + draw_tri_point(&cp->bez.vec[1][0], 10.0, cp->bez.f2); + draw_rect_point(&cp->bez.vec[0][0], 8.0, cp->bez.f1 || cp->bez.f2); + draw_rect_point(&cp->bez.vec[2][0], 8.0, cp->bez.f3 || cp->bez.f2); + + for (j = 0; j < 2; j++) + BKE_curve_forward_diff_bezier( + cp->bez.vec[1][j], + cp->bez.vec[2][j], + cp_next->bez.vec[0][j], + cp_next->bez.vec[1][j], + data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + + glVertexPointer(2, GL_FLOAT, 0, data); + glLineWidth(3.0); + glColor4f(0.0, 0.0, 0.0, 0.5); + glDrawArrays(GL_LINE_STRIP, 0, PAINT_CURVE_NUM_SEGMENTS + 1); + + glLineWidth(1.0); + glColor4f(0.9, 0.9, 1.0, 0.5); + glDrawArrays(GL_LINE_STRIP, 0, PAINT_CURVE_NUM_SEGMENTS + 1); + } + + /* draw last line segment */ + draw_bezier_handle_lines(&cp->bez); + draw_tri_point(&cp->bez.vec[1][0], 10.0, cp->bez.f2); + draw_rect_point(&cp->bez.vec[0][0], 8.0, cp->bez.f1 || cp->bez.f2); + draw_rect_point(&cp->bez.vec[2][0], 8.0, cp->bez.f3 || cp->bez.f2); + + glLineWidth(1.0); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glDisableClientState(GL_VERTEX_ARRAY); + } +} + /* Special actions taken when paint cursor goes over mesh */ /* TODO: sculpt only for now */ static void paint_cursor_on_hit(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, @@ -848,6 +983,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) zoomx = max_ff(zoomx, zoomy); mode = BKE_paintmode_get_active_from_context(C); + /* skip everything and draw brush here */ + if (brush->flag & BRUSH_CURVE) { + paint_draw_curve_cursor(brush); + return; + } + /* set various defaults */ translation[0] = x; translation[1] = y; @@ -857,8 +998,11 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* don't calculate rake angles while a stroke is active because the rake variables are global and * we may get interference with the stroke itself. For line strokes, such interference is visible */ - if (!ups->stroke_active && (brush->flag & BRUSH_RAKE)) - paint_calculate_rake_rotation(ups, translation); + if (!ups->stroke_active) { + if (brush->flag & BRUSH_RAKE) + /* here, translation contains the mouse coordinates. */ + paint_calculate_rake_rotation(ups, translation); + } /* draw overlay */ paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); @@ -878,7 +1022,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* check if brush is subtracting, use different color then */ /* TODO: no way currently to know state of pen flip or * invert key modifier without starting a stroke */ - if ((!(brush->flag & BRUSH_INVERTED) ^ + if ((!(ups->draw_inverted) ^ !(brush->flag & BRUSH_DIR_IN)) && ELEM(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, @@ -890,12 +1034,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* only do if brush is over the mesh */ if (hit) paint_cursor_on_hit(ups, brush, &vc, location); + } - if (ups->draw_anchored) { - final_radius = ups->anchored_size; - translation[0] = ups->anchored_initial_mouse[0]; - translation[1] = ups->anchored_initial_mouse[1]; - } + if (ups->draw_anchored) { + final_radius = ups->anchored_size; + translation[0] = ups->anchored_initial_mouse[0]; + translation[1] = ups->anchored_initial_mouse[1]; } /* make lines pretty */ diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c new file mode 100644 index 00000000000..6ca0a041388 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -0,0 +1,800 @@ +/* + * ***** 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/editors/sculpt_paint/paint_curve.c + * \ingroup edsculpt + */ + +#include <string.h> +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_paint.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "ED_paint.h" +#include "ED_view3d.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_view2d.h" + +#include "paint_intern.h" + +#define PAINT_CURVE_SELECT_THRESHOLD 40.0f +#define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT) + + +int paint_curve_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Paint *p; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + SpaceImage *sima; + + if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0))) + return false; + + sima = CTX_wm_space_image(C); + + if (sima && sima->mode != SI_MODE_PAINT) + return false; + + p = BKE_paint_get_active_from_context(C); + + if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + return true; + } + + return false; +} + +/* Paint Curve Undo*/ + +typedef struct UndoCurve { + struct UndoImageTile *next, *prev; + + PaintCurvePoint *points; /* points of curve */ + int tot_points; + int active_point; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ +} UndoCurve; + +static void paintcurve_undo_restore(bContext *C, ListBase *lb) +{ + Paint *p = BKE_paint_get_active_from_context(C); + UndoCurve *uc; + PaintCurve *pc; + + if (p->brush) { + pc = p->brush->paint_curve; + } + + if (!pc) + return; + + uc = (UndoCurve *)lb->first; + + if (strncmp(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname))) == 0) { + SWAP(PaintCurvePoint *, pc->points, uc->points); + SWAP(int, pc->tot_points, uc->tot_points); + SWAP(int, pc->add_index, uc->active_point); + } +} + +static void paintcurve_undo_delete(ListBase *lb) +{ + UndoCurve *uc; + uc = (UndoCurve *)lb->first; + + if (uc->points) + MEM_freeN(uc->points); + uc->points = NULL; +} + + +static void paintcurve_undo_begin(bContext *C, wmOperator *op, PaintCurve *pc) +{ + PaintMode mode = BKE_paintmode_get_active_from_context(C); + ListBase *lb = NULL; + int undo_stack_id; + UndoCurve *uc; + + switch (mode) { + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: + undo_stack_id = UNDO_PAINT_IMAGE; + break; + + case PAINT_SCULPT: + undo_stack_id = UNDO_PAINT_MESH; + break; + + default: + /* do nothing, undo is handled by global */ + return; + } + + + ED_undo_paint_push_begin(undo_stack_id, op->type->name, + paintcurve_undo_restore, paintcurve_undo_delete, NULL); + lb = undo_paint_push_get_list(undo_stack_id); + + uc = MEM_callocN(sizeof(*uc), "Undo_curve"); + + lb->first = uc; + + BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname)); + uc->tot_points = pc->tot_points; + uc->active_point = pc->add_index; + uc->points = MEM_dupallocN(pc->points); + + undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points); + + ED_undo_paint_push_end(undo_stack_id); +} +#define SEL_F1 (1 << 0) +#define SEL_F2 (1 << 1) +#define SEL_F3 (1 << 2) + +/* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */ +static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point) +{ + PaintCurvePoint *pcp, *closest = NULL; + int i; + float dist, closest_dist = FLT_MAX; + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F1; + } + } + if (!ignore_pivot) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F2; + } + } + } + dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F3; + } + } + } + + return closest; +} + +static int paintcurve_point_co_index(char sel) +{ + char i = 0; + while (sel != 1) { + sel >>= 1; + i++; + } + return i; +} + +/******************* Operators *********************************/ + +static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); + + if (p && p->brush) { + p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve"); + } + + return OPERATOR_FINISHED; +} + +void PAINTCURVE_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve"; + ot->description = "Add new paint curve"; + ot->idname = "PAINTCURVE_OT_new"; + + /* api callbacks */ + ot->exec = paintcurve_new_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + Main *bmain = CTX_data_main(C); + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + float vec[3] = {loc[0], loc[1], 0.0}; + int add_index; + int i; + + pc = br->paint_curve; + if (!pc) { + br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); + } + + paintcurve_undo_begin(C, op, pc); + + pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); + add_index = pc->add_index; + + if (pc->points) { + if (add_index > 0) + memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint)); + if (add_index < pc->tot_points) + memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint)); + + MEM_freeN(pc->points); + } + pc->points = pcp; + pc->tot_points++; + + /* initialize new point */ + memset(&pcp[add_index], 0, sizeof(PaintCurvePoint)); + copy_v3_v3(pcp[add_index].bez.vec[0], vec); + copy_v3_v3(pcp[add_index].bez.vec[1], vec); + copy_v3_v3(pcp[add_index].bez.vec[2], vec); + + /* last step, clear selection from all bezier handles expect the next */ + for (i = 0; i < pc->tot_points; i++) { + pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0; + } + pcp[add_index].bez.f3 = SELECT; + pcp[add_index].bez.h2 = HD_ALIGN; + + pc->add_index = add_index + 1; + + WM_paint_cursor_tag_redraw(window, ar); +} + + +static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int loc[2] = {event->mval[0], event->mval[1]}; + paintcurve_point_add(C, op, loc); + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; +} + +static int paintcurve_add_point_exec(bContext *C, wmOperator *op) +{ + int loc[2]; + + if (RNA_struct_property_is_set(op->ptr, "location")) { + RNA_int_get_array(op->ptr, "location", loc); + paintcurve_point_add(C, op, loc); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void PAINTCURVE_OT_add_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve Point"; + ot->description = "Add new paint curve point"; + ot->idname = "PAINTCURVE_OT_add_point"; + + /* api callbacks */ + ot->invoke = paintcurve_add_point_invoke; + ot->exec = paintcurve_add_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, + "Location", "Location of vertex in area space", 0, SHRT_MAX); +} + +static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + int i; + int tot_del = 0; + pc = br->paint_curve; + + if (!pc || pc->tot_points == 0) { + return OPERATOR_CANCELLED; + } + + paintcurve_undo_begin(C, op, pc); + +#define DELETE_TAG 2 + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if ((pcp->bez.f1 & SELECT) || (pcp->bez.f2 & SELECT) || (pcp->bez.f3 & SELECT)) { + pcp->bez.f2 |= DELETE_TAG; + tot_del++; + } + } + + if (tot_del > 0) { + int j = 0; + int new_tot = pc->tot_points - tot_del; + PaintCurvePoint *points_new = NULL; + if (new_tot > 0) + points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint"); + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if (!(pcp->bez.f2 & DELETE_TAG)) { + points_new[j] = pc->points[i]; + + if ((i + 1) == pc->add_index) { + pc->add_index = j + 1; + } + j++; + } + else if ((i + 1) == pc->add_index) { + /* prefer previous point */ + pc->add_index = j; + } + } + MEM_freeN(pc->points); + + pc->points = points_new; + pc->tot_points = new_tot; + } + +#undef DELETE_TAG + + WM_paint_cursor_tag_redraw(window, ar); + + return OPERATOR_FINISHED; +} + + +void PAINTCURVE_OT_delete_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve Point"; + ot->description = "Add new paint curve point"; + ot->idname = "PAINTCURVE_OT_delete_point"; + + /* api callbacks */ + ot->exec = paintcurve_delete_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; +} + + +static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend) +{ + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + PaintCurvePoint *pcp; + int i; + const float loc_fl[2] = {UNPACK2(loc)}; + + pc = br->paint_curve; + + if (!pc) + return false; + + paintcurve_undo_begin(C, op, pc); + + pcp = pc->points; + + if (toggle) { + char select = 0; + bool selected = false; + + for (i = 0; i < pc->tot_points; i++) { + if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) { + selected = true; + break; + } + } + + if (!selected) { + select = SELECT; + } + + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select; + } + } + else { + PaintCurvePoint *pcp; + char selflag; + + pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); + + if (pcp) { + pc->add_index = (pcp - pc->points) + 1; + + if (selflag == SEL_F2) { + if (extend) + pcp->bez.f2 ^= SELECT; + else + pcp->bez.f2 |= SELECT; + } + else if (selflag == SEL_F1) { + if (extend) + pcp->bez.f1 ^= SELECT; + else + pcp->bez.f1 |= SELECT; + } + else if (selflag == SEL_F3) { + if (extend) + pcp->bez.f3 ^= SELECT; + else + pcp->bez.f3 |= SELECT; + } + } + + /* clear selection for unselected points if not extending and if a point has been selected */ + if (!extend && pcp) { + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0; + + if ((pc->points + i) == pcp) { + char index = paintcurve_point_co_index(selflag); + PAINT_CURVE_POINT_SELECT(pcp, index); + } + } + } + + if (!pcp) + return false; + } + + WM_paint_cursor_tag_redraw(window, ar); + + return true; +} + + +static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int loc[2] = {UNPACK2(event->mval)}; + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + if (paintcurve_point_select(C, op, loc, toggle, extend)) { + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int paintcurve_select_point_exec(bContext *C, wmOperator *op) +{ + int loc[2]; + + if (RNA_struct_property_is_set(op->ptr, "location")) { + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + RNA_int_get_array(op->ptr, "location", loc); + if (paintcurve_point_select(C, op, loc, toggle, extend)) + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void PAINTCURVE_OT_select(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Paint Curve Point"; + ot->description = "Select a paint curve point"; + ot->idname = "PAINTCURVE_OT_select"; + + /* api callbacks */ + ot->invoke = paintcurve_select_point_invoke; + ot->exec = paintcurve_select_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, + "Location", "Location of vertex in area space", 0, SHRT_MAX); + prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "Select/Deselect all"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +typedef struct PointSlideData { + PaintCurvePoint *pcp; + char select; + int initial_loc[2]; + float point_initial_loc[3][2]; + int event; + bool align; +} PointSlideData; + +static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Paint *p = BKE_paint_get_active_from_context(C); + const float loc_fl[2] = {UNPACK2(event->mval)}; + char select; + int i; + bool do_select = RNA_boolean_get(op->ptr, "select"); + bool align = RNA_boolean_get(op->ptr, "align"); + Brush *br = p->brush; + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + + if (!pc) + return OPERATOR_PASS_THROUGH; + + if (do_select) { + pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select); + } + else { + /* just find first selected point */ + for (i = 0; i < pc->tot_points; i++) { + if (pc->points[i].bez.f1 || pc->points[i].bez.f2 || pc->points[i].bez.f3) { + pcp = &pc->points[i]; + select = SEL_F3; + break; + } + } + } + + + if (pcp) { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData"); + copy_v2_v2_int(psd->initial_loc, event->mval); + psd->event = event->type; + psd->pcp = pcp; + psd->select = paintcurve_point_co_index(select); + for (i = 0; i < 3; i++) { + copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]); + } + psd->align = align; + op->customdata = psd; + + if (do_select) + paintcurve_undo_begin(C, op, pc); + + /* first, clear all selection from points */ + for (i = 0; i < pc->tot_points; i++) + pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0; + + /* only select the active point */ + PAINT_CURVE_POINT_SELECT(pcp, psd->select); + pc->add_index = (pcp - pc->points) + 1; + + WM_event_add_modal_handler(C, op); + WM_paint_cursor_tag_redraw(window, ar); + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; +} + +static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + PointSlideData *psd = op->customdata; + + if (event->type == psd->event && event->val == KM_RELEASE) { + MEM_freeN(psd); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case MOUSEMOVE: + { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + float diff[2] = {event->mval[0] - psd->initial_loc[0], + event->mval[1] - psd->initial_loc[1]}; + if (psd->select == 1) { + int i; + for (i = 0; i < 3; i++) + add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]); + } + else { + add_v2_v2(diff, psd->point_initial_loc[psd->select]); + copy_v2_v2(psd->pcp->bez.vec[psd->select], diff); + + if (psd->align) { + char opposite = (psd->select == 0) ? 2 : 0; + sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]); + add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff); + } + } + WM_paint_cursor_tag_redraw(window, ar); + break; + } + default: + break; + } + + return OPERATOR_RUNNING_MODAL; +} + + +void PAINTCURVE_OT_slide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Slide Paint Curve Point"; + ot->description = "Select and slide paint curve point"; + ot->idname = "PAINTCURVE_OT_slide"; + + /* api callbacks */ + ot->invoke = paintcurve_slide_invoke; + ot->modal = paintcurve_slide_modal; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform"); + RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform"); +} + +static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PaintMode mode = BKE_paintmode_get_active_from_context(C); + const char *name; + + switch (mode) { + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: + name = "PAINT_OT_image_paint"; + break; + case PAINT_WEIGHT: + name = "PAINT_OT_weight_paint"; + break; + case PAINT_VERTEX: + name = "PAINT_OT_vertex_paint"; + break; + case PAINT_SCULPT: + name = "SCULPT_OT_brush_stroke"; + break; + default: + return OPERATOR_PASS_THROUGH; + } + + return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL); +} + +void PAINTCURVE_OT_draw(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Draw Curve"; + ot->description = "Draw curve"; + ot->idname = "PAINTCURVE_OT_draw"; + + /* api callbacks */ + ot->exec = paintcurve_draw_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; +} + +static int paintcurve_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + op->customdata = SET_INT_IN_POINTER(event->type); + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int paintcurve_cursor_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (event->type == GET_INT_FROM_POINTER(op->customdata) && event->val == KM_RELEASE) + return OPERATOR_FINISHED; + + if (event->type == MOUSEMOVE) { + PaintMode mode = BKE_paintmode_get_active_from_context(C); + + switch (mode) { + case PAINT_TEXTURE_2D: + { + ARegion *ar = CTX_wm_region(C); + SpaceImage *sima = CTX_wm_space_image(C); + float location[2]; + + if (!sima) + return OPERATOR_CANCELLED; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); + copy_v2_v2(sima->cursor, location); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); + break; + } + default: + ED_view3d_cursor3d_update(C, event->mval); + break; + } + } + + return OPERATOR_RUNNING_MODAL; +} + +void PAINTCURVE_OT_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Cursor"; + ot->description = "Place cursor"; + ot->idname = "PAINTCURVE_OT_cursor"; + + /* api callbacks */ + ot->invoke = paintcurve_cursor_invoke; + ot->modal = paintcurve_cursor_modal; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = 0; +} diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 34232c51ff7..88b9f5bb296 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -43,6 +43,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_threads.h" #include "PIL_time.h" @@ -59,15 +60,21 @@ #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" #include "BKE_paint.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" +#include "BKE_colortools.h" #include "BKE_editmesh.h" #include "UI_view2d.h" #include "ED_image.h" +#include "ED_mesh.h" #include "ED_object.h" #include "ED_paint.h" #include "ED_screen.h" @@ -82,6 +89,9 @@ #include "GPU_draw.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" + #include "IMB_colormanagement.h" #include "paint_intern.h" @@ -102,14 +112,27 @@ typedef struct UndoImageTile { int x, y; + Image *ima; short source, use_float; char gen_type; + bool valid; } UndoImageTile; /* this is a static resource for non-globality, * Maybe it should be exposed as part of the * paint operation, but for now just give a public interface */ static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; +static SpinLock undolock; + +void image_undo_init_locks(void) +{ + BLI_spin_init(&undolock); +} + +void image_undo_end_locks(void) +{ + BLI_spin_end(&undolock); +} ImagePaintPartialRedraw *get_imapaintpartial(void) { @@ -122,26 +145,53 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) } /* UNDO */ +typedef enum { + COPY = 0, + RESTORE = 1, + RESTORE_COPY = 2 +} CopyMode; -static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) +static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + if (mode == COPY) { + /* copy or swap contents of tile->rect and region in ibuf->rect */ + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } } else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - if (restore) + if (mode == RESTORE_COPY) + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + /* swap to the tmpbuf for easy copying */ + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE, tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + + if (mode == RESTORE) { + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + } + } } -void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask) +void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -160,6 +210,8 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi *mask = tile->mask; } + if (validate) + tile->valid = true; return tile->rect.pt; } @@ -170,7 +222,7 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi return NULL; } -void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -179,10 +231,14 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, void *data; /* check if tile is already pushed */ - data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, NULL); - if (data) - return data; - + + /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ + if (!proj) { + data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); + if (data) + return data; + } + if (*tmpibuf == NULL) *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); @@ -191,6 +247,11 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->x = x_tile; tile->y = y_tile; + /* add mask explicitly here */ + if (mask) + *mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, + "UndoImageTile.mask"); + allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); @@ -200,12 +261,23 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->gen_type = ima->gen_type; tile->source = ima->source; tile->use_float = use_float; + tile->valid = true; + tile->ima = ima; - undo_copy_tile(tile, *tmpibuf, ibuf, 0); - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); + if (valid) + *valid = &tile->valid; + + undo_copy_tile(tile, *tmpibuf, ibuf, COPY); + if (proj) + BLI_spin_lock(&undolock); + + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); BLI_addtail(lb, tile); - + + if (proj) + BLI_spin_unlock(&undolock); + return tile->rect.pt; } @@ -222,6 +294,33 @@ void image_undo_remove_masks(void) } } +static void image_undo_restore_runtime(ListBase *lb) +{ + ImBuf *ibuf, *tmpibuf; + UndoImageTile *tile; + + tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, + IB_rectfloat | IB_rect); + + for (tile = lb->first; tile; tile = tile->next) { + Image *ima = tile->ima; + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); + + GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ + if (ibuf->rect_float) + ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + if (ibuf->mipmap[0]) + ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + IMB_freeImBuf(tmpibuf); +} + void ED_image_undo_restore(bContext *C, ListBase *lb) { Main *bmain = CTX_data_main(C); @@ -273,7 +372,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) continue; } - undo_copy_tile(tile, tmpibuf, ibuf, 1); + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); GPU_free_image(ima); /* force OpenGL reload */ if (ibuf->rect_float) @@ -282,6 +381,8 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + DAG_id_tag_update(&ima->id, 0); + BKE_image_release_ibuf(ima, ibuf, NULL); } @@ -296,6 +397,42 @@ void ED_image_undo_free(ListBase *lb) MEM_freeN(tile->rect.pt); } +static void image_undo_end(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; + int deallocsize = 0; + int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; + + /* first dispose of invalid tiles (may happen due to drag dot for instance) */ + for (tile = lb->first; tile;) { + if (!tile->valid) { + UndoImageTile *tmp_tile = tile->next; + deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); + MEM_freeN(tile->rect.pt); + BLI_freelinkN (lb, tile); + tile = tmp_tile; + } + else { + tile = tile->next; + } + } + + /* don't forget to remove the size of deallocated tiles */ + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +} + +static void image_undo_invalidate(void) +{ + UndoImageTile *tile; + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + + for (tile = lb->first; tile; tile = tile->next) + tile->valid = false; +} + /* Imagepaint Partial Redraw & Dirty Region */ void ED_imapaint_clear_partial_redraw(void) @@ -344,7 +481,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int for (ty = tiley; ty <= tileh; ty++) for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty); + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false); ibuf->userflags |= IB_BITMAPDIRTY; @@ -373,6 +510,70 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te } } +/* paint blur kernels */ +BlurKernel *paint_new_blur_kernel(Brush *br) +{ + int i, j; + BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + int pixel_len = br->blur_kernel_radius; + BlurKernelType type = br->blur_mode; + + kernel->side = pixel_len * 2 + 1; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = pixel_len; + + switch (type) { + case KERNEL_BOX: + for (i = 0; i < kernel->side_squared; i++) + kernel->wdata[i] = 1.0; + break; + + case KERNEL_GAUSSIAN: + { + float standard_dev = pixel_len / 3.0; /* at standard deviation of 3.0 kernel is at about zero */ + int i_term = pixel_len + 1; + + /* make the necessary adjustment to the value for use in the normal distribution formula */ + standard_dev = standard_dev * standard_dev * 2; + + kernel->wdata[pixel_len + pixel_len * kernel->side] = 1.0; + /* fill in all four quadrants at once */ + for (i = 0; i < i_term; i++) { + for (j = 0; j < pixel_len; j++) { + float idist = pixel_len - i; + float jdist = pixel_len - j; + + float value = exp((idist * idist + jdist * jdist) / standard_dev); + + kernel->wdata[i + j * kernel->side] = + kernel->wdata[(kernel->side - j - 1) + i * kernel->side] = + kernel->wdata[(kernel->side - i - 1) + (kernel->side - j - 1) * kernel->side] = + kernel->wdata[j + (kernel->side - i - 1) * kernel->side] = + value; + } + } + + break; + } + + default: + printf("unidentified kernel type, aborting\n"); + MEM_freeN(kernel->wdata); + MEM_freeN(kernel); + return NULL; + break; + } + + return kernel; +} + +void paint_delete_blur_kernel(BlurKernel *kernel) +{ + if (kernel->wdata) + MEM_freeN(kernel->wdata); +} + /************************ image paint poll ************************/ static Brush *image_paint_brush(bContext *C) @@ -432,11 +633,57 @@ typedef struct PaintOperation { void *custom_paint; float prevmouse[2]; + float startmouse[2]; double starttime; + void *cursor; ViewContext vc; } PaintOperation; +bool paint_use_opacity_masking(Brush *brush) +{ + return (brush->flag & BRUSH_AIRBRUSH) || + (brush->flag & BRUSH_DRAG_DOT) || + (brush->flag & BRUSH_ANCHORED) || + (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || + (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) || + (brush->imagepaint_tool == PAINT_TOOL_FILL) || + (brush->flag & BRUSH_USE_GRADIENT) || + (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D)) ? + false : true; +} + +void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, + float pressure, float color[3], struct ColorManagedDisplay *display) +{ + if (invert) + copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br)); + else { + if (br->flag & BRUSH_USE_GRADIENT) { + switch (br->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + do_colorband(br->gradient, pressure, color); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: + { + float coord = fmod(distance / br->gradient_spacing, 1.0); + do_colorband(br->gradient, coord, color); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: + { + do_colorband(br->gradient, distance / br->gradient_spacing, color); + break; + } + } + } + else + copy_v3_v3(color, BKE_brush_color_get(scene, br)); + } + if (color_correction) + IMB_colormanagement_display_to_scene_linear_v3(color, display); +} + void paint_brush_init_tex(Brush *brush) { /* init mtex nodes */ @@ -462,26 +709,54 @@ void paint_brush_exit_tex(Brush *brush) } } +static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +{ + PaintOperation *pop = (PaintOperation *)customdata; + + if (pop) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glLineWidth(4.0); + glColor4ub(0, 0, 0, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(2.0); + glColor4ub(255, 255, 255, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(1.0); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } +} + -static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mouse[2]) +static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); int mode = RNA_enum_get(op->ptr, "mode"); view3d_set_viewcontext(C, &pop->vc); - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); + + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, gradient_draw_line, pop); + } /* initialize from context */ if (CTX_wm_region_view3d(C)) { + Object *ob = OBACT; + paint_proj_mesh_data_ensure(C, ob, op); pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, OBACT, pop->prevmouse, mode); + pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); } else { pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op); + pop->custom_paint = paint_2d_new_stroke(C, op, mode); } if (!pop->custom_paint) { @@ -491,52 +766,69 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mou settings->imapaint.flag |= IMAGEPAINT_DRAWING; ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); - - { - UnifiedPaintSettings *ups = &settings->unified_paint_settings; - ups->stroke_active = true; - } + ED_image_undo_restore, ED_image_undo_free, NULL); return pop; } +/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ +static void paint_stroke_restore(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + image_undo_restore_runtime(lb); + image_undo_invalidate(); +} + static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { PaintOperation *pop = paint_stroke_mode_data(stroke); Scene *scene = CTX_data_scene(C); - Brush *brush = BKE_paint_brush(&scene->toolsettings->imapaint.paint); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startsize = (float)BKE_brush_size_get(scene, brush); float startalpha = BKE_brush_alpha_get(scene, brush); float mouse[2]; float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); int eraser; RNA_float_get_array(itemptr, "mouse", mouse); pressure = RNA_float_get(itemptr, "pressure"); eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = max_ff(1.0f, RNA_float_get(itemptr, "size")); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } if (BKE_brush_use_alpha_pressure(scene, brush)) - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure)); - if (BKE_brush_use_size_pressure(scene, brush)) - BKE_brush_size_set(scene, brush, (int)max_ff(1.0f, startsize * pressure)); + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + else + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + paint_stroke_restore(); + } if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse); + paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse, pressure, distance, size); } else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser); + paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); } - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); /* restore brush values */ BKE_brush_alpha_set(scene, brush, startalpha); - BKE_brush_size_set(scene, brush, startsize); } static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) @@ -554,11 +846,39 @@ static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, b static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) { Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; + ToolSettings *toolsettings = scene->toolsettings; PaintOperation *pop = paint_stroke_mode_data(stroke); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + if (pop->mode == PAINT_MODE_2D) { + paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + else { + if (pop->mode == PAINT_MODE_2D) { + float color[3]; + + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + } if (pop->mode == PAINT_MODE_3D_PROJECT) { paint_proj_stroke_done(pop->custom_paint); } @@ -566,7 +886,11 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) paint_2d_stroke_done(pop->custom_paint); } - ED_undo_paint_push_end(UNDO_PAINT_IMAGE); + if (pop->cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor); + } + + image_undo_end(); /* duplicate warning, see texpaint_init */ #if 0 @@ -576,43 +900,41 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile); #endif MEM_freeN(pop); - - { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->stroke_active = false; - } -} - -static bool paint_stroke_test_start(bContext *UNUSED(C), wmOperator *UNUSED(op), const float UNUSED(mouse[2])) -{ - return true; } - -static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { PaintOperation *pop; - float mouse[2]; - int retval; /* TODO Should avoid putting this here. Instead, last position should be requested * from stroke system. */ - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; if (!(pop = texture_paint_init(C, op, mouse))) { - return OPERATOR_CANCELLED; + return false; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + paint_stroke_set_mode_data(op->customdata, pop); + + return true; +} + + +static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, event->type); - paint_stroke_set_mode_data(op->customdata, pop); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -637,12 +959,10 @@ static int paint_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, 0); - paint_stroke_set_mode_data(op->customdata, pop); - /* frees op->customdata */ paint_stroke_exec(C, op); @@ -686,9 +1006,9 @@ int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) if (!rv3d) { SpaceImage *sima = CTX_wm_space_image(C); - ARegion *ar = CTX_wm_region(C); if (sima->mode == SI_MODE_PAINT) { + ARegion *ar = CTX_wm_region(C); ED_space_image_get_zoom(sima, ar, zoomx, zoomy); return 1; @@ -847,17 +1167,39 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) typedef struct { bool show_cursor; short event_type; -} SampleColorData; + float initcolor[3]; + bool sample_palette; +} SampleColorData; + + +static void sample_color_update_header(SampleColorData *data, bContext *C) +{ +#define HEADER_LENGTH 150 + char msg[HEADER_LENGTH]; + ScrArea *sa = CTX_wm_area(C); + + if (sa) { + BLI_snprintf(msg, HEADER_LENGTH, + "Sample color for %s", + !data->sample_palette ? + "Brush. Use Left Click to sample for palette instead" : + "Palette. Use Left Click to sample more colors"); + ED_area_headerprint(sa, msg); + } + +#undef HEADER_LENGTH +} static int sample_color_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); ARegion *ar = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); bool show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); int location[2]; - + bool use_palette; paint->flags &= ~PAINT_SHOW_BRUSH; /* force redraw without cursor */ @@ -865,7 +1207,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) WM_redraw_windows(C); RNA_int_get_array(op->ptr, "location", location); - paint_sample_color(C, ar, location[0], location[1]); + use_palette = RNA_boolean_get(op->ptr, "palette"); + + paint_sample_color(C, ar, location[0], location[1], mode == PAINT_TEXTURE_PROJECTIVE, use_palette); if (show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; @@ -878,7 +1222,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); + PaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); ARegion *ar = CTX_wm_region(C); @@ -886,18 +1232,24 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event data->event_type = event->type; data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); + copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); + data->sample_palette = false; op->customdata = data; paint->flags &= ~PAINT_SHOW_BRUSH; + sample_color_update_header(data, C); + + WM_event_add_modal_handler(C, op); + /* force redraw without cursor */ WM_paint_cursor_tag_redraw(win, ar); WM_redraw_windows(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); - WM_event_add_modal_handler(C, op); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); return OPERATOR_RUNNING_MODAL; @@ -905,17 +1257,27 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); SampleColorData *data = op->customdata; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { + ScrArea *sa = CTX_wm_area(C); + if (data->show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; } + if (data->sample_palette) { + BKE_brush_color_set(scene, brush, data->initcolor); + RNA_boolean_set(op->ptr, "palette", true); + } WM_cursor_modal_restore(CTX_wm_window(C)); MEM_freeN(data); + ED_area_headerprint(sa, NULL); + return OPERATOR_FINISHED; } @@ -924,10 +1286,22 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); break; } + + case LEFTMOUSE: + if (event->val == KM_PRESS) { + ARegion *ar = CTX_wm_region(C); + RNA_int_set_array(op->ptr, "location", event->mval); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, true); + if (!data->sample_palette) { + data->sample_palette = true; + sample_color_update_header(data, C); + } + } + break; } return OPERATOR_RUNNING_MODAL; @@ -956,6 +1330,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot) /* properties */ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "Cursor location in region coordinates", 0, 16384); + RNA_def_boolean(ot->srna, "palette", 0, "Palette", "Add color to palette"); } /******************** texture paint toggle operator ********************/ @@ -973,13 +1348,84 @@ static int texture_paint_toggle_poll(bContext *C) return 1; } + +/* Make sure that active object has a material, and assign UVs and image layers if they do not exist */ +void paint_proj_mesh_data_ensure(bContext *C, Object *ob, wmOperator *op) +{ + Mesh *me; + int layernum; + bool add_material = false; + ImagePaintSettings *imapaint = &(CTX_data_tool_settings(C)->imapaint); + Brush *br = BKE_paint_brush(&imapaint->paint); + + /* no material, add one */ + if (ob->totcol == 0) { + add_material = true; + } + else { + /* there may be material slots but they may be empty, check */ + bool has_material = false; + int i; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + if (ma) { + has_material = true; + if (!ma->texpaintslot) { + proj_paint_add_slot(C, MAP_COL, ma); + } + } + } + + if (!has_material) + add_material = true; + } + + if (add_material) { + Material *ma = BKE_material_add(CTX_data_main(C), "Material"); + /* no material found, just assign to first slot */ + assign_material(ob, ma, 1, BKE_MAT_ASSIGN_USERPREF); + proj_paint_add_slot(C, MAP_COL, ma); + } + + me = BKE_mesh_from_object(ob); + layernum = CustomData_number_of_layers(&me->pdata, CD_MTEXPOLY); + + if (layernum == 0) { + BKE_reportf(op->reports, RPT_WARNING, "Object did not have UV map. Recommend manual unwrap"); + + ED_mesh_uv_texture_add(me, "UVMap", true); + } + + /* Make sure we have a stencil to paint on! */ + if (br->imagepaint_tool == PAINT_TOOL_MASK) { + imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; + + if (imapaint->stencil == NULL) { + int width; + int height; + Main *bmain = CTX_data_main(C); + float color[4] = {0.0, 0.0, 0.0, 1.0}; + + /* should not be allowed, but just in case */ + if (imapaint->slot_xresolution_default == 0) + imapaint->slot_xresolution_default = 1024; + if (imapaint->slot_yresolution_default == 0) + imapaint->slot_yresolution_default = 1024; + + width = imapaint->slot_xresolution_default; + height = imapaint->slot_yresolution_default; + imapaint->stencil = BKE_image_add_generated(bmain, width, height, "Stencil", 32, false, IMA_GENTYPE_BLANK, color); + } + } +} + static int texture_paint_toggle_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); const int mode_flag = OB_MODE_TEXTURE_PAINT; const bool is_mode_set = (ob->mode & mode_flag) != 0; - Mesh *me; if (!is_mode_set) { if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { @@ -987,8 +1433,6 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) } } - me = BKE_mesh_from_object(ob); - if (ob->mode & mode_flag) { ob->mode &= ~mode_flag; @@ -999,11 +1443,36 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) toggle_paint_cursor(C, 0); } else { - ob->mode |= mode_flag; + bScreen *sc; + Main *bmain = CTX_data_main(C); + Material *ma; + + bool use_nodes = BKE_scene_use_new_shading_nodes(scene); + /* This has to stay here to regenerate the texture paint + * cache in case we are loading a file */ + BKE_texpaint_slots_refresh_object(ob, use_nodes); + + paint_proj_mesh_data_ensure(C, ob, op); + + /* set the current material active paint slot on image editor */ + ma = give_current_material(ob, ob->actcol); + + if (ma->tot_slots > 0) { + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + ED_space_image_set(sima, scene, scene->obedit, ma->texpaintslot[ma->paint_active_slot].ima); + } + } + } + } + } - if (me->mtface == NULL) - me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, - NULL, me->totface); + ob->mode |= mode_flag; BKE_paint_init(&scene->toolsettings->imapaint.paint, PAINT_CURSOR_TEXTURE_PAINT); @@ -1035,6 +1504,60 @@ void PAINT_OT_texture_paint_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *br = image_paint_brush(C); + if (ups->flag & UNIFIED_PAINT_COLOR) { + swap_v3_v3(ups->rgb, ups->secondary_rgb); + } + else if (br) { + swap_v3_v3(br->rgb, br->secondary_rgb); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); + + return OPERATOR_FINISHED; +} + +static int brush_colors_flip_poll(bContext *C) +{ + if (image_paint_poll(C)) { + Brush *br = image_paint_brush(C); + if (br->imagepaint_tool == PAINT_TOOL_DRAW) + return 1; + } + + return 0; +} + +void PAINT_OT_brush_colors_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Brush Colors Flip"; + ot->idname = "PAINT_OT_brush_colors_flip"; + ot->description = "Toggle foreground and background brush colors"; + + /* api callbacks */ + ot->exec = brush_colors_flip_exec; + ot->poll = brush_colors_flip_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) +{ + ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, + ED_image_undo_restore, ED_image_undo_free, NULL); + + paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +} + + static int texture_paint_poll(bContext *C) { if (texture_paint_toggle_poll(C)) diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 85889fe4cb9..d394d6d3f63 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -40,11 +40,18 @@ #include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_math_color_blend.h" +#include "BLI_stack.h" +#include "BLI_bitmap.h" + #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_texture.h" #include "ED_paint.h" #include "ED_screen.h" @@ -69,24 +76,25 @@ /* Defines and Structs */ typedef struct BrushPainterCache { - int size; /* size override, if 0 uses 2*BKE_brush_size_get(brush) */ - bool use_float; /* need float imbuf? */ bool use_color_correction; /* use color correction for float */ - bool use_masking; /* use masking? */ + bool invert; bool is_texbrush; bool is_maskbrush; - int lastsize; - float lastalpha; - float lastjitter; + int lastdiameter; float last_tex_rotation; float last_mask_rotation; + float last_pressure; ImBuf *ibuf; ImBuf *texibuf; - unsigned short *mask; + unsigned short *curve_mask; + unsigned short *tex_mask; + unsigned short *tex_mask_old; + unsigned int tex_mask_old_w; + unsigned int tex_mask_old_h; } BrushPainterCache; typedef struct BrushPainter { @@ -136,43 +144,42 @@ typedef struct ImagePaintState { int do_facesel; bool need_redraw; + + BlurKernel *blurkernel; } ImagePaintState; -static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush) +static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool invert) { BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); painter->brush = brush; painter->scene = scene; painter->firsttouch = 1; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + painter->cache.invert = invert; return painter; } -static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction, bool use_masking) +static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction) { Brush *brush = painter->brush; if ((painter->cache.use_float != use_float)) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.mask) MEM_freeN(painter->cache.mask); + if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); painter->cache.ibuf = NULL; - painter->cache.mask = NULL; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ - } - - if (painter->cache.use_float != use_float) { - if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - painter->cache.texibuf = NULL; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ + painter->cache.curve_mask = NULL; + painter->cache.tex_mask = NULL; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ } painter->cache.use_float = use_float; painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.use_masking = use_masking; painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; } @@ -181,7 +188,9 @@ static void brush_painter_2d_free(BrushPainter *painter) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.mask) MEM_freeN(painter->cache.mask); + if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); MEM_freeN(painter); } @@ -192,41 +201,177 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) texco[2] = 0.0f; } -/* create a mask with the falloff strength and optionally brush alpha */ -static unsigned short *brush_painter_mask_new(BrushPainter *painter, int size) +/* create a mask with the mask texture */ +static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) { Scene *scene = painter->scene; Brush *brush = painter->brush; - bool use_masking = painter->cache.use_masking; - - float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush); - int radius = BKE_brush_size_get(scene, brush); - int xoff = -size * 0.5f + 0.5f; - int yoff = -size * 0.5f + 0.5f; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + float texco[3]; unsigned short *mask, *m; - int x, y; + int x, y, thread = 0; - mask = MEM_callocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); + mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); m = mask; for (y = 0; y < size; y++) { for (x = 0; x < size; x++, m++) { + float res; + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); + *m = (unsigned short)(65535.0f * res); + } + } + + return mask; +} + +/* update rectangular section of the brush image */ +static void brush_painter_mask_imbuf_update( + BrushPainter *painter, unsigned short *tex_mask_old, + int origx, int origy, int w, int h, int xt, int yt, int diameter) +{ + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf tex_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + unsigned short res; + + bool use_texture_old = (tex_mask_old != NULL); + + int x, y, thread = 0; + + unsigned short *tex_mask = painter->cache.tex_mask; + unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture */ + float texco[3]; + + /* handle byte pixel */ + unsigned short *b = tex_mask + (y * diameter + x); + unsigned short *t = tex_mask_cur + (y * diameter + x); + + if (!use_texture_old) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + res = (unsigned short)(65535.0f * BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); + } + + /* read from old texture buffer */ + if (use_texture_old) { + res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + } + + /* write to new texture mask */ + *t = res; + /* write to mask image buffer */ + *b = res; + } + } +} + + +/** + * Update the brush mask image by trying to reuse the cached texture result. + * This can be considerably faster for brushes that change size due to pressure or + * textures that stick to the surface where only part of the pixels are new + */ +static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +{ + BrushPainterCache *cache = &painter->cache; + unsigned short *tex_mask_old; + int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + if (!cache->tex_mask) + cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + + /* create new texture image buffer with coordinates relative to old */ + tex_mask_old = cache->tex_mask_old; + cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + + if (tex_mask_old) { + ImBuf maskibuf; + ImBuf maskibuf_old; + maskibuf.x = maskibuf.y = diameter; + maskibuf_old.x = cache->tex_mask_old_w; + maskibuf_old.y = cache->tex_mask_old_h; + + srcx = srcy = 0; + w = cache->tex_mask_old_w; + h = cache->tex_mask_old_h; + destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); + desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); + + /* hack, use temporary rects so that clipping works */ + IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, diameter); + y1 = min_ii(desty, diameter); + x2 = min_ii(destx + w, diameter); + y2 = min_ii(desty + h, diameter); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + + if (tex_mask_old) + MEM_freeN(tex_mask_old); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); + if ((x2 < diameter) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + if ((x1 < x2) && (0 < y1)) + brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); + if ((x1 < x2) && (y2 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + + /* through with sampling, now update sizes */ + cache->tex_mask_old_w = diameter; + cache->tex_mask_old_h = diameter; +} + +/* create a mask with the falloff strength */ +static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius) +{ + Brush *brush = painter->brush; + + int xoff = -diameter * 0.5f + 0.5f; + int yoff = -diameter * 0.5f + 0.5f; + + unsigned short *mask, *m; + int x, y; + + mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + m = mask; + + for (y = 0; y < diameter; y++) { + for (x = 0; x < diameter; x++, m++) { float xy[2] = {x + xoff, y + yoff}; float len = len_v2(xy); - float strength = alpha; - - strength *= BKE_brush_curve_strength_clamp(brush, len, radius); - *m = (unsigned short)(65535.0f * strength); + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamp(brush, len, radius)); } } return mask; } + /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance) { Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -235,19 +380,11 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; - rctf mask_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; - bool use_masking = painter->cache.use_masking; bool use_color_correction = painter->cache.use_color_correction; bool use_float = painter->cache.use_float; bool is_texbrush = painter->cache.is_texbrush; - bool is_maskbrush = painter->cache.is_maskbrush; - - float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush); - int radius = BKE_brush_size_get(scene, brush); - int xoff = -size * 0.5f + 0.5f; - int yoff = -size * 0.5f + 0.5f; int x, y, thread = 0; float brush_rgb[3]; @@ -257,11 +394,7 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - copy_v3_v3(brush_rgb, brush->rgb); - - if (use_color_correction) { - IMB_colormanagement_display_to_scene_linear_v3(brush_rgb, display); - } + paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -289,19 +422,6 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) rgba[3] = 1.0f; } - if (is_maskbrush) { - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - } - - /* when not using masking, multiply in falloff and strength */ - if (!use_masking) { - float xy[2] = {x + xoff, y + yoff}; - float len = len_v2(xy); - - rgba[3] *= alpha * BKE_brush_curve_strength_clamp(brush, len, radius); - } - if (use_float) { /* write to float pixel */ float *dstf = ibuf->rect_float + (y * size + x) * 4; @@ -332,14 +452,11 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; - rctf mask_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; - bool use_masking = painter->cache.use_masking; bool use_color_correction = painter->cache.use_color_correction; bool use_float = painter->cache.use_float; bool is_texbrush = painter->cache.is_texbrush; - bool is_maskbrush = painter->cache.is_maskbrush; bool use_texture_old = (oldtexibuf != NULL); int x, y, thread = 0; @@ -347,15 +464,10 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, ImBuf *ibuf = painter->cache.ibuf; ImBuf *texibuf = painter->cache.texibuf; - unsigned short *mask = painter->cache.mask; /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - copy_v3_v3(brush_rgb, brush->rgb); - - if (use_color_correction) { - IMB_colormanagement_display_to_scene_linear_v3(brush_rgb, display); - } + paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -363,7 +475,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, brush_rgb[2] = 1.0f; } - /* fill pixes */ + /* fill pixels */ for (y = origy; y < h; y++) { for (x = origx; x < w; x++) { /* sample texture and multiply with brush color */ @@ -383,11 +495,6 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, copy_v3_v3(rgba, brush_rgb); rgba[3] = 1.0f; } - - if (is_maskbrush) { - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - } } if (use_float) { @@ -404,12 +511,6 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, /* write to new texture buffer */ copy_v4_v4(tf, rgba); - /* if not using masking, multiply in the mask now */ - if (!use_masking) { - unsigned short *m = mask + (y * ibuf->x + x); - rgba[3] *= *m * (1.0f / 65535.0f); - } - /* output premultiplied float image, mf was already premultiplied */ mul_v3_v3fl(bf, rgba, rgba[3]); bf[3] = rgba[3]; @@ -438,12 +539,6 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, t[2] = crgba[2]; t[3] = crgba[3]; - /* if not using masking, multiply in the mask now */ - if (!use_masking) { - unsigned short *m = mask + (y * ibuf->x + x); - crgba[3] = (crgba[3] * (*m)) / 65535; - } - /* write to brush image buffer */ b[0] = crgba[0]; b[1] = crgba[1]; @@ -457,14 +552,11 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, /* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2]) +static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) { - const Scene *scene = painter->scene; - Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - int diameter = 2 * BKE_brush_size_get(scene, brush); /* create brush image buffer if it didn't exist yet */ imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; @@ -478,10 +570,10 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa if (oldtexibuf) { srcx = srcy = 0; - destx = (int)painter->lastpaintpos[0] - (int)pos[0]; - desty = (int)painter->lastpaintpos[1] - (int)pos[1]; w = oldtexibuf->x; h = oldtexibuf->y; + destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); + desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } @@ -514,7 +606,7 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { float invw = 1.0f / (float)s->canvas->x; float invh = 1.0f / (float)s->canvas->y; @@ -522,19 +614,19 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const flo int ipos[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - size / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - size / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); if (mapmode == MTEX_MAP_MODE_STENCIL) { /* map from view coordinates of brush to region coordinates */ UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region(s->v2d, (ipos[0] + size) * invw, (ipos[1] + size) * invh, &xmax, &ymax); + UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ mapping->xmin = xmin; mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)size; - mapping->ymax = (ymax - ymin) / (float)size; + mapping->xmax = (xmax - xmin) / (float)diameter; + mapping->ymax = (ymax - ymin) / (float)diameter; } else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1 */ @@ -545,104 +637,126 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const flo } else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { /* view mapping */ - mapping->xmin = mouse[0] - size * 0.5f + 0.5f; - mapping->ymin = mouse[1] - size * 0.5f + 0.5f; + mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; + mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = -size * 0.5f + 0.5f + (int)pos[0] - (int)startpos[0]; - mapping->ymin = -size * 0.5f + 0.5f + (int)pos[1] - (int)startpos[1]; + mapping->xmin = (int)(-diameter * 0.5) + (int)pos[0] - (int)startpos[0]; + mapping->ymin = (int)(-diameter * 0.5) + (int)pos[1] - (int)startpos[1]; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2]) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * BKE_brush_size_get(scene, brush); - const int size = (cache->size) ? cache->size : diameter; - const float alpha = BKE_brush_alpha_get(scene, brush); - const bool use_masking = painter->cache.use_masking; + const int diameter = 2 * size; bool do_random = false; bool do_partial_update = false; - bool do_view = false; + bool update_color = (brush->flag & BRUSH_USE_GRADIENT) && + ((ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP)) || + (cache->last_pressure != pressure)); float tex_rotation = -brush->mtex.rot; float mask_rotation = -brush->mask_mtex.rot; + painter->pool = BKE_image_pool_new(); + /* determine how can update based on textures used */ if (painter->cache.is_texbrush) { if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - do_view = true; tex_rotation += ups->brush_rotation; } else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) do_random = true; - else + else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) do_partial_update = true; - brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, + brush_painter_2d_tex_mapping(s, diameter, painter->startpaintpos, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } if (painter->cache.is_maskbrush) { + bool renew_maxmask = false; + bool do_partial_update_mask = false; + /* invalidate case for all mapping modes */ if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - do_view = true; mask_rotation += ups->brush_rotation; } - else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - do_random = true; - else - do_partial_update = true; + else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { + renew_maxmask = true; + } + else if (!(brush->flag & BRUSH_ANCHORED)) { + do_partial_update_mask = true; + renew_maxmask = true; + } + /* explicilty disable partial update even if it has been enabled above */ + if (brush->mask_pressure) { + do_partial_update_mask = false; + renew_maxmask = true; + } - brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, - brush->mask_mtex.brush_map_mode, &painter->mask_mapping); + if ((diameter != cache->lastdiameter) || + (mask_rotation != cache->last_mask_rotation) || + renew_maxmask) + { + if (cache->tex_mask) { + MEM_freeN(cache->tex_mask); + cache->tex_mask = NULL; + } + + brush_painter_2d_tex_mapping(s, diameter, painter->startpaintpos, pos, mouse, + brush->mask_mtex.brush_map_mode, &painter->mask_mapping); + + if (do_partial_update_mask) + brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + else + cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); + cache->last_mask_rotation = mask_rotation; + } } - if (do_view || do_random) - do_partial_update = false; + /* curve mask can only change if the size changes */ + if (diameter != cache->lastdiameter) { + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); + cache->curve_mask = NULL; + } - painter->pool = BKE_image_pool_new(); + cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); + } /* detect if we need to recreate image brush buffer */ - if (diameter != cache->lastsize || - alpha != cache->lastalpha || - brush->jitter != cache->lastjitter || - tex_rotation != cache->last_tex_rotation || - mask_rotation != cache->last_mask_rotation || - do_random) + if ((diameter != cache->lastdiameter) || + (tex_rotation != cache->last_tex_rotation) || + do_random || + update_color) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); cache->ibuf = NULL; } - if (cache->mask) { - MEM_freeN(cache->mask); - cache->mask = NULL; - } if (do_partial_update) { - /* do partial update of texture + recreate mask */ - cache->mask = brush_painter_mask_new(painter, size); - brush_painter_imbuf_partial_update(painter, pos); + /* do partial update of texture */ + brush_painter_imbuf_partial_update(painter, pos, diameter); } else { - /* create brush and mask from scratch */ - if (use_masking) - cache->mask = brush_painter_mask_new(painter, size); - cache->ibuf = brush_painter_imbuf_new(painter, size); + /* create brush from scratch */ + cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); } - cache->lastsize = diameter; - cache->lastalpha = alpha; - cache->lastjitter = brush->jitter; + cache->lastdiameter = diameter; cache->last_tex_rotation = tex_rotation; - cache->last_mask_rotation = mask_rotation; + cache->last_pressure = pressure; } else if (do_partial_update) { /* do only partial update of texture */ @@ -650,7 +764,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos); + brush_painter_imbuf_partial_update(painter, pos, diameter); } } @@ -703,7 +817,7 @@ static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus } } -static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus) +static float paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus, float w) { float inrgb[4]; @@ -716,16 +830,23 @@ static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo paint_2d_ibuf_rgb_get(ibuf, x, y, 0, inrgb); } + mul_v4_fl(inrgb, w); add_v4_v4(outrgb, inrgb); - return 1; + return w; } -static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool is_torus) +static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short is_torus) { - int x, y, count, xi, yi, xo, yo; + bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + float threshold = s->brush->sharp_threshold; + int x, y, xi, yi, xo, yo, xk, yk; + float count; int out_off[2], in_off[2], dim[2]; + int diff_pos[2]; float outrgb[4]; + float rgba[4]; + BlurKernel *kernel = s->blurkernel; dim[0] = ibufb->x; dim[1] = ibufb->y; @@ -741,28 +862,52 @@ static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool return; } + /* find offset inside mask buffers to sample them */ + sub_v2_v2v2_int(diff_pos, out_off, in_off); + for (y = 0; y < dim[1]; y++) { for (x = 0; x < dim[0]; x++) { /* get input pixel */ xi = in_off[0] + x; yi = in_off[1] + y; - count = 1; - paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); + count = 0.0; + paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, rgba); + zero_v4(outrgb); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + count += paint_2d_ibuf_add_if(ibuf, xi + xk - kernel->pixel_len, + yi + yk - kernel->pixel_len, outrgb, is_torus, + kernel->wdata[xk + yk * kernel->side]); + } + } - count += paint_2d_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); + if (count > 0.0f) { + mul_v4_fl(outrgb, 1.0f / (float)count); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); + if (sharpen) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(outrgb, rgba, outrgb); - mul_v4_fl(outrgb, 1.0f / (float)count); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshhold */ + outrgb[0] = outrgb[1] = outrgb[2] = rgb_to_grayscale(outrgb); + if (fabsf(outrgb[0]) > threshold) { + float mask = BKE_brush_alpha_get(s->scene, s->brush); + float alpha = rgba[3]; + rgba[3] = outrgb[3] = mask; + /* add to enhance edges */ + blend_color_add_float(outrgb, rgba, outrgb); + outrgb[3] = alpha; + } + else + copy_v4_v4(outrgb, rgba); + } + } + else + copy_v4_v4(outrgb, rgba); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; @@ -830,10 +975,10 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos) tot = paint_2d_torus_split_region(region, ibufb, ibuf); for (a = 0; a < tot; a++) - IMB_rectblend(ibufb, ibufb, ibuf, NULL, NULL, 0, region[a].destx, region[a].desty, + IMB_rectblend(ibufb, ibufb, ibuf, NULL, NULL, NULL, 0, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, IMB_BLEND_COPY_RGB); + region[a].width, region[a].height, IMB_BLEND_COPY_RGB, false); } static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) @@ -844,10 +989,10 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); - IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, - IMB_BLEND_COPY_ALPHA); - IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, - IMB_BLEND_COPY_RGB); + IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, + IMB_BLEND_COPY_ALPHA, false); + IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, + IMB_BLEND_COPY_RGB, false); return clonebuf; } @@ -858,17 +1003,16 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); } -static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const float lastpos[2], const float pos[2]) +static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); - ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL; + ImBuf *clonebuf = NULL, *frombuf; ImagePaintRegion region[4]; short torus = s->brush->flag & BRUSH_TORUS; short blend = s->blend; const float *offset = s->brush->clone.offset; float liftpos[2]; - float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush); - unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f); + float mask_max = BKE_brush_alpha_get(s->scene, s->brush); int bpos[2], blastpos[2], bliftpos[2]; int a, tot; @@ -876,7 +1020,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f /* lift from canvas */ if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s->canvas, ibufb, bpos, torus); + paint_2d_lift_soften(s, s->canvas, ibufb, bpos, torus); } else if (s->tool == PAINT_TOOL_SMEAR) { if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) @@ -903,9 +1047,6 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); tot = 1; } - - if (s->do_masking) - tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); /* blend into canvas */ for (a = 0; a < tot; a++) { @@ -916,11 +1057,14 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ int tilex, tiley, tilew, tileh, tx, ty; + ImBuf *tmpbuf; imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty, region[a].width, region[a].height, &tilex, &tiley, &tilew, &tileh); - + + tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { /* retrieve original pixels + mask from undo buffer */ @@ -929,31 +1073,32 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE; if (s->canvas->rect_float) - tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); else - tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, - maskb, mask_max, + curveb, texmaskb, mask_max, region[a].destx, region[a].desty, origx, origy, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); + region[a].width, region[a].height, blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); } } + + IMB_freeImBuf(tmpbuf); } else { /* no masking, composite brush directly onto canvas */ - IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, NULL, 0, + IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); + region[a].width, region[a].height, blend, false); } } if (clonebuf) IMB_freeImBuf(clonebuf); - if (tmpbuf) IMB_freeImBuf(tmpbuf); return 1; } @@ -1003,10 +1148,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) } /* set masking */ - s->do_masking = (s->brush->flag & BRUSH_AIRBRUSH || - (s->brush->imagepaint_tool == PAINT_TOOL_SMEAR) || - (s->brush->mtex.tex && !ELEM(s->brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D))) - ? false : true; + s->do_masking = paint_use_opacity_masking(s->brush); return 1; } @@ -1016,11 +1158,15 @@ static void paint_2d_canvas_free(ImagePaintState *s) BKE_image_release_ibuf(s->image, s->canvas, NULL); BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); - if (s->do_masking) - image_undo_remove_masks(); + if (s->blurkernel) { + paint_delete_blur_kernel(s->blurkernel); + MEM_freeN(s->blurkernel); + } + + image_undo_remove_masks(); } -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser) +void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser, float pressure, float distance, float size) { float newuv[2], olduv[2]; ImagePaintState *s = ps; @@ -1063,17 +1209,17 @@ void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], in /* OCIO_TODO: float buffers are now always linear, so always use color correction * this should probably be changed when texture painting color space is supported */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data, s->do_masking); + brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); - brush_painter_2d_refresh_cache(s, painter, newuv, mval); + brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); - if (paint_2d_op(s, painter->cache.ibuf, painter->cache.mask, olduv, newuv)) + if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv)) s->need_redraw = true; BKE_image_release_ibuf(s->image, ibuf, NULL); } -void *paint_2d_new_stroke(bContext *C, wmOperator *op) +void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) { Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; @@ -1102,10 +1248,14 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op) return NULL; } + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + s->blurkernel = paint_new_blur_kernel(brush); + } + paint_brush_init_tex(s->brush); /* create painter */ - s->painter = brush_painter_2d_new(scene, s->brush); + s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); return s; } @@ -1134,6 +1284,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) /* compositor listener deals with updating */ WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + DAG_id_tag_update(&s->image->id, 0); } else { if (!s->sima || !s->sima->lock) @@ -1153,3 +1304,334 @@ void paint_2d_stroke_done(void *ps) MEM_freeN(s); } + +static void paint_2d_fill_add_pixel_byte( + const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, + const float color[4], float threshold_sq) +{ + int coordinate; + + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; + + coordinate = y_px * ibuf->x + x_px; + + if (!BLI_BITMAP_TEST(touched, coordinate)) { + float color_f[4]; + unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); + rgba_uchar_to_float(color_f, color_b); + + if (compare_len_squared_v3v3(color_f, color, threshold_sq)) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } +} + +static void paint_2d_fill_add_pixel_float( + const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, + const float color[4], float threshold_sq) +{ + int coordinate; + + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; + + coordinate = y_px * ibuf->x + x_px; + + if (!BLI_BITMAP_TEST(touched, coordinate)) { + if (compare_len_squared_v3v3(ibuf->rect_float + 4 * coordinate, color, threshold_sq)) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } +} + +/* this function expects linear space color values */ +void paint_2d_bucket_fill( + const bContext *C, const float color[3], Brush *br, + const float mouse_init[2], + void *ps) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float strength = br ? br->alpha : 1.0f; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + do_float = (ibuf->rect_float != NULL); + /* first check if our image is float. If it is not we should correct the color to + * be in gamma space. strictly speaking this is not correct, but blender does not paint + * byte images in linear space */ + if (!do_float) { + linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); + *(((char *)&color_b) + 3) = strength * 255; + } + else { + copy_v3_v3(color_f, color); + color_f[3] = strength; + } + + if (!mouse_init || !br) { + /* first case, no image UV, fill the whole image */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + if (do_float) { + for (; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), color_f); + } + } + } + else { + for (; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), (unsigned char *)&color_b); + } + } + } + } + else { + /* second case, start sweeping the neighboring pixels, looking for pixels whose + * value is within the brush fill threshold from the fill color */ + BLI_Stack *stack; + BLI_bitmap *touched; + int coordinate; + int width = ibuf->x; + float image_init[2]; + int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; + float pixel_color[4]; + float threshold_sq = br->fill_threshold * br->fill_threshold; + + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + x_px = image_init[0] * ibuf->x; + y_px = image_init[1] * ibuf->y; + + if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { + BKE_image_release_ibuf(ima, ibuf, NULL); + return; + } + + /* change image invalidation method later */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + stack = BLI_stack_new(sizeof(int), __func__); + touched = BLI_BITMAP_NEW(ibuf->x * ibuf->y, "bucket_fill_bitmap"); + + coordinate = (y_px * ibuf->x + x_px); + + if (do_float) { + copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); + } + else { + int pixel_color_b = *(ibuf->rect + coordinate); + rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); + } + + BLI_stack_push(stack, &coordinate); + BLI_BITMAP_SET(touched, coordinate, true); + + if (do_float) { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_float(ibuf->rect_float + 4 * (coordinate), + ibuf->rect_float + 4 * (coordinate), + color_f, br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_float(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + else { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_byte((unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)&color_b, br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_byte(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + + MEM_freeN(touched); + BLI_stack_free(stack); + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); +} + +void paint_2d_gradient_fill( + const bContext *C, Brush *br, + const float mouse_init[2], const float mouse_final[2], + void *ps) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float image_init[2], image_final[2]; + float tangent[2]; + float line_len_sq_inv, line_len; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + image_final[0] *= ibuf->x; + image_final[1] *= ibuf->y; + + image_init[0] *= ibuf->x; + image_init[1] *= ibuf->y; + + /* some math to get needed gradient variables */ + sub_v2_v2v2(tangent, image_final, image_init); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrt(line_len); + + do_float = (ibuf->rect_float != NULL); + + /* this will be substituted by something else when selection is available */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + do_colorband(br->gradient, f, color_f); + /* convert to premultiplied */ + mul_v3_fl(color_f, color_f[3]); + color_f[3] *= br->alpha; + IMB_blend_color_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + color_f, br->blend); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + + do_colorband(br->gradient, f, color_f); + rgba_float_to_uchar((unsigned char *)&color_b, color_f); + ((unsigned char *)&color_b)[3] *= br->alpha; + IMB_blend_color_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)&color_b, br->blend); + } + } + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); +} diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 0154960cf40..1f0b2c80fc4 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -48,30 +48,39 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "PIL_time.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "BKE_camera.h" +#include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_idprop.h" #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_texture.h" #include "UI_view2d.h" +#include "UI_interface.h" #include "ED_paint.h" #include "ED_screen.h" @@ -141,6 +150,7 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_SRC_VIEW 1 #define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 +#define PROJ_SRC_VIEW_FILL 4 #define PROJ_VIEW_DATA_ID "view_data" #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */ @@ -162,6 +172,9 @@ BLI_INLINE unsigned char f_to_char(const float val) /* vert flags */ #define PROJ_VERT_CULL 1 +/* to avoid locking in tile initialization */ +#define TILE_PENDING SET_INT_IN_POINTER(-1) + /* This is mainly a convenience struct used so we can keep an array of images we use * Thir imbufs, etc, in 1 array, When using threads this array is copied for each thread * because 'partRedrawRect' and 'touch' values would not be thread safe */ @@ -169,7 +182,10 @@ typedef struct ProjPaintImage { Image *ima; ImBuf *ibuf; ImagePaintPartialRedraw *partRedrawRect; - void **undoRect; /* only used to build undo tiles after painting */ + volatile void **undoRect; /* only used to build undo tiles during painting */ + unsigned short **maskRect; /* the mask accumulation must happen on canvas, not on space screen bucket. + * Here we store the mask rectangle */ + bool **valid; /* store flag to enforce validation of undo rectangle */ int touch; } ProjPaintImage; @@ -181,9 +197,14 @@ typedef struct ProjPaintState { Scene *scene; int source; /* PROJ_SRC_**** */ + /* the paint color. It can change depending of inverted mode or not */ + float paint_color[3]; + float paint_color_linear[3]; + Brush *brush; short tool, blend, mode; int orig_brush_size; + float brush_size; Object *ob; /* end similarities with ImagePaintState */ @@ -194,10 +215,13 @@ typedef struct ProjPaintState { MVert *dm_mvert; MFace *dm_mface; - MTFace *dm_mtface; - MTFace *dm_mtface_clone; /* other UV map, use for cloning between layers */ + MTFace **dm_mtface; + MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */ MTFace *dm_mtface_stencil; + Image *stencil_ima; + float stencil_value; + /* projection painting only */ MemArena *arena_mt[BLENDER_MAX_THREADS]; /* for multithreading, the first item is sometimes used for non threaded cases too */ LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */ @@ -231,6 +255,7 @@ typedef struct ProjPaintState { bool do_layer_clone; bool do_layer_stencil; bool do_layer_stencil_inv; + bool do_stencil_brush; bool do_occlude; /* Use raytraced occlusion? - ortherwise will paint right through to the back*/ bool do_backfacecull; /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */ @@ -245,7 +270,6 @@ typedef struct ProjPaintState { bool do_masking; /* use masking during painting. Some operations such as airbrush may disable */ bool is_texbrush; /* only to avoid running */ bool is_maskbrush; /* mask brush is applied before masking */ - bool is_maskbrush_tiled; /* mask brush is applied after masking */ #ifndef PROJ_DEBUG_NOSEAMBLEED float seam_bleed_px; #endif @@ -269,6 +293,10 @@ typedef struct ProjPaintState { /* redraw */ bool need_redraw; + + BlurKernel *blurkernel; + + SpinLock *tile_lock; } ProjPaintState; typedef union pixelPointer { @@ -290,14 +318,16 @@ typedef struct ProjPixel { * Store the max mask value to avoid painting over an area with a lower opacity * with an advantage that we can avoid touching the pixel at all, if the * new mask value is lower then mask_accum */ - unsigned short mask_accum; + unsigned short *mask_accum; /* for various reasons we may want to mask out painting onto this pixel */ unsigned short mask; short x_px, y_px; + /* horrible hack, store tile valid flag pointer here to re-validate tiles used for anchored and drag-dot strokes */ + bool *valid; - PixelStore origColor; + PixelPointer origColor; PixelStore newColor; PixelPointer pixel; @@ -310,33 +340,43 @@ typedef struct ProjPixelClone { PixelStore clonepx; } ProjPixelClone; -/* blur, store surrounding colors */ -#define PROJ_PIXEL_SOFTEN_TOT 4 -/* blur picking offset (in screenspace) */ -#define PROJ_PIXEL_SOFTEN_OFS_PX 1.0f +/* undo tile pushing */ +typedef struct { + SpinLock *lock; + bool masked; + unsigned short tile_width; + ImBuf **tmpibuf; + ProjPaintImage *pjima; +} TileInfo; -static const float proj_pixel_soften_v2[PROJ_PIXEL_SOFTEN_TOT][2] = { - {-PROJ_PIXEL_SOFTEN_OFS_PX, 0.0f}, - { 0.0f, -PROJ_PIXEL_SOFTEN_OFS_PX}, - { 0.0f, PROJ_PIXEL_SOFTEN_OFS_PX}, - { PROJ_PIXEL_SOFTEN_OFS_PX, 0.0f}, -}; /* Finish projection painting structs */ -static Image *project_paint_face_image(const ProjPaintState *ps, MTFace *dm_mtface, int face_index) +static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int face_index) { - Image *ima; + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + return &ma->texpaintslot[ma->paint_active_slot]; +} - if (ps->do_new_shading_nodes) { /* cached BKE_scene_use_new_shading_nodes result */ - MFace *mf = ps->dm_mface + face_index; - ED_object_get_active_image(ps->ob, mf->mat_nr + 1, &ima, NULL, NULL); +static Image *project_paint_face_paint_image(const ProjPaintState *ps, int face_index) +{ + if (ps->do_stencil_brush) { + return ps->stencil_ima; } else { - ima = dm_mtface[face_index].tpage; + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + return ma->texpaintslot[ma->paint_active_slot].ima; } +} + - return ima; +static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int face_index) +{ + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + return &ma->texpaintslot[ma->paint_clone_slot]; } /* fast projection bucket array lookup, use the safe version for bound checking */ @@ -477,8 +517,8 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, float *x, float *y) { /* use */ - *x = (float)fmodf(uv[0], 1.0f); - *y = (float)fmodf(uv[1], 1.0f); + *x = fmodf(uv[0], 1.0f); + *y = fmodf(uv[1], 1.0f); if (*x < 0.0f) *x += 1.0f; if (*y < 0.0f) *y += 1.0f; @@ -505,7 +545,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], if (face_index == -1) return 0; - tf = ps->dm_mtface + face_index; + tf = *(ps->dm_mtface + face_index); if (side == 0) { interp_v2_v2v2v2(uv, tf->uv[0], tf->uv[1], tf->uv[2], w); @@ -514,8 +554,9 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], interp_v2_v2v2v2(uv, tf->uv[0], tf->uv[2], tf->uv[3], w); } - ima = project_paint_face_image(ps, ps->dm_mtface, face_index); + ima = project_paint_face_paint_image(ps, face_index); ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */ + if (!ibuf) return 0; if (interp) { float x, y; @@ -762,11 +803,11 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve static bool cmp_uv(const float vec2a[2], const float vec2b[2]) { /* if the UV's are not between 0.0 and 1.0 */ - float xa = (float)fmodf(vec2a[0], 1.0f); - float ya = (float)fmodf(vec2a[1], 1.0f); + float xa = fmodf(vec2a[0], 1.0f); + float ya = fmodf(vec2a[1], 1.0f); - float xb = (float)fmodf(vec2b[0], 1.0f); - float yb = (float)fmodf(vec2b[1], 1.0f); + float xb = fmodf(vec2b[0], 1.0f); + float yb = fmodf(vec2b[1], 1.0f); if (xa < 0.0f) xa += 1.0f; if (ya < 0.0f) ya += 1.0f; @@ -843,7 +884,7 @@ static bool pixel_bounds_array(float (*uv)[2], rcti *bounds_px, const int ibuf_x static void project_face_winding_init(const ProjPaintState *ps, const int face_index) { /* detect the winding of faces in uv space */ - MTFace *tf = ps->dm_mtface + face_index; + MTFace *tf = ps->dm_mtface[face_index]; float winding = cross_tri_v2(tf->uv[0], tf->uv[1], tf->uv[2]); if (ps->dm_mface[face_index].v4) @@ -868,7 +909,7 @@ static bool check_seam(const ProjPaintState *ps, MFace *mf; MTFace *tf; const MFace *orig_mf = ps->dm_mface + orig_face; - const MTFace *orig_tf = ps->dm_mtface + orig_face; + const MTFace *orig_tf = ps->dm_mtface[orig_face]; /* vert indices from face vert order indices */ i1 = (*(&orig_mf->v1 + orig_i1_fidx)); @@ -890,13 +931,13 @@ static bool check_seam(const ProjPaintState *ps, /* Only need to check if 'i2_fidx' is valid because we know i1_fidx is the same vert on both faces */ if (i2_fidx != -1) { - Image *tpage = project_paint_face_image(ps, ps->dm_mtface, face_index); - Image *orig_tpage = project_paint_face_image(ps, ps->dm_mtface, orig_face); + Image *tpage = project_paint_face_paint_image(ps, face_index); + Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); BLI_assert(i1_fidx != -1); /* This IS an adjacent face!, now lets check if the UVs are ok */ - tf = ps->dm_mtface + face_index; + tf = ps->dm_mtface[face_index]; /* set up the other face */ *other_face = face_index; @@ -909,7 +950,7 @@ static bool check_seam(const ProjPaintState *ps, /* first test if they have the same image */ if ((orig_tpage == tpage) && cmp_uv(orig_tf->uv[orig_i1_fidx], tf->uv[i1_fidx]) && - cmp_uv(orig_tf->uv[orig_i2_fidx], tf->uv[i2_fidx]) ) + cmp_uv(orig_tf->uv[orig_i2_fidx], tf->uv[i2_fidx])) { /* if faces don't have the same winding in uv space, * they are on the same side so edge is boundary */ @@ -1168,7 +1209,7 @@ static float project_paint_uvpixel_mask( if (ps->do_layer_stencil) { /* another UV maps image is masking this one's */ ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_image(ps, ps->dm_mtface_stencil, face_index); + Image *other_tpage = ps->stencil_ima; const MTFace *tf_other = ps->dm_mtface_stencil + face_index; if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { @@ -1296,24 +1337,70 @@ static int project_paint_pixel_sizeof(const short tool) } } +static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) +{ + ProjPaintImage *pjIma = tinf->pjima; + int tile_index = tx + ty * tinf->tile_width; + bool generate_tile = false; + + /* double check lock to avoid locking */ + if (UNLIKELY(!pjIma->undoRect[tile_index])) { + if (tinf->lock) + BLI_spin_lock(tinf->lock); + if (LIKELY(!pjIma->undoRect[tile_index])) { + pjIma->undoRect[tile_index] = TILE_PENDING; + generate_tile = true; + } + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + + if (generate_tile) { + volatile void *undorect; + if (tinf->masked) { + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true); + } + else { + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true); + } + + pjIma->ibuf->userflags |= IB_BITMAPDIRTY; + /* tile ready, publish */ + if (tinf->lock) + BLI_spin_lock(tinf->lock); + pjIma->undoRect[tile_index] = undorect; + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + + } + + return tile_index; +} /* run this function when we know a bucket's, face's pixel can be initialized, * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */ static ProjPixel *project_paint_uvpixel_init( const ProjPaintState *ps, MemArena *arena, - const ImBuf *ibuf, + const TileInfo *tinf, int x_px, int y_px, const float mask, const int face_index, - const int image_index, const float pixelScreenCo[4], const float world_spaceCo[3], const int side, const float w[3]) { ProjPixel *projPixel; - + int x_tile, y_tile; + int x_round, y_round; + int tile_offset; + /* volatile is important here to ensure pending check is not optimized away by compiler*/ + volatile int tile_index; + + ProjPaintImage *projima = tinf->pjima; + ImBuf *ibuf = projima->ibuf; /* wrap pixel location */ x_px = mod_i(x_px, ibuf->x); @@ -1321,19 +1408,36 @@ static ProjPixel *project_paint_uvpixel_init( BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); projPixel = (ProjPixel *)BLI_memarena_alloc(arena, ps->pixel_sizeof); + + /* calculate the undo tile offset of the pixel, used to store the original + * pixel color and accumulated mask if any */ + x_tile = x_px >> IMAPAINT_TILE_BITS; + y_tile = y_px >> IMAPAINT_TILE_BITS; + + x_round = x_tile * IMAPAINT_TILE_SIZE; + y_round = y_tile * IMAPAINT_TILE_SIZE; //memset(projPixel, 0, size); + tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); + + /* other thread may be initializing the tile so wait here */ + while (projima->undoRect[tile_index] == TILE_PENDING) + ; + + BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + + projPixel->valid = projima->valid[tile_index]; + if (ibuf->rect_float) { projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); - projPixel->origColor.f[0] = projPixel->pixel.f_pt[0]; - projPixel->origColor.f[1] = projPixel->pixel.f_pt[1]; - projPixel->origColor.f[2] = projPixel->pixel.f_pt[2]; - projPixel->origColor.f[3] = projPixel->pixel.f_pt[3]; + projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; zero_v4(projPixel->newColor.f); } else { - projPixel->pixel.ch_pt = ((unsigned char *)ibuf->rect + ((x_px + y_px * ibuf->x) * 4)); - projPixel->origColor.uint = *projPixel->pixel.uint_pt; + projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); + projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; projPixel->newColor.uint = 0; } @@ -1348,7 +1452,10 @@ static ProjPixel *project_paint_uvpixel_init( projPixel->y_px = y_px; projPixel->mask = (unsigned short)(mask * 65535); - projPixel->mask_accum = 0; + if (ps->do_masking) + projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; + else + projPixel->mask_accum = NULL; /* which bounding box cell are we in?, needed for undo */ projPixel->bb_cell_index = ((int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + @@ -1358,8 +1465,8 @@ static ProjPixel *project_paint_uvpixel_init( if (ps->tool == PAINT_TOOL_CLONE) { if (ps->dm_mtface_clone) { ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_image(ps, ps->dm_mtface_clone, face_index); - const MTFace *tf_other = ps->dm_mtface_clone + face_index; + Image *other_tpage = project_paint_face_clone_slot(ps, face_index)->ima; + const MTFace *tf_other = ps->dm_mtface_clone[face_index]; if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { /* BKE_image_acquire_ibuf - TODO - this may be slow */ @@ -1423,7 +1530,8 @@ static ProjPixel *project_paint_uvpixel_init( if (ibuf->rect_float) projPixel->pixel.f_pt[0] = 0; else projPixel->pixel.ch_pt[0] = 0; #endif - projPixel->image_index = image_index; + /* pointer arithmetics */ + projPixel->image_index = projima - ps->projImages; return projPixel; } @@ -2131,15 +2239,24 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot /* One of the most important function for projection painting, since it selects the pixels to be added into each bucket. * initialize pixels from this face where it intersects with the bucket_index, optionally initialize pixels for removing seams */ -static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, const ImBuf *ibuf, const short clamp_u, const short clamp_v) +static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) { /* Projection vars, to get the 3D locations into screen space */ MemArena *arena = ps->arena_mt[thread_index]; LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; + bool threaded = (ps->thread_tot > 1); + + TileInfo tinf = { + ps->tile_lock, + ps->do_masking, + IMAPAINT_TILE_NUMBER(ibuf->x), + tmpibuf, + ps->projImages + image_index + }; const MFace *mf = ps->dm_mface + face_index; - const MTFace *tf = ps->dm_mtface + face_index; + const MTFace *tf = ps->dm_mtface[face_index]; /* UV/pixel seeking data */ int x; /* Image X-Pixel */ @@ -2258,6 +2375,10 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i CLAMP(bounds_px.ymax, 0, ibuf->y); } + /* + project_paint_undo_tiles_init(&bounds_px, ps->projImages + image_index, tmpibuf, + tile_width, threaded, ps->do_masking); + */ /* clip face and */ has_isect = 0; @@ -2300,8 +2421,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, - image_index, pixelScreenCo, wco, side, w), + project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, face_index, + pixelScreenCo, wco, side, w), arena ); } @@ -2333,7 +2454,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (ps->seam_bleed_px > 0.0f) { int face_seam_flag; - if (ps->thread_tot > 1) + if (threaded) BLI_lock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ face_seam_flag = ps->faceSeamFlags[face_index]; @@ -2351,7 +2472,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if ((face_seam_flag & (PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2 | PROJ_FACE_SEAM3 | PROJ_FACE_SEAM4)) == 0) { - if (ps->thread_tot > 1) + if (threaded) BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ } @@ -2376,7 +2497,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i uv_image_outset(tf_uv_pxoffset, outset_uv, ps->seam_bleed_px, ibuf->x, ibuf->y, mf->v4 != 0); /* ps->faceSeamUVs cant be modified when threading, now this is done we can unlock */ - if (ps->thread_tot > 1) + if (threaded) BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ vCoSS[0] = ps->screenCoords[mf->v1]; @@ -2520,7 +2641,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, wco, side, w), + project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, face_index, + pixelScreenCo, wco, side, w), arena ); } @@ -2589,6 +2711,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index ImBuf *ibuf = NULL; Image *tpage_last = NULL, *tpage; Image *ima = NULL; + ImBuf *tmpibuf = NULL; if (ps->image_tot == 1) { /* Simple loop, no context switching */ @@ -2596,7 +2719,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index ima = ps->projImages[0].ima; for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); } } else { @@ -2606,7 +2729,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index face_index = GET_INT_FROM_POINTER(node->link); /* Image context switching */ - tpage = project_paint_face_image(ps, ps->dm_mtface, face_index); + tpage = project_paint_face_paint_image(ps, face_index); if (tpage_last != tpage) { tpage_last = tpage; @@ -2620,10 +2743,13 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index } /* context switching done */ - project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); } } + if (tmpibuf) + IMB_freeImBuf(tmpibuf); + ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; } @@ -2771,11 +2897,17 @@ static void project_paint_begin(ProjPaintState *ps) ProjPaintImage *projIma; Image *tpage_last = NULL, *tpage; + TexPaintSlot *slot_last = NULL, *slot = NULL; + TexPaintSlot *slot_last_clone = NULL, *slot_clone; /* Face vars */ MPoly *mpoly_orig; MFace *mf; - MTFace *tf; + MTFace **tf; + MTFace *tf_base; + + MTFace **tf_clone; + MTFace *tf_clone_base; int a, i; /* generic looping vars */ int image_index = -1, face_index; @@ -2830,13 +2962,15 @@ static void project_paint_begin(ProjPaintState *ps) return; } - ps->dm_mvert = ps->dm->getVertArray(ps->dm); - ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); - ps->dm_mtface = ps->dm->getTessFaceDataArray(ps->dm, CD_MTFACE); + DM_update_materials(ps->dm, ps->ob); ps->dm_totvert = ps->dm->getNumVerts(ps->dm); ps->dm_totface = ps->dm->getNumTessFaces(ps->dm); + ps->dm_mvert = ps->dm->getVertArray(ps->dm); + ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); + ps->dm_mtface = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); + if (ps->do_face_sel) { index_mf_to_mpoly = ps->dm->getTessFaceDataArray(ps->dm, CD_ORIGINDEX); index_mp_to_orig = ps->dm->getPolyDataArray(ps->dm, CD_ORIGINDEX); @@ -2852,32 +2986,23 @@ static void project_paint_begin(ProjPaintState *ps) } /* use clone mtface? */ - - - /* Note, use the original mesh for getting the clone and mask layer index - * this avoids re-generating the derived mesh just to get the new index */ if (ps->do_layer_clone) { - //int layer_num = CustomData_get_clone_layer(&ps->dm->faceData, CD_MTFACE); - int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); - if (layer_num != -1) - ps->dm_mtface_clone = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - - if (ps->dm_mtface_clone == NULL || ps->dm_mtface_clone == ps->dm_mtface) { - ps->do_layer_clone = false; - ps->dm_mtface_clone = NULL; - } + ps->dm_mtface_clone = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); } - if (ps->do_layer_stencil) { + if (ps->do_layer_stencil || ps->do_stencil_brush) { //int layer_num = CustomData_get_stencil_layer(&ps->dm->faceData, CD_MTFACE); int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); if (layer_num != -1) ps->dm_mtface_stencil = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - if (ps->dm_mtface_stencil == NULL || ps->dm_mtface_stencil == ps->dm_mtface) { - ps->do_layer_stencil = false; - ps->dm_mtface_stencil = NULL; + if (ps->dm_mtface_stencil == NULL) { + /* get active instead */ + ps->dm_mtface_stencil = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); } + + if (ps->do_stencil_brush) + tf_base = ps->dm_mtface_stencil; } /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ @@ -3092,6 +3217,13 @@ static void project_paint_begin(ProjPaintState *ps) if (reset_threads) ps->thread_tot = 1; + if (ps->thread_tot > 1) { + ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); + BLI_spin_init(ps->tile_lock); + } + + image_undo_init_locks(); + for (a = 0; a < ps->thread_tot; a++) { ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); } @@ -3153,7 +3285,46 @@ static void project_paint_begin(ProjPaintState *ps) is_face_sel = true; } - if (is_face_sel && (tpage = project_paint_face_image(ps, ps->dm_mtface, face_index))) { + if (!ps->do_stencil_brush) { + slot = project_paint_face_paint_slot(ps, face_index); + /* all faces should have a valid slot, reassert here */ + if (slot == NULL) + continue; + + if (slot != slot_last) { + if (!slot->uvname[0] || !(tf_base = CustomData_get_layer_named(&ps->dm->faceData, CD_MTFACE, slot->uvname))) + tf_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + slot_last = slot; + } + + /* don't allow using the same inage for painting and stencilling */ + if (slot->ima == ps->stencil_ima) + continue; + } + + *tf = tf_base + face_index; + + if (ps->do_layer_clone) { + slot_clone = project_paint_face_clone_slot(ps, face_index); + /* all faces should have a valid slot, reassert here */ + if (ELEM(slot_clone, NULL, slot)) + continue; + + tf_clone = ps->dm_mtface_clone + face_index; + + if (slot_clone != slot_last_clone) { + if (!slot_clone->uvname[0] || !(tf_clone_base = CustomData_get_layer_named(&ps->dm->faceData, CD_MTFACE, slot_clone->uvname))) + tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + slot_last_clone = slot_clone; + } + + *tf_clone = tf_clone_base + face_index; + } + + /* tfbase here should be non-null! */ + BLI_assert (tf_base != NULL); + + if (is_face_sel && ((slot && (tpage = slot->ima)) || (tpage = project_paint_face_paint_image(ps, face_index)))) { const float *v1coSS, *v2coSS, *v3coSS, *v4coSS = NULL; v1coSS = ps->screenCoords[mf->v1]; @@ -3251,10 +3422,19 @@ static void project_paint_begin(ProjPaintState *ps) projIma = ps->projImages = (ProjPaintImage *)BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + int size; projIma->ima = node->link; projIma->touch = 0; projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - projIma->partRedrawRect = BLI_memarena_calloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + memset(projIma->partRedrawRect, 0, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); + memset(projIma->undoRect, 0, size); + projIma->maskRect = (unsigned short **) BLI_memarena_alloc(arena, size); + memset(projIma->maskRect, 0, size); + projIma->valid = (bool **) BLI_memarena_alloc(arena, size); + memset(projIma->valid, 0, size); } /* we have built the array, discard the linked list */ @@ -3281,95 +3461,12 @@ static void project_paint_end(ProjPaintState *ps) int a; ProjPaintImage *projIma; - /* build undo data from original pixel colors */ - if (U.uiflag & USER_GLOBALUNDO) { - ProjPixel *projPixel; - ImBuf *tmpibuf = NULL, *tmpibuf_float = NULL; - LinkNode *pixel_node; - void *tilerect; - MemArena *arena = ps->arena_mt[0]; /* threaded arena re-used for non threaded case */ - - int bucket_tot = (ps->buckets_x * ps->buckets_y); /* we could get an X/Y but easier to loop through all possible buckets */ - int bucket_index; - int tile_index; - int x_round, y_round; - int x_tile, y_tile; - int is_float = -1; - - /* context */ - ProjPaintImage *last_projIma; - int last_image_index = -1; - int last_tile_width = 0; - - for (a = 0, last_projIma = ps->projImages; a < ps->image_tot; a++, last_projIma++) { - int size = sizeof(void **) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->y); - last_projIma->undoRect = (void **) BLI_memarena_calloc(arena, size); - last_projIma->ibuf->userflags |= IB_BITMAPDIRTY; - } - - for (bucket_index = 0; bucket_index < bucket_tot; bucket_index++) { - /* loop through all pixels */ - for (pixel_node = ps->bucketRect[bucket_index]; pixel_node; pixel_node = pixel_node->next) { - - /* ok we have a pixel, was it modified? */ - projPixel = (ProjPixel *)pixel_node->link; - - if (last_image_index != projPixel->image_index) { - /* set the context */ - last_image_index = projPixel->image_index; - last_projIma = ps->projImages + last_image_index; - last_tile_width = IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x); - is_float = last_projIma->ibuf->rect_float ? 1 : 0; - } - - - if ((is_float == 0 && projPixel->origColor.uint != *projPixel->pixel.uint_pt) || - (is_float == 1 && - (projPixel->origColor.f[0] != projPixel->pixel.f_pt[0] || - projPixel->origColor.f[1] != projPixel->pixel.f_pt[1] || - projPixel->origColor.f[2] != projPixel->pixel.f_pt[2] || - projPixel->origColor.f[3] != projPixel->pixel.f_pt[3])) - ) - { - - x_tile = projPixel->x_px >> IMAPAINT_TILE_BITS; - y_tile = projPixel->y_px >> IMAPAINT_TILE_BITS; - - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; - - tile_index = x_tile + y_tile * last_tile_width; - - if (last_projIma->undoRect[tile_index] == NULL) { - /* add the undo tile from the modified image, then write the original colors back into it */ - tilerect = last_projIma->undoRect[tile_index] = image_undo_push_tile(last_projIma->ima, last_projIma->ibuf, is_float ? (&tmpibuf_float) : (&tmpibuf), x_tile, y_tile); - } - else { - tilerect = last_projIma->undoRect[tile_index]; - } - - /* This is a BIT ODD, but overwrite the undo tiles image info with this pixels original color - * because allocating the tiles along the way slows down painting */ - - if (is_float) { - float *rgba_fp = (float *)tilerect + (((projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE)) * 4; - copy_v4_v4(rgba_fp, projPixel->origColor.f); - } - else { - ((unsigned int *)tilerect)[(projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE] = projPixel->origColor.uint; - } - } - } - } - - if (tmpibuf) IMB_freeImBuf(tmpibuf); - if (tmpibuf_float) IMB_freeImBuf(tmpibuf_float); - } - /* done calculating undo data */ + image_undo_remove_masks(); /* dereference used image buffers */ for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); + DAG_id_tag_update(&projIma->ima->id, 0); } BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); @@ -3378,6 +3475,14 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN(ps->bucketRect); MEM_freeN(ps->bucketFaces); MEM_freeN(ps->bucketFlags); + MEM_freeN(ps->dm_mtface); + if (ps->do_layer_clone) + MEM_freeN(ps->dm_mtface_clone); + if (ps->thread_tot > 1) { + BLI_spin_end(ps->tile_lock); + MEM_freeN((void *)ps->tile_lock); + } + image_undo_end_locks(); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0f) { @@ -3388,6 +3493,11 @@ static void project_paint_end(ProjPaintState *ps) } #endif + if (ps->blurkernel) { + paint_delete_blur_kernel(ps->blurkernel); + MEM_freeN(ps->blurkernel); + } + if (ps->vertFlags) MEM_freeN(ps->vertFlags); for (a = 0; a < ps->thread_tot; a++) { @@ -3480,7 +3590,7 @@ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) { if (ps->source == PROJ_SRC_VIEW) { float min_brush[2], max_brush[2]; - const float radius = (float)BKE_brush_size_get(ps->scene, ps->brush); + const float radius = ps->brush_size; /* so we don't have a bucket bounds that is way too small to paint into */ // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ @@ -3518,7 +3628,7 @@ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) static bool project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2]) { - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + const int diameter = 2 * ps->brush_size; if (ps->thread_tot > 1) BLI_lock_thread(LOCK_CUSTOM1); @@ -3580,7 +3690,7 @@ static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, floa clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, clone_rgba, ps->blend); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); } else { IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); @@ -3598,7 +3708,7 @@ static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, fl mul_v4_v4fl(clone_rgba, clone_pt, mask); if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f, clone_rgba, ps->blend); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); } else { IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); @@ -3636,42 +3746,58 @@ static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, fl BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); } -/* do_projectpaint_soften for float & byte - */ -static float inv_pow2(float f) -{ - f = 1.0f - f; - f = f * f; - return 1.0f - f; -} - static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels) { - unsigned int accum_tot = 0; - unsigned int i; - + float accum_tot = 0.0; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; float *rgba = projPixel->newColor.f; - /* sigh, mask values tend to need to be a _lot_ stronger with blur */ - mask = inv_pow2(mask); - /* rather then painting, accumulate surrounding colors */ zero_v4(rgba); - for (i = 0; i < PROJ_PIXEL_SOFTEN_TOT; i++) { - float co_ofs[2]; - float rgba_tmp[4]; - sub_v2_v2v2(co_ofs, projPixel->projCoSS, proj_pixel_soften_v2[i]); - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - add_v4_v4(rgba, rgba_tmp); - accum_tot++; + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {xk - kernel->pixel_len, yk - kernel->pixel_len}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } } } if (LIKELY(accum_tot != 0)) { mul_v4_fl(rgba, 1.0f / (float)accum_tot); - blend_color_interpolate_float(rgba, rgba, projPixel->pixel.f_pt, mask); + + if (ps->mode == BRUSH_STROKE_INVERT) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshhold */ + rgba[0] = rgba[1] = rgba[2] = rgb_to_grayscale(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = projPixel->pixel.f_pt[3]; + projPixel->pixel.f_pt[3] = rgba[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); + projPixel->pixel.f_pt[3] = alpha; + } + else + return; + } + else { + blend_color_interpolate_float(rgba, rgba, projPixel->pixel.f_pt, mask); + } + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); } } @@ -3679,24 +3805,27 @@ static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, f static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels) { - unsigned int accum_tot = 0; - unsigned int i; - + float accum_tot = 0; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; float rgba[4]; /* convert to byte after */ - /* sigh, mask values tend to need to be a _lot_ stronger with blur */ - mask = inv_pow2(mask); - /* rather then painting, accumulate surrounding colors */ zero_v4(rgba); - for (i = 0; i < PROJ_PIXEL_SOFTEN_TOT; i++) { - float co_ofs[2]; - float rgba_tmp[4]; - sub_v2_v2v2(co_ofs, projPixel->projCoSS, proj_pixel_soften_v2[i]); - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - add_v4_v4(rgba, rgba_tmp); - accum_tot++; + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {xk - kernel->pixel_len, yk - kernel->pixel_len}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } } } @@ -3704,9 +3833,34 @@ static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, flo unsigned char *rgba_ub = projPixel->newColor.ch; mul_v4_fl(rgba, 1.0f / (float)accum_tot); - premul_float_to_straight_uchar(rgba_ub, rgba); - blend_color_interpolate_byte(rgba_ub, rgba_ub, projPixel->pixel.ch_pt, mask); + if (ps->mode == BRUSH_STROKE_INVERT) { + float rgba_pixel[4]; + + straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); + + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, rgba_pixel, rgba); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshhold */ + rgba[0] = rgba[1] = rgba[2] = rgb_to_grayscale(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = rgba_pixel[3]; + rgba[3] = rgba_pixel[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, rgba_pixel, rgba); + + rgba[3] = alpha; + premul_float_to_straight_uchar(rgba_ub, rgba); + } + else + return; + } + else { + premul_float_to_straight_uchar(rgba_ub, rgba); + blend_color_interpolate_byte(rgba_ub, rgba_ub, projPixel->pixel.ch_pt, mask); + } BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); } } @@ -3716,7 +3870,7 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const float rgb[3]; unsigned char rgba_ub[4]; - copy_v3_v3(rgb, ps->brush->rgb); + copy_v3_v3(rgb, ps->paint_color); if (ps->is_texbrush) { mul_v3_v3(rgb, texrgb); @@ -3728,7 +3882,7 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const rgba_ub[3] = f_to_char(mask); if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, rgba_ub, ps->blend); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); } else { IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); @@ -3739,7 +3893,7 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, con { float rgba[4]; - srgb_to_linearrgb_v3_v3(rgba, ps->brush->rgb); + copy_v3_v3(rgba, ps->paint_color_linear); if (ps->is_texbrush) mul_v3_v3(rgba, texrgb); @@ -3748,13 +3902,42 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, con rgba[3] = mask; if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f, rgba, ps->blend); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); } else { IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); } } +static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask) +{ + unsigned char rgba_ub[4]; + rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } +} + +static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) +{ + float rgba[4]; + rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; + rgba[3] = mask; + + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } +} + + /* run this for single and multithreaded painting */ static void *do_projectpaint_thread(void *ph_v) { @@ -3788,7 +3971,7 @@ static void *do_projectpaint_thread(void *ph_v) float co[2]; unsigned short mask_short; const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); - const float brush_radius = (float)BKE_brush_size_get(ps->scene, brush); + const float brush_radius = ps->brush_size; const float brush_radius_sq = brush_radius * brush_radius; /* avoid a square root with every dist comparison */ short lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : brush->flag & BRUSH_LOCK_ALPHA; @@ -3838,32 +4021,102 @@ static void *do_projectpaint_thread(void *ph_v) } /* end copy */ - if (is_floatbuf) { - /* re-project buffer is assumed byte - TODO, allow float */ - bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float newColor_f[4]; - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + /* fill tools */ + if (ps->source == PROJ_SRC_VIEW_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + /* these could probably be cached instead of being done per pixel */ + float tangent[2]; + float line_len_sq_inv, line_len; + float f; + float color_f[4]; + float p[2] = {projPixel->projCoSS[0] - lastpos[0], projPixel->projCoSS[1] - lastpos[1]}; + + sub_v2_v2v2(tangent, pos, lastpos); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrt(line_len); + + switch (brush->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + do_colorband(brush->gradient, f, color_f); + color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + + if (is_floatbuf) { + /* convert to premultipied */ + mul_v3_fl(color_f, color_f[3]); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + color_f, ps->blend); + } + else { + rgba_float_to_uchar(projPixel->newColor.ch, color_f); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch, ps->blend); + } + } + else { + if (is_floatbuf) { + float newColor_f[4]; + newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + copy_v3_v3(newColor_f, ps->paint_color_linear); - straight_uchar_to_premul_float(newColor_f, projPixel->newColor.ch); - IMB_colormanagement_colorspace_to_scene_linear_v4(newColor_f, true, ps->reproject_ibuf->rect_colorspace); - mul_v4_v4fl(newColor_f, newColor_f, mask); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + newColor_f, ps->blend); + } + else { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] = mask * 255 * brush->alpha; - blend_color_mix_float(projPixel->pixel.f_pt, projPixel->origColor.f, - newColor_f); + rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch, ps->blend); + } } + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + last_partial_redraw_cell->x1 = min_ii(last_partial_redraw_cell->x1, (int)projPixel->x_px); + last_partial_redraw_cell->y1 = min_ii(last_partial_redraw_cell->y1, (int)projPixel->y_px); + + last_partial_redraw_cell->x2 = max_ii(last_partial_redraw_cell->x2, (int)projPixel->x_px + 1); + last_partial_redraw_cell->y2 = max_ii(last_partial_redraw_cell->y2, (int)projPixel->y_px + 1); } else { - /* re-project buffer is assumed byte - TODO, allow float */ - bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] *= mask; - - blend_color_mix_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, - projPixel->newColor.ch); + if (is_floatbuf) { + /* re-project buffer is assumed byte - TODO, allow float */ + bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, + projPixel->projCoSS[0], projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float newColor_f[4]; + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + straight_uchar_to_premul_float(newColor_f, projPixel->newColor.ch); + IMB_colormanagement_colorspace_to_scene_linear_v4(newColor_f, true, ps->reproject_ibuf->rect_colorspace); + mul_v4_v4fl(newColor_f, newColor_f, mask); + + blend_color_mix_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + newColor_f); + } + } + else { + /* re-project buffer is assumed byte - TODO, allow float */ + bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, + projPixel->projCoSS[0], projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] *= mask; + + blend_color_mix_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch); + } } } } @@ -3885,7 +4138,7 @@ static void *do_projectpaint_thread(void *ph_v) if (falloff > 0.0f) { float texrgb[3]; - float mask = falloff; + float mask; if (ps->do_masking) { /* masking to keep brush contribution to a pixel limited. note we do not do @@ -3894,20 +4147,24 @@ static void *do_projectpaint_thread(void *ph_v) * * Instead we use a formula that adds up but approaches brush_alpha slowly * and never exceeds it, which gives nice smooth results. */ - float mask_accum = projPixel->mask_accum; + float mask_accum = *projPixel->mask_accum; + float max_mask = brush_alpha * falloff * 65535.0f; if (ps->is_maskbrush) { float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - CLAMP(texmask, 0.0f, 1.0f); - mask = mask_accum + (brush_alpha * texmask * 65535.0f - mask_accum) * mask; - } - else { - mask = mask_accum + (brush_alpha * 65535.0f - mask_accum) * mask; + max_mask *= texmask; } + + if (brush->flag & BRUSH_ACCUMULATE) + mask = mask_accum + max_mask; + else + mask = mask_accum + (max_mask - mask_accum * falloff); + + mask = min_ff(mask, 65535.0f); mask_short = (unsigned short)mask; - if (mask_short > projPixel->mask_accum) { - projPixel->mask_accum = mask_short; + if (mask_short > *projPixel->mask_accum) { + *projPixel->mask_accum = mask_short; mask = mask_short * (1.0f / 65535.0f); } else { @@ -3916,7 +4173,7 @@ static void *do_projectpaint_thread(void *ph_v) } } else { - mask *= brush_alpha; + mask = brush_alpha * falloff; if (ps->is_maskbrush) { float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); CLAMP(texmask, 0.0f, 1.0f); @@ -3945,10 +4202,6 @@ static void *do_projectpaint_thread(void *ph_v) mask *= texrgba[3]; } - if (ps->is_maskbrush_tiled) { - mask *= BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - } - /* extra mask for normal, layer stencil, .. */ mask *= ((float)projPixel->mask) * (1.0f / 65535.0f); @@ -3964,6 +4217,9 @@ static void *do_projectpaint_thread(void *ph_v) } /* end copy */ + /* validate undo tile, since we will modify t*/ + *projPixel->valid = true; + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; last_partial_redraw_cell->x1 = min_ii(last_partial_redraw_cell->x1, (int)projPixel->x_px); last_partial_redraw_cell->y1 = min_ii(last_partial_redraw_cell->y1, (int)projPixel->y_px); @@ -3987,6 +4243,10 @@ static void *do_projectpaint_thread(void *ph_v) if (is_floatbuf) do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); else do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); break; + case PAINT_TOOL_MASK: + if (is_floatbuf) do_projectpaint_mask_f(ps, projPixel, mask); + else do_projectpaint_mask(ps, projPixel, mask); + break; default: if (is_floatbuf) do_projectpaint_draw_f(ps, projPixel, texrgb, mask); else do_projectpaint_draw(ps, projPixel, texrgb, mask); @@ -3995,8 +4255,8 @@ static void *do_projectpaint_thread(void *ph_v) } if (lock_alpha) { - if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f[3]; - else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch[3]; + if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f_pt[3]; + else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; } /* done painting */ @@ -4114,14 +4374,17 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } -void paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const float pos[2]) +void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], const float pos[2], float pressure, float distance, float size) { ProjPaintState *ps = pps; + Brush *brush = ps->brush; + Scene *scene = ps->scene; int a; + ps->brush_size = size; + /* clone gets special treatment here to avoid going through image initialization */ if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { - Scene *scene = ps->scene; View3D *v3d = ps->v3d; float *cursor = ED_view3d_cursor3d_get(scene, v3d); int mval_i[2] = {(int)pos[0], (int)pos[1]}; @@ -4136,6 +4399,25 @@ void paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const fl return; } + /* handle gradient and inverted stroke color here */ + if (ps->tool == PAINT_TOOL_DRAW) { + paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL); + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else if (ps->tool == PAINT_TOOL_FILL) { + copy_v3_v3(ps->paint_color, BKE_brush_color_get(scene, brush)); + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else if (ps->tool == PAINT_TOOL_MASK) { + ps->stencil_value = brush->weight; + + if ((ps->mode == BRUSH_STROKE_INVERT) ^ + ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) + { + ps->stencil_value = 1.0f - ps->stencil_value; + } + } + /* continue adding to existing partial redraw rects until redraw */ if (!ps->need_redraw) { for (a = 0; a < ps->image_tot; a++) @@ -4160,30 +4442,24 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int Brush *brush = ps->brush; ps->tool = brush->imagepaint_tool; ps->blend = brush->blend; + /* only check for inversion for the soften tool, elsewhere, a resident brush inversion flag can cause issues */ + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + ps->mode = ((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0) ? + BRUSH_STROKE_INVERT : BRUSH_STROKE_NORMAL); + + ps->blurkernel = paint_new_blur_kernel(brush); + } /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ - ps->do_masking = (brush->flag & BRUSH_AIRBRUSH || - (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || - (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D))) - ? false : true; + ps->do_masking = paint_use_opacity_masking(brush); ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - ps->is_maskbrush = false; - ps->is_maskbrush_tiled = false; - if (brush->mask_mtex.tex) { - if (ELEM(brush->mask_mtex.brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_TILED)) { - ps->is_maskbrush_tiled = true; - } - else { - ps->is_maskbrush = true; - } - } + ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; } else { /* brush may be NULL*/ ps->do_masking = false; ps->is_texbrush = false; ps->is_maskbrush = false; - ps->is_maskbrush_tiled = false; } /* sizeof(ProjPixel), since we alloc this a _lot_ */ @@ -4198,6 +4474,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int ps->scene = scene; ps->ob = ob; /* allow override of active object */ + ps->stencil_ima = settings->imapaint.stencil; /* setup projection painting data */ ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1; ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1; @@ -4207,8 +4484,11 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int if (ps->tool == PAINT_TOOL_CLONE) ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; - ps->do_layer_stencil = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? 1 : 0; - ps->do_layer_stencil_inv = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) ? 1 : 0; + ps->do_stencil_brush = ps->brush->imagepaint_tool == PAINT_TOOL_MASK; + /* deactivate stenciling for the stencil brush :) */ + ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && + !(ps->do_stencil_brush) && ps->stencil_ima); + ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0); #ifndef PROJ_DEBUG_NOSEAMBLEED @@ -4236,6 +4516,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) { ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + project_state_init(C, ob, ps, mode); if (ps->tool == PAINT_TOOL_CLONE && mode == BRUSH_STROKE_INVERT) { @@ -4268,6 +4549,10 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m paint_proj_begin_clone(ps, mouse); + /* special full screen draw mode for fill tool */ + if (ps->tool == PAINT_TOOL_FILL) + ps->source = PROJ_SRC_VIEW_FILL; + return ps; } @@ -4316,8 +4601,11 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) int orig_brush_size; IDProperty *idgroup; IDProperty *view_data = NULL; + Object *ob = OBACT; - project_state_init(C, OBACT, &ps, BRUSH_STROKE_NORMAL); + paint_proj_mesh_data_ensure(C, ob, op); + + project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); if (ps.ob == NULL || ps.ob->type != OB_MESH) { BKE_report(op->reports, RPT_ERROR, "No active mesh object"); @@ -4365,7 +4653,6 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) /* override */ ps.is_texbrush = false; ps.is_maskbrush = false; - ps.is_maskbrush_tiled = false; ps.do_masking = false; orig_brush_size = BKE_brush_size_get(scene, ps.brush); BKE_brush_size_set(scene, ps.brush, 32); /* cover the whole image */ @@ -4375,7 +4662,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); + ED_image_undo_restore, ED_image_undo_free, NULL); /* allocate and initialize spatial data structures */ project_paint_begin(&ps); @@ -4508,3 +4795,132 @@ void PAINT_OT_image_from_view(wmOperatorType *ot) RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); } + +/* Add layer operator */ + +static EnumPropertyItem layer_type_items[] = { + {MAP_COL, "DIFFUSE_COLOR", 0, "Diffuse Color", ""}, + {MAP_REF, "DIFFUSE_INTENSITY", 0, "Diffuse Intensity", ""}, + {MAP_ALPHA, "ALPHA", 0, "Alpha", ""}, + {MAP_TRANSLU, "TRANSLUCENCY", 0, "Translucency", ""}, + {MAP_COLSPEC, "SPECULAR_COLOR", 0, "Specular Color", ""}, + {MAP_SPEC, "SPECULAR_INTENSITY", 0, "Specular Intensity", ""}, + {MAP_HAR, "SPECULAR_HARDNESS", 0, "Specular Hardness", ""}, + {MAP_AMB, "AMBIENT", 0, "Ambient", ""}, + {MAP_EMIT, "EMMIT", 0, "Emmit", ""}, + {MAP_COLMIR, "MIRROR_COLOR", 0, "Mirror Color", ""}, + {MAP_RAYMIRR, "RAYMIRROR", 0, "Ray Mirror", ""}, + {MAP_NORM, "NORMAL", 0, "Normal", ""}, + {MAP_WARP, "WARP", 0, "Warp", ""}, + {MAP_DISPLACE, "DISPLACE", 0, "Displace", ""}, + {0, NULL, 0, NULL, NULL} +}; + +bool proj_paint_add_slot(bContext *C, int type, Material *ma) +{ + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + int i; + ImagePaintSettings *imapaint = &CTX_data_tool_settings(C)->imapaint; + bool use_nodes = BKE_scene_use_new_shading_nodes(scene); + int width; + int height; + + if (!ob) + return false; + + /* should not be allowed, but just in case */ + if (imapaint->slot_xresolution_default == 0) + imapaint->slot_xresolution_default = 1024; + if (imapaint->slot_yresolution_default == 0) + imapaint->slot_yresolution_default = 1024; + + width = imapaint->slot_xresolution_default; + height = imapaint->slot_yresolution_default; + + if (!ma) + ma = give_current_material(ob, ob->actcol); + + if (ma) { + + if (use_nodes) { + /* not supported for now */ + } + else { + MTex *mtex = add_mtex_id(&ma->id, -1); + + /* successful creation of mtex layer, now create set */ + if (mtex) { + Main *bmain = CTX_data_main(C); + Image *ima; + const char *name; + + /* get the name of the texture layer type */ + i = RNA_enum_from_value(layer_type_items, type); + BLI_assert(i != -1); + name = layer_type_items[i].name; + + mtex->tex = add_texture(bmain, DATA_(name)); + mtex->mapto = type; + + if (mtex->tex) { + char imagename[FILE_MAX]; + float color[4]; + bool use_float = type == MAP_NORM; + + copy_v4_v4(color, imapaint->slot_color_default); + if (use_float) { + mul_v3_fl(color, color[3]); + } + else { + /* crappy workaround because we only upload straight color to OpenGL and that makes + * painting result on viewport too opaque */ + color[3] = 1.0; + } + + /* take the second letter to avoid the ID identifier */ + BLI_snprintf(imagename, FILE_MAX, "%s_%s", &ma->id.name[2], name); + + ima = mtex->tex->ima = BKE_image_add_generated(bmain, width, height, imagename, 32, use_float, + IMA_GENTYPE_BLANK, color); + + BKE_texpaint_slot_refresh_cache(ma, false); + BKE_image_signal(ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); + WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, mtex->tex); + WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + ED_area_tag_redraw(CTX_wm_area(C)); + } + + WM_event_add_notifier(C, NC_TEXTURE, CTX_data_scene(C)); + return true; + } + } + } + + return false; +} + +static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) +{ + int type = RNA_enum_get(op->ptr, "type"); + + return proj_paint_add_slot(C, type, NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Texture Paint Slot"; + ot->description = "Add a texture paint slot"; + ot->idname = "PAINT_OT_add_texture_paint_slot"; + + /* api callbacks */ + ot->exec = texture_paint_add_texture_paint_slot_exec; + ot->poll = ED_operator_region_view3d_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index b20c1756d75..f49699faad7 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -38,7 +38,9 @@ struct bglMats; struct Brush; struct ImagePool; struct ColorSpace; +struct ColorManagedDisplay; struct ListBase; +struct Material; struct Mesh; struct MTex; struct Object; @@ -65,7 +67,7 @@ typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); -struct PaintStroke *paint_stroke_new(struct bContext *C, +struct PaintStroke *paint_stroke_new(struct bContext *C, struct wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type); @@ -84,6 +86,7 @@ int paint_stroke_exec(struct bContext *C, struct wmOperator *op); void paint_stroke_cancel(struct bContext *C, struct wmOperator *op); struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); void *paint_stroke_mode_data(struct PaintStroke *stroke); +float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); int paint_poll(struct bContext *C); void paint_cursor_start(struct bContext *C, int (*poll)(struct bContext *C)); @@ -117,7 +120,7 @@ void PAINT_OT_weight_gradient(struct wmOperatorType *ot); void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_vertex_paint(struct wmOperatorType *ot); -unsigned int vpaint_get_current_col(struct VPaint *vp); +unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp); /* paint_vertex_proj.c */ @@ -144,32 +147,42 @@ typedef struct ImagePaintPartialRedraw { #define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) int image_texture_paint_poll(struct bContext *C); -void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask); -void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile); +void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate); +void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj); void image_undo_remove_masks(void); +void image_undo_init_locks(void); +void image_undo_end_locks(void); + void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); struct ImagePaintPartialRedraw *get_imapaintpartial(void); void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr); void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy); -void *paint_2d_new_stroke(struct bContext *, struct wmOperator *); +void *paint_2d_new_stroke(struct bContext *, struct wmOperator *, int mode); void paint_2d_redraw(const bContext *C, void *ps, bool final); void paint_2d_stroke_done(void *ps); -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser); +void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser, float pressure, float distance, float size); +void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps); +void paint_2d_gradient_fill (const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode); -void paint_proj_stroke(struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2]); -void paint_proj_redraw(const bContext *C, void *pps, bool final); +void paint_proj_stroke(const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2], float pressure, float distance, float size); +void paint_proj_redraw(const struct bContext *C, void *pps, bool final); void paint_proj_stroke_done(void *ps); +void paint_proj_mesh_data_ensure(bContext *C, struct Object *ob, struct wmOperator *op); +bool proj_paint_add_slot(bContext *C, int type, struct Material *ma); + +void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, float pressure, float color[3], struct ColorManagedDisplay *display); +bool paint_use_opacity_masking(struct Brush *brush); void paint_brush_init_tex(struct Brush *brush); void paint_brush_exit_tex(struct Brush *brush); void PAINT_OT_grab_clone(struct wmOperatorType *ot); void PAINT_OT_sample_color(struct wmOperatorType *ot); +void PAINT_OT_brush_colors_flip(struct wmOperatorType *ot); void PAINT_OT_texture_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_project_image(struct wmOperatorType *ot); void PAINT_OT_image_from_view(struct wmOperatorType *ot); - -/* new texture painting */ +void PAINT_OT_add_texture_paint_slot(struct wmOperatorType *ot); void PAINT_OT_image_paint(struct wmOperatorType *ot); /* uv sculpting */ @@ -202,7 +215,7 @@ float paint_calc_object_space_radius(struct ViewContext *vc, const float center[ float paint_get_tex_pixel(struct MTex *mtex, float u, float v, struct ImagePool *pool, int thread); void paint_get_tex_pixel_col(struct MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool, int thread, bool convert, struct ColorSpace *colorspace); -void paint_sample_color(const struct bContext *C, struct ARegion *ar, int x, int y); +void paint_sample_color(bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette); void BRUSH_OT_curve_preset(struct wmOperatorType *ot); void PAINT_OT_face_select_linked(struct wmOperatorType *ot); @@ -213,8 +226,10 @@ void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); + int vert_paint_poll(struct bContext *C); int mask_paint_poll(struct bContext *C); +int paint_curve_poll(struct bContext *C); int facemask_paint_poll(struct bContext *C); void flip_v3_v3(float out[3], const float in[3], const char symm); @@ -229,7 +244,6 @@ typedef enum BrushStrokeMode { /* paint_undo.c */ struct ListBase *undo_paint_push_get_list(int type); void undo_paint_push_count_alloc(int type, int size); -bool sculpt_undo_cleanup(struct bContext *C, struct ListBase *lb); /* paint_hide.c */ @@ -258,4 +272,29 @@ typedef enum { void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); void PAINT_OT_mask_lasso_gesture(struct wmOperatorType *ot); +/* paint_curve.c */ +void PAINTCURVE_OT_new(struct wmOperatorType *ot); +void PAINTCURVE_OT_add_point(struct wmOperatorType *ot); +void PAINTCURVE_OT_delete_point(struct wmOperatorType *ot); +void PAINTCURVE_OT_select(struct wmOperatorType *ot); +void PAINTCURVE_OT_slide(struct wmOperatorType *ot); +void PAINTCURVE_OT_draw(struct wmOperatorType *ot); +void PAINTCURVE_OT_cursor(struct wmOperatorType *ot); + +/* image painting blur kernel */ +typedef struct { + float *wdata; /* actual kernel */ + int side; /* kernel side */ + int side_squared; /* data side */ + int pixel_len; /* pixels around center that kernel is wide */ +} BlurKernel; + +enum BlurKernelType; +/* can be extended to other blur kernels later */ +BlurKernel *paint_new_blur_kernel(struct Brush *br); +void paint_delete_blur_kernel(BlurKernel *); + +/* paint curve defines */ +#define PAINT_CURVE_NUM_SEGMENTS 40 + #endif /* __PAINT_INTERN_H__ */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 9021581d47f..be783ac7ba7 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -149,11 +149,112 @@ static void BRUSH_OT_scale_size(wmOperatorType *ot) RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); } +/* Palette operators */ + +static int palette_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); + Palette *palette; + + palette = BKE_palette_add(bmain, "Palette"); + + BKE_paint_palette_set(paint, palette); + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Palette"; + ot->description = "Add new palette"; + ot->idname = "PALETTE_OT_new"; + + /* api callbacks */ + ot->exec = palette_new_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int palette_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + + if (paint && paint->palette != NULL) + return true; + + return false; +} + +static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = paint->brush; + PaintMode mode = BKE_paintmode_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *color = BKE_palette_color_add(palette); + + if (ELEM(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX)) { + copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); + color->value = 0.0; + } + else if (mode == PAINT_WEIGHT) { + zero_v3(color->rgb); + color->value = brush->weight; + } + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_color_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Palette Color"; + ot->description = "Add new color to active palette"; + ot->idname = "PALETTE_OT_color_add"; + + /* api callbacks */ + ot->exec = palette_color_add_exec; + ot->poll = palette_poll; + /* flags */ + 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); + Palette *palette = paint->palette; + + BKE_palette_color_delete(palette); + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_color_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Palette Color"; + ot->description = "Remove active color from palette"; + ot->idname = "PALETTE_OT_color_delete"; + + /* api callbacks */ + ot->exec = palette_color_delete_exec; + ot->poll = palette_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + + static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); - unsigned int paintcol = vpaint_get_current_col(scene->toolsettings->vpaint); + unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint); if (ED_vpaint_fill(obact, paintcol)) { ED_region_tag_redraw(CTX_wm_region(C)); // XXX - should redraw all 3D views @@ -332,6 +433,7 @@ static int brush_generic_tool_set(Main *bmain, Paint *paint, const int tool, if (brush) { BKE_paint_brush_set(paint, brush); BKE_paint_invalidate_overlay_all(); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); return OPERATOR_FINISHED; } @@ -928,8 +1030,37 @@ static void ed_keymap_stencil(wmKeyMap *keymap) /**************************** registration **********************************/ +void ED_operatormacros_paint(void) +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("PAINTCURVE_OT_add_point_slide", "Add Curve Point and Slide", + "Add new curve point and slide it", OPTYPE_UNDO); + ot->description = "Add new curve point and slide it"; + WM_operatortype_macro_define(ot, "PAINTCURVE_OT_add_point"); + otmacro = WM_operatortype_macro_define(ot, "PAINTCURVE_OT_slide"); + RNA_boolean_set(otmacro->ptr, "align", true); + RNA_boolean_set(otmacro->ptr, "select", false); +} + + void ED_operatortypes_paint(void) { + /* palette */ + WM_operatortype_append(PALETTE_OT_new); + WM_operatortype_append(PALETTE_OT_color_add); + WM_operatortype_append(PALETTE_OT_color_delete); + + /* paint curve */ + WM_operatortype_append(PAINTCURVE_OT_new); + WM_operatortype_append(PAINTCURVE_OT_add_point); + WM_operatortype_append(PAINTCURVE_OT_delete_point); + WM_operatortype_append(PAINTCURVE_OT_select); + WM_operatortype_append(PAINTCURVE_OT_slide); + WM_operatortype_append(PAINTCURVE_OT_draw); + WM_operatortype_append(PAINTCURVE_OT_cursor); + /* brush */ WM_operatortype_append(BRUSH_OT_add); WM_operatortype_append(BRUSH_OT_scale_size); @@ -950,6 +1081,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_grab_clone); WM_operatortype_append(PAINT_OT_project_image); WM_operatortype_append(PAINT_OT_image_from_view); + WM_operatortype_append(PAINT_OT_brush_colors_flip); + WM_operatortype_append(PAINT_OT_add_texture_paint_slot); /* weight */ WM_operatortype_append(PAINT_OT_weight_paint_toggle); @@ -1119,12 +1252,44 @@ static void paint_partial_visibility_keys(wmKeyMap *keymap) RNA_enum_set(kmi->ptr, "area", PARTIALVIS_ALL); } + +static void paint_keymap_curve(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_add_point_slide", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", true); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_slide", ACTIONMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_slide", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "align", true); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "toggle", true); + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_cursor", ACTIONMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_delete_point", XKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_draw", RETKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", EVT_TWEAK_S, KM_ANY, 0, 0); + RNA_boolean_set(kmi->ptr, "release_confirm", true); + WM_keymap_add_item(keymap, "TRANSFORM_OT_rotate", RKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "TRANSFORM_OT_resize", SKEY, KM_PRESS, 0, 0); +} + void ED_keymap_paint(wmKeyConfig *keyconf) { wmKeyMap *keymap; wmKeyMapItem *kmi; int i; + keymap = WM_keymap_find(keyconf, "Paint Curve", 0, 0); + keymap->poll = paint_curve_poll; + + paint_keymap_curve(keymap); + /* Sculpt mode */ keymap = WM_keymap_find(keyconf, "Sculpt", 0, 0); keymap->poll = sculpt_mode_poll; @@ -1191,7 +1356,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "create_missing", 1); /* */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.stroke_method"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", SKEY, KM_PRESS, KM_SHIFT, 0); @@ -1225,7 +1390,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.texture_angle_source_random"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method"); /* Weight Paint mode */ @@ -1250,7 +1415,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_stencil(keymap); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* face mask toggle */ @@ -1283,6 +1448,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_image_paint", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", BRUSH_STROKE_NORMAL); RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_image_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", BRUSH_STROKE_INVERT); + WM_keymap_add_item(keymap, "PAINT_OT_brush_colors_flip", XKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "PAINT_OT_grab_clone", RIGHTMOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "PAINT_OT_sample_color", SKEY, KM_PRESS, 0, 0); @@ -1301,7 +1467,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.texture_angle_source_random"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.stroke_method"); /* face-mask mode */ diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index dca6dfbcea4..47a772af9e4 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -36,16 +36,19 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_rand.h" +#include "BLI_listbase.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" +#include "DNA_curve_types.h" #include "RNA_access.h" #include "BKE_context.h" #include "BKE_paint.h" #include "BKE_brush.h" +#include "BKE_curve.h" #include "BKE_colortools.h" #include "BKE_image.h" @@ -72,7 +75,7 @@ typedef struct PaintSample { typedef struct PaintStroke { void *mode_data; - void *smooth_stroke_cursor; + void *stroke_cursor; wmTimer *timer; /* Cached values */ @@ -81,6 +84,9 @@ typedef struct PaintStroke { Brush *brush; UnifiedPaintSettings *ups; + /* used for lines and curves */ + ListBase line; + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs * to smooth the stroke */ PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; @@ -88,6 +94,8 @@ typedef struct PaintStroke { int cur_sample; float last_mouse_position[2]; + /* space distance covered so far */ + float stroke_distance; /* Set whether any stroke step has yet occurred * e.g. in sculpt mode, stroke doesn't start until cursor @@ -116,18 +124,17 @@ typedef struct PaintStroke { StrokeDone done; } PaintStroke; -/*** Cursor ***/ -static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) +/*** Cursors ***/ +static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); PaintStroke *stroke = customdata; - if (stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) { - glColor4ubv(paint->paint_cursor_col); + if (stroke && brush) { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); - + glColor4ubv(paint->paint_cursor_col); sdrawline(x, y, (int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1]); glDisable(GL_BLEND); @@ -135,6 +142,33 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata } } +static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + PaintStroke *stroke = customdata; + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glEnable(GL_LINE_STIPPLE); + glLineStipple(3, 0xAAAA); + + glColor4ub(0, 0, 0, paint->paint_cursor_col[3]); + glLineWidth(3.0); + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); + + glColor4ub(255, 255, 255, paint->paint_cursor_col[3]); + glLineWidth(1.0); + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); + + glDisable(GL_LINE_STIPPLE); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} + static bool paint_tool_require_location(Brush *brush, PaintMode mode) { switch (mode) { @@ -155,13 +189,18 @@ static bool paint_tool_require_location(Brush *brush, PaintMode mode) } /* Initialize the stroke cache variants from operator properties */ -static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, - struct PaintStroke *stroke, - const float mouse[2], float pressure) +static bool paint_brush_update(bContext *C, + Brush *brush, + PaintMode mode, + struct PaintStroke *stroke, + const float mouse_init[2], + float mouse[2], float pressure, + float location[3]) { Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - + UnifiedPaintSettings *ups = stroke->ups; + bool location_sampled = false; + bool location_success = false; /* XXX: Use pressure value from first brush step for brushes which don't * support strokes (grab, thumb). They depends on initial state and * brush coord/pressure/etc. @@ -222,14 +261,14 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, else { copy_v2_v2(ups->tex_mouse, mouse); } - } - /* take care of mask texture, if any */ - if (brush->mask_mtex.tex) { - if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - BKE_brush_randomize_texture_coordinates(ups, true); - else { - copy_v2_v2(ups->mask_tex_mouse, mouse); + /* take care of mask texture, if any */ + if (brush->mask_mtex.tex) { + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + BKE_brush_randomize_texture_coordinates(ups, true); + else { + copy_v2_v2(ups->mask_tex_mouse, mouse); + } } } @@ -246,14 +285,14 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, ups->brush_rotation = atan2(dx, dy) + M_PI; if (brush->flag & BRUSH_EDGE_TO_EDGE) { - float out[3]; - halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, out, halfway)) { + if (stroke->get_location(C, location, halfway)) { hit = true; + location_sampled = true; + location_success = true; } else if (!paint_tool_require_location(brush, mode)) { hit = true; @@ -266,17 +305,43 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, if (hit) { copy_v2_v2(ups->anchored_initial_mouse, halfway); copy_v2_v2(ups->tex_mouse, halfway); + copy_v2_v2(ups->mask_tex_mouse, halfway); + copy_v2_v2(mouse, halfway); ups->anchored_size /= 2.0f; ups->pixel_radius /= 2.0f; + stroke->stroke_distance = ups->pixel_radius; } - else + else { copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse); - + copy_v2_v2(mouse, stroke->initial_mouse); + stroke->stroke_distance = ups->pixel_radius; + } + ups->pixel_radius /= stroke->zoom_2d; ups->draw_anchored = true; } else if (brush->flag & BRUSH_RAKE) { - paint_calculate_rake_rotation(ups, mouse); + /* here we are using the initial mouse coordinate because we do not want the rake + * result to depend on jittering */ + if (!stroke->brush_init) + copy_v2_v2(ups->last_rake, mouse_init); + else + paint_calculate_rake_rotation(ups, mouse_init); } + + if (!location_sampled) { + if (stroke->get_location) { + if (stroke->get_location(C, location, mouse)) + location_success = true; + else if (!paint_tool_require_location(brush, mode)) + location_success = true; + } + else { + zero_v3(location); + location_success = true; + } + } + + return location_success; } @@ -284,12 +349,11 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float mouse_in[2], float pressure) { Scene *scene = CTX_data_scene(C); - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); Paint *paint = BKE_paint_get_active_from_context(C); PaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; float mouse_out[2]; PointerRNA itemptr; float location[3]; @@ -315,8 +379,6 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float copy_v2_v2(stroke->last_mouse_position, mouse_in); stroke->last_pressure = pressure; - paint_brush_update(C, brush, mode, stroke, mouse_in, pressure); - { float delta[2]; float factor = stroke->zoom_2d; @@ -336,22 +398,13 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float } } - /* TODO: can remove the if statement once all modes have this */ - if (stroke->get_location) { - if (!stroke->get_location(C, location, mouse_out)) { - if (paint_tool_require_location(brush, mode)) { - if (ar && (paint->flags & PAINT_SHOW_BRUSH)) - WM_paint_cursor_tag_redraw(window, ar); - return; - } - } + if (!paint_brush_update(C, brush, mode, stroke, mouse_in, mouse_out, pressure, location)) { + return; } - else - zero_v3(location); /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - + RNA_float_set(&itemptr, "size", ups->pixel_radius); RNA_float_set_array(&itemptr, "location", location); RNA_float_set_array(&itemptr, "mouse", mouse_out); RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); @@ -362,20 +415,12 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float /* don't record this for now, it takes up a lot of memory when doing long * strokes with small brush size, and operators have register disabled */ RNA_collection_clear(op->ptr, "stroke"); - - /* always redraw region if brush is shown */ - if (ar && (paint->flags & PAINT_SHOW_BRUSH)) - WM_paint_cursor_tag_redraw(window, ar); } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outpressure, const PaintSample *sample, PaintMode mode) { - output[0] = sample->mouse[0]; - output[1] = sample->mouse[1]; - *outpressure = sample->pressure; - if (paint_supports_smooth_stroke(stroke->brush, mode)) { float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d; float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u; @@ -391,6 +436,11 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outp output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u; *outpressure = sample->pressure * v + stroke->last_pressure * u; } + else { + output[0] = sample->mouse[0]; + output[1] = sample->mouse[1]; + *outpressure = sample->pressure; + } return 1; } @@ -413,6 +463,55 @@ static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, return max_ff(1.0, size_clamp * spacing / 50.0f); } + + +static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing) +{ + int i; + const int n = 100 / spacing; + const float h = spacing / 50.0f; + const float x0 = x - 1; + + float sum; + + sum = 0; + for (i = 0; i < n; i++) { + float xx; + + xx = fabs(x0 + i * h); + + if (xx < 1.0f) + sum += BKE_brush_curve_strength(br, xx, 1); + } + + return sum; +} + +static float paint_stroke_integrate_overlap(Brush *br, float factor) +{ + int i; + int m; + float g; + float max; + + float spacing = br->spacing * factor; + + if (!(br->flag & BRUSH_SPACE_ATTEN && (br->spacing < 100))) + return 1.0; + + m = 10; + g = 1.0f / m; + max = 0; + for (i = 0; i < m; i++) { + float overlap = paint_stroke_overlapped_curve(br, i * g, spacing); + + if (overlap > max) + max = overlap; + } + + return 1.0f / max; +} + static float paint_space_stroke_spacing_variable(const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) { if (BKE_brush_use_size_pressure(scene, stroke->brush)) { @@ -444,40 +543,42 @@ static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mou { const Scene *scene = CTX_data_scene(C); PaintStroke *stroke = op->customdata; - PaintMode mode = BKE_paintmode_get_active_from_context(C); + UnifiedPaintSettings *ups = stroke->ups; int cnt = 0; - if (paint_space_stroke_enabled(stroke->brush, mode)) { - float pressure, dpressure; - float mouse[2], dmouse[2]; - float length; + float pressure, dpressure; + float mouse[2], dmouse[2]; + float length; + float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); + sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; - length = normalize_v2(dmouse); + length = normalize_v2(dmouse); - while (length > 0.0f) { - float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length); - - if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; - pressure = stroke->last_pressure + (spacing / length) * dpressure; + while (length > 0.0f) { + float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length); - paint_brush_stroke_add_step(C, op, mouse, pressure); + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + pressure = stroke->last_pressure + (spacing / length) * dpressure; - length -= spacing; - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, spacing / no_pressure_spacing); - cnt++; - } - else { - break; - } + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, pressure); + + length -= spacing; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; + + cnt++; + } + else { + break; } } @@ -487,6 +588,7 @@ static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mou /**** Public API ****/ PaintStroke *paint_stroke_new(bContext *C, + wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, @@ -497,6 +599,7 @@ PaintStroke *paint_stroke_new(bContext *C, ToolSettings *toolsettings = CTX_data_tool_settings(C); UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; Brush *br = stroke->brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + float zoomx, zoomy; view3d_set_viewcontext(C, &stroke->vc); if (stroke->vc.v3d) @@ -510,6 +613,18 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->event_type = event_type; /* for modal, return event */ stroke->ups = ups; + get_imapaint_zoom(C, &zoomx, &zoomy); + stroke->zoom_2d = max_ff(zoomx, zoomy); + + if ((br->flag & BRUSH_CURVE) && + RNA_struct_property_is_set(op->ptr, "mode")) + { + RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); + } + /* initialize here */ + ups->overlap_factor = 1.0; + ups->stroke_active = true; + /* initialize here to avoid initialization conflict with threaded strokes */ curvemapping_initialize(br->curve); @@ -521,8 +636,7 @@ PaintStroke *paint_stroke_new(bContext *C, void paint_stroke_data_free(struct wmOperator *op) { BKE_paint_set_overlay_override(0); - MEM_freeN(op->customdata); - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static void stroke_done(struct bContext *C, struct wmOperator *op) @@ -552,8 +666,10 @@ static void stroke_done(struct bContext *C, struct wmOperator *op) stroke->timer); } - if (stroke->smooth_stroke_cursor) - WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor); + if (stroke->stroke_cursor) + WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); + + BLI_freelistN(&stroke->line); paint_stroke_data_free(op); } @@ -584,6 +700,16 @@ bool paint_supports_dynamic_size(Brush *br, PaintMode mode) if (sculpt_is_grab_tool(br)) return false; break; + + case PAINT_TEXTURE_2D: /* fall through */ + case PAINT_TEXTURE_PROJECTIVE: + if ((br->imagepaint_tool == PAINT_TOOL_FILL) && + (br->flag & BRUSH_USE_GRADIENT)) + { + return false; + } + break; + default: break; } @@ -593,8 +719,7 @@ bool paint_supports_dynamic_size(Brush *br, PaintMode mode) bool paint_supports_smooth_stroke(Brush *br, PaintMode mode) { if (!(br->flag & BRUSH_SMOOTH_STROKE) || - (br->flag & BRUSH_ANCHORED) || - (br->flag & BRUSH_DRAG_DOT)) + (br->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT | BRUSH_LINE))) { return false; } @@ -701,28 +826,141 @@ static void paint_stroke_sample_average(const PaintStroke *stroke, /*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/ } +/** + * Slightly different version of spacing for line/curve strokes, + * makes sure the dabs stay on the line path. + */ +static void paint_line_strokes_spacing( + bContext *C, wmOperator *op, PaintStroke *stroke, float spacing, float *length_residue, + const float old_pos[2], const float new_pos[2]) +{ + UnifiedPaintSettings *ups = stroke->ups; + + float mouse[2], dmouse[2]; + float length; + + sub_v2_v2v2(dmouse, new_pos, old_pos); + copy_v2_v2(stroke->last_mouse_position, old_pos); + + length = normalize_v2(dmouse); + + BLI_assert(length >= 0.0f); + + if (length == 0.0f) + return; + + while (length > 0.0f) { + float spacing_final = spacing - *length_residue; + length += *length_residue; + *length_residue = 0.0; + + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; + + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); + + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, 1.0); + + length -= spacing; + spacing_final = spacing; + } + else { + break; + } + } + + *length_residue = length; +} + + +static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, float mouse[2]) +{ + Brush *br = stroke->brush; + if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + + paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); + paint_space_stroke(C, op, mouse, 1.0); + } +} + +static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *stroke) +{ + Brush *br = stroke->brush; + if (br->flag & BRUSH_CURVE) { + const Scene *scene = CTX_data_scene(C); + const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + float length_residue = 0.0f; + int i; + + if (!pc) + return true; + + pcp = pc->points; + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + + for (i = 0; i < pc->tot_points - 1; i++, pcp++) { + int j; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + PaintCurvePoint *pcp_next = pcp + 1; + + for (j = 0; j < 2; j++) + BKE_curve_forward_diff_bezier( + pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + + + for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { + if (!stroke->stroke_started) { + stroke->last_pressure = 1.0; + copy_v2_v2(stroke->last_mouse_position, data + 2 * j); + stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); + + if (stroke->stroke_started) { + paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); + paint_line_strokes_spacing(C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + else { + paint_line_strokes_spacing(C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + } + + stroke_done(C, op); + return true; + } + + return false; +} + + int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { Paint *p = BKE_paint_get_active_from_context(C); PaintMode mode = BKE_paintmode_get_active_from_context(C); PaintStroke *stroke = op->customdata; + Brush *br = stroke->brush; PaintSample sample_average; float mouse[2]; bool first_dab = false; bool first_modal = false; - float zoomx, zoomy; bool redraw = false; float pressure; - /* see if tablet affects event */ - pressure = WM_event_tablet_data(event, &stroke->pen_flip, NULL); + /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */ + pressure = (br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f : WM_event_tablet_data(event, &stroke->pen_flip, NULL); paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); paint_stroke_sample_average(stroke, &sample_average); - get_imapaint_zoom(C, &zoomx, &zoomy); - stroke->zoom_2d = max_ff(zoomx, zoomy); - /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously! * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it * since the 2D deltas are zero -- code in this file needs to be updated to use the @@ -732,8 +970,12 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* one time initialization */ if (!stroke->stroke_init) { - stroke->smooth_stroke_cursor = - WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke); + if (paint_stroke_curve_end(C, op, stroke)) + return OPERATOR_FINISHED; + + if (paint_supports_smooth_stroke(br, mode)) + stroke->stroke_cursor = + WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_cursor, stroke); stroke->stroke_init = true; first_modal = true; @@ -747,9 +989,14 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ if (stroke->stroke_started) { - if (stroke->brush->flag & BRUSH_AIRBRUSH) + if (br->flag & BRUSH_AIRBRUSH) stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); + if (br->flag & BRUSH_LINE) { + stroke->stroke_cursor = + WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_line_cursor, stroke); + } + first_dab = true; } } @@ -765,20 +1012,42 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - if (event->type == stroke->event_type && event->val == KM_RELEASE && !first_modal) { + if (event->type == stroke->event_type && !first_modal) { + if (event->val == KM_RELEASE) { + paint_stroke_line_end (C, op, stroke, sample_average.mouse); + stroke_done(C, op); + return OPERATOR_FINISHED; + } + } + else if (ELEM(event->type, RETKEY, SPACEKEY)) { + paint_stroke_line_end(C, op, stroke, sample_average.mouse); stroke_done(C, op); return OPERATOR_FINISHED; } - else if (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) || - (event->type == TIMER && (event->customdata == stroke->timer)) ) + else if ((br->flag & BRUSH_LINE) && stroke->stroke_started && + (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) + { + if (br->flag & BRUSH_RAKE) { + copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); + paint_calculate_rake_rotation(stroke->ups, sample_average.mouse); + } + } + else if (first_modal || + /* regular dabs */ + (!(br->flag & (BRUSH_AIRBRUSH)) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + /* airbrush */ + ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) { if (stroke->stroke_started) { - if (paint_space_stroke_enabled(stroke->brush, mode)) { + if (paint_space_stroke_enabled(br, mode)) { if (paint_space_stroke(C, op, mouse, pressure)) redraw = true; } else { + float dmouse[2]; + sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); + stroke->stroke_distance += len_v2(dmouse); paint_brush_stroke_add_step(C, op, mouse, pressure); redraw = true; } @@ -789,19 +1058,27 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* we want the stroke to have the first daub at the start location * instead of waiting till we have moved the space distance */ if (first_dab && - paint_space_stroke_enabled(stroke->brush, mode) && - !(stroke->brush->flag & BRUSH_ANCHORED) && - !(stroke->brush->flag & BRUSH_SMOOTH_STROKE)) + paint_space_stroke_enabled(br, mode) && + !(br->flag & BRUSH_SMOOTH_STROKE)) { - paint_brush_stroke_add_step(C, op, mouse, pressure); + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); redraw = true; } /* do updates for redraw. if event is inbetween mousemove there are more * coming, so postpone potentially slow redraw updates until all are done */ - if (event->type != INBETWEEN_MOUSEMOVE) + if (event->type != INBETWEEN_MOUSEMOVE) { + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + + /* At the very least, invalidate the cursor */ + if (ar && (p->flags & PAINT_SHOW_BRUSH)) + WM_paint_cursor_tag_redraw(window, ar); + if (redraw && stroke->redraw) stroke->redraw(C, stroke, false); + } return OPERATOR_RUNNING_MODAL; } @@ -843,6 +1120,11 @@ void *paint_stroke_mode_data(struct PaintStroke *stroke) return stroke->mode_data; } +float paint_stroke_distance_get(struct PaintStroke *stroke) +{ + return stroke->stroke_distance; +} + void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) { stroke->mode_data = mode_data; @@ -856,6 +1138,6 @@ int paint_poll(bContext *C) ARegion *ar = CTX_wm_region(C); return p && ob && BKE_paint_brush(p) && - (sa && sa->spacetype == SPACE_VIEW3D) && + (sa && ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) && (ar && ar->regiontype == RGN_TYPE_WINDOW); } diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index c5c747dbab4..20e3155c01d 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -51,19 +51,17 @@ typedef struct UndoElem { UndoRestoreCb restore; UndoFreeCb free; + UndoCleanupCb cleanup; } UndoElem; -typedef bool (*UndoCleanupCb)(struct bContext *C, ListBase *lb); - typedef struct UndoStack { int type; ListBase elems; UndoElem *current; - UndoCleanupCb cleanup; } UndoStack; -static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL, NULL}; -static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL, sculpt_undo_cleanup}; +static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL}; +static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL}; /* Generic */ @@ -81,7 +79,7 @@ static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel) } } -static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free) +static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) { UndoElem *uel; int nr; @@ -101,6 +99,7 @@ static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestor stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file"); uel->restore = restore; uel->free = free; + uel->cleanup = cleanup; BLI_addtail(&stack->elems, uel); /* name can be a dynamic string */ @@ -179,25 +178,24 @@ static void undo_stack_cleanup(UndoStack *stack, bContext *C) UndoElem *uel = stack->elems.first; bool stack_reset = false; - if (stack->cleanup) { - while (uel) { - if (stack->cleanup(C, &uel->elems)) { - UndoElem *uel_tmp = uel->next; - if (stack->current == uel) { - stack->current = NULL; - stack_reset = true; - } - undo_elem_free(stack, uel); - BLI_freelinkN(&stack->elems, uel); - uel = uel_tmp; + while (uel) { + if (uel->cleanup && uel->cleanup(C, &uel->elems)) { + UndoElem *uel_tmp = uel->next; + if (stack->current == uel) { + stack->current = NULL; + stack_reset = true; } - else - uel = uel->next; - } - if (stack_reset) { - stack->current = stack->elems.last; + undo_elem_free(stack, uel); + BLI_freelinkN(&stack->elems, uel); + uel = uel_tmp; } + else + uel = uel->next; + } + if (stack_reset) { + stack->current = stack->elems.last; } + } static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name) @@ -255,23 +253,25 @@ static void undo_stack_free(UndoStack *stack) /* Exported Functions */ -void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free) +void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) { if (type == UNDO_PAINT_IMAGE) - undo_stack_push_begin(&ImageUndoStack, name, restore, free); + undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup); else if (type == UNDO_PAINT_MESH) - undo_stack_push_begin(&MeshUndoStack, name, restore, free); + undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup); } ListBase *undo_paint_push_get_list(int type) { if (type == UNDO_PAINT_IMAGE) { - if (ImageUndoStack.current) + if (ImageUndoStack.current) { return &ImageUndoStack.current->elems; + } } else if (type == UNDO_PAINT_MESH) { - if (MeshUndoStack.current) + if (MeshUndoStack.current) { return &MeshUndoStack.current->elems; + } } return NULL; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 25308f6595e..eaa4885572e 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -35,23 +35,28 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_material_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "BLI_math.h" +#include "BLI_math_color.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLF_translation.h" +#include "BKE_scene.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" +#include "BKE_material.h" #include "BKE_image.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_image.h" #include "RNA_access.h" #include "RNA_define.h" @@ -67,6 +72,10 @@ #include "ED_view3d.h" #include "ED_screen.h" +#include "ED_uvedit.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" #include "BLI_sys_types.h" #include "ED_mesh.h" /* for face mask functions */ @@ -205,6 +214,175 @@ void paint_get_tex_pixel_col(MTex *mtex, float u, float v, float rgba[4], struct CLAMP(rgba[3], 0.0f, 1.0f); } +/* 3D Paint */ + +static void imapaint_project(float matrix[4][4], const float co[3], float pco[4]) +{ + copy_v3_v3(pco, co); + pco[3] = 1.0f; + + mul_m4_v4(matrix, pco); +} + +static void imapaint_tri_weights(float matrix[4][4], GLint view[4], + const float v1[3], const float v2[3], const float v3[3], + const float co[2], float w[3]) +{ + float pv1[4], pv2[4], pv3[4], h[3], divw; + float wmat[3][3], invwmat[3][3]; + + /* compute barycentric coordinates */ + + /* project the verts */ + imapaint_project(matrix, v1, pv1); + imapaint_project(matrix, v2, pv2); + imapaint_project(matrix, v3, pv3); + + /* do inverse view mapping, see gluProject man page */ + h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f; + h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f; + h[2] = 1.0f; + + /* solve for (w1,w2,w3)/perspdiv in: + * h * perspdiv = Project * Model * (w1 * v1 + w2 * v2 + w3 * v3) */ + + wmat[0][0] = pv1[0]; wmat[1][0] = pv2[0]; wmat[2][0] = pv3[0]; + wmat[0][1] = pv1[1]; wmat[1][1] = pv2[1]; wmat[2][1] = pv3[1]; + wmat[0][2] = pv1[3]; wmat[1][2] = pv2[3]; wmat[2][2] = pv3[3]; + + invert_m3_m3(invwmat, wmat); + mul_m3_v3(invwmat, h); + + copy_v3_v3(w, h); + + /* w is still divided by perspdiv, make it sum to one */ + divw = w[0] + w[1] + w[2]; + if (divw != 0.0f) { + mul_v3_fl(w, 1.0f / divw); + } +} + +/* compute uv coordinates of mouse in face */ +static void imapaint_pick_uv(Scene *scene, Object *ob, unsigned int faceindex, const int xy[2], float uv[2]) +{ + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + MTFace *tf_base, *tf; + Material *ma; + TexPaintSlot *slot; + int numfaces = dm->getNumTessFaces(dm), a, findex; + float p[2], w[3], absw, minabsw; + MFace mf; + MVert mv[4]; + float matrix[4][4], proj[4][4]; + GLint view[4]; + + /* compute barycentric coordinates */ + + /* double lookup */ + const int *index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + if (index_mf_to_mpoly == NULL) { + index_mp_to_orig = NULL; + } + + /* get the needed opengl matrices */ + glGetIntegerv(GL_VIEWPORT, view); + glGetFloatv(GL_MODELVIEW_MATRIX, (float *)matrix); + glGetFloatv(GL_PROJECTION_MATRIX, (float *)proj); + view[0] = view[1] = 0; + mul_m4_m4m4(matrix, matrix, ob->obmat); + mul_m4_m4m4(matrix, proj, matrix); + + minabsw = 1e10; + uv[0] = uv[1] = 0.0; + + /* test all faces in the derivedmesh with the original index of the picked face */ + for (a = 0; a < numfaces; a++) { + findex = index_mf_to_mpoly ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a; + + if (findex == faceindex) { + dm->getTessFace(dm, a, &mf); + + ma = dm->mat[mf.mat_nr]; + slot = &ma->texpaintslot[ma->paint_active_slot]; + + dm->getVert(dm, mf.v1, &mv[0]); + dm->getVert(dm, mf.v2, &mv[1]); + dm->getVert(dm, mf.v3, &mv[2]); + if (mf.v4) + dm->getVert(dm, mf.v4, &mv[3]); + + if (!slot->uvname[0] || !(tf_base = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, slot->uvname))) + tf_base = CustomData_get_layer(&dm->faceData, CD_MTFACE); + + tf = &tf_base[a]; + + p[0] = xy[0]; + p[1] = xy[1]; + + if (mf.v4) { + /* the triangle with the largest absolute values is the one + * with the most negative weights */ + imapaint_tri_weights(matrix, view, mv[0].co, mv[1].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[0][0] * w[0] + tf->uv[1][0] * w[1] + tf->uv[3][0] * w[2]; + uv[1] = tf->uv[0][1] * w[0] + tf->uv[1][1] * w[1] + tf->uv[3][1] * w[2]; + minabsw = absw; + } + + imapaint_tri_weights(matrix, view, mv[1].co, mv[2].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[1][0] * w[0] + tf->uv[2][0] * w[1] + tf->uv[3][0] * w[2]; + uv[1] = tf->uv[1][1] * w[0] + tf->uv[2][1] * w[1] + tf->uv[3][1] * w[2]; + minabsw = absw; + } + } + else { + imapaint_tri_weights(matrix, view, mv[0].co, mv[1].co, mv[2].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[0][0] * w[0] + tf->uv[1][0] * w[1] + tf->uv[2][0] * w[2]; + uv[1] = tf->uv[0][1] * w[0] + tf->uv[1][1] * w[1] + tf->uv[2][1] * w[2]; + minabsw = absw; + } + } + } + } + + dm->release(dm); +} + +/* returns 0 if not found, otherwise 1 */ +static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int *r_index, unsigned int totface) +{ + if (totface == 0) + return 0; + + /* sample only on the exact position */ + *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); + + if ((*r_index) == 0 || (*r_index) > (unsigned int)totface) { + return 0; + } + + (*r_index)--; + + return 1; +} + + +static Image *imapaint_face_image(DerivedMesh *dm, int face_index) +{ + Image *ima; + MFace *mf = dm->getTessFaceArray(dm) + face_index; + Material *ma = dm->mat[mf->mat_nr]; + ima = ma->texpaintslot[ma->paint_active_slot].ima; + + return ima; +} + /* Uses symm to selectively flip any axis of a coordinate. */ void flip_v3_v3(float out[3], const float in[3], const char symm) { @@ -223,25 +401,123 @@ void flip_v3_v3(float out[3], const float in[3], const char symm) } /* used for both 3d view and image window */ -void paint_sample_color(const bContext *C, ARegion *ar, int x, int y) /* frontbuf */ +void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) { + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = BKE_paint_palette(paint); + PaletteColor *color; Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); unsigned int col; - const char *cp; + const unsigned char *cp; CLAMP(x, 0, ar->winx); CLAMP(y, 0, ar->winy); - glReadBuffer(GL_FRONT); - glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); - glReadBuffer(GL_BACK); + if (use_palette) { + if (!palette) { + palette = BKE_palette_add(CTX_data_main(C), "Palette"); + BKE_paint_palette_set(paint, palette); + } + + color = BKE_palette_color_add(palette); + } + + + if (CTX_wm_view3d(C) && texpaint_proj) { + /* first try getting a colour directly from the mesh faces if possible */ + Object *ob = OBACT; + bool sample_success = false; + + if (ob) { + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + + ViewContext vc; + const int mval[2] = {x, y}; + unsigned int faceindex; + unsigned int totface = dm->getNumTessFaces(dm); + MTFace *dm_mtface = dm->getTessFaceDataArray(dm, CD_MTFACE); + + DM_update_materials(dm, ob); + + if (dm_mtface) { + view3d_set_viewcontext(C, &vc); + + view3d_operator_needs_opengl(C); + + if (imapaint_pick_face(&vc, mval, &faceindex, totface)) { + Image *image = imapaint_face_image(dm, faceindex); + + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (ibuf && ibuf->rect) { + float uv[2]; + float u, v; + imapaint_pick_uv(scene, ob, faceindex, mval, uv); + sample_success = true; + + u = fmodf(uv[0], 1.0f); + v = fmodf(uv[1], 1.0f); + + if (u < 0.0f) u += 1.0f; + if (v < 0.0f) v += 1.0f; + + u = u * ibuf->x - 0.5f; + v = v * ibuf->y - 0.5f; + + if (ibuf->rect_float) { + float rgba_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + straight_to_premul_v4(rgba_f); + if (use_palette) { + linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); + } + else { + linearrgb_to_srgb_v3_v3(rgba_f, rgba_f); + BKE_brush_color_set(scene, br, rgba_f); + } + } + else { + unsigned char rgba[4]; + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (use_palette) { + rgb_uchar_to_float(color->rgb, rgba); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, rgba); + BKE_brush_color_set(scene, br, rgba_f); + } + } + } + + BKE_image_release_ibuf(image, ibuf, NULL); + } + } + dm->release(dm); + } - cp = (char *)&col; + if (!sample_success) { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + else + return; + } + else { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + cp = (unsigned char *)&col; - if (br) { - br->rgb[0] = cp[0] / 255.0f; - br->rgb[1] = cp[1] / 255.0f; - br->rgb[2] = cp[2] / 255.0f; + if (use_palette) { + rgb_uchar_to_float(color->rgb, cp); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, cp); + BKE_brush_color_set(scene, br, rgba_f); } } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 969c5a09a82..2929a96db29 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -197,11 +197,11 @@ static int *get_indexarray(Mesh *me) return MEM_mallocN(sizeof(int) * (me->totpoly + 1), "vertexpaint"); } -unsigned int vpaint_get_current_col(VPaint *vp) +unsigned int vpaint_get_current_col(Scene *scene, VPaint *vp) { Brush *brush = BKE_paint_brush(&vp->paint); unsigned char col[4]; - rgb_float_to_uchar(col, brush->rgb); + rgb_float_to_uchar(col, BKE_brush_color_get(scene, brush)); col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ return *(unsigned int *)col; } @@ -2547,14 +2547,17 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start, wpaint_stroke_update_step, NULL, wpaint_stroke_done, event->type); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -2563,7 +2566,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int wpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start, wpaint_stroke_update_step, NULL, wpaint_stroke_done, 0); @@ -2778,7 +2781,8 @@ static void vpaint_build_poly_facemap(struct VPaintData *vd, Mesh *me) static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float UNUSED(mouse[2])) { - ToolSettings *ts = CTX_data_tool_settings(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; struct PaintStroke *stroke = op->customdata; VPaint *vp = ts->vpaint; Brush *brush = BKE_paint_brush(&vp->paint); @@ -2810,7 +2814,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f vpd->vp_handle = ED_vpaint_proj_handle_create(vpd->vc.scene, ob, &vpd->vertexcosnos); vpd->indexar = get_indexarray(me); - vpd->paintcol = vpaint_get_current_col(vp); + vpd->paintcol = vpaint_get_current_col(scene, vp); vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) && brush->mtex.tex; @@ -3062,14 +3066,18 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start, vpaint_stroke_update_step, NULL, vpaint_stroke_done, event->type); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -3078,7 +3086,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int vpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start, vpaint_stroke_update_step, NULL, vpaint_stroke_done, 0); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 4f9a8182ae9..aa43b7dcde4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -663,47 +663,6 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca /* ===== Sculpting ===== * */ - -static float overlapped_curve(Brush *br, float x) -{ - int i; - const int n = 100 / br->spacing; - const float h = br->spacing / 50.0f; - const float x0 = x - 1; - - float sum; - - sum = 0; - for (i = 0; i < n; i++) { - float xx; - - xx = fabsf(x0 + i * h); - - if (xx < 1.0f) - sum += BKE_brush_curve_strength(br, xx, 1); - } - - return sum; -} - -static float integrate_overlap(Brush *br) -{ - int i; - int m = 10; - float g = 1.0f / m; - float max; - - max = 0; - for (i = 0; i < m; i++) { - float overlap = overlapped_curve(br, i * g); - - if (overlap > max) - max = overlap; - } - - return max; -} - static void flip_v3(float v[3], const char symm) { flip_v3_v3(v, v, symm); @@ -776,7 +735,7 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) /* Return modified brush strength. Includes the direction of the brush, positive * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) +static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, UnifiedPaintSettings *ups) { const Scene *scene = cache->vc->scene; Brush *brush = BKE_paint_brush(&sd->paint); @@ -788,13 +747,10 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; float pen_flip = cache->pen_flip ? -1 : 1; float invert = cache->invert ? -1 : 1; - float accum = integrate_overlap(brush); + float overlap = ups->overlap_factor; /* spacing is integer percentage of radius, divide by 50 to get * normalized diameter */ - float overlap = (brush->flag & BRUSH_SPACE_ATTEN && - brush->flag & BRUSH_SPACE && - !(brush->flag & BRUSH_ANCHORED) && - (brush->spacing < 100)) ? 1.0f / accum : 1; + float flip = dir * invert * pen_flip; switch (brush->sculpt_tool) { @@ -3377,7 +3333,7 @@ static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, /* XXX This reduces the length of the grab delta if it approaches the line of symmetry * XXX However, a different approach appears to be needed */ #if 0 - if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + if (sd->paint.symmetry_flags & SCULPT_SYMMETRY_FEATHER) { float frac = 1.0f / max_overlap_count(sd); float reduce = (feather - frac) / (1 - frac); @@ -3437,7 +3393,7 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) } static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, - BrushActionFunc action) + BrushActionFunc action, UnifiedPaintSettings *ups) { Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; @@ -3447,7 +3403,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, float feather = calc_symmetry_feather(sd, ss->cache); - cache->bstrength = brush_strength(sd, cache, feather); + cache->bstrength = brush_strength(sd, cache, feather, ups); cache->symmetry = symm; /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ @@ -3733,8 +3689,8 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio /* not very nice, but with current events system implementation * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) brush->flag |= BRUSH_INVERTED; - else brush->flag &= ~BRUSH_INVERTED; + if (cache->invert) ups->draw_inverted = true; + else ups->draw_inverted = false; /* Alt-Smooth */ if (cache->alt_smooth) { @@ -3992,16 +3948,9 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, cache->radius_squared = cache->radius * cache->radius; if (brush->flag & BRUSH_ANCHORED) { + /* true location has been calculated as part of the stroke system already here */ if (brush->flag & BRUSH_EDGE_TO_EDGE) { - float halfway[2]; - float out[3]; - halfway[0] = 0.5f * (cache->mouse[0] + cache->initial_mouse[0]); - halfway[1] = 0.5f * (cache->mouse[1] + cache->initial_mouse[1]); - - if (sculpt_stroke_get_location(C, out, halfway)) { - copy_v3_v3(cache->anchored_location, out); - copy_v3_v3(cache->true_location, cache->anchored_location); - } + RNA_float_get_array(ptr, "location", cache->true_location); } cache->radius = paint_calc_object_space_radius(cache->vc, @@ -4393,10 +4342,10 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st } if (sculpt_stroke_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update); + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); } - do_symmetrical_brush_actions(sd, ob, do_brush_action); + do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); sculpt_combine_proxies(sd, ob); @@ -4446,8 +4395,9 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str /* Finished */ if (ss->cache) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; Brush *brush = BKE_paint_brush(&sd->paint); - brush->flag &= ~BRUSH_INVERTED; + ups->draw_inverted = false; sculpt_stroke_modifiers_check(C, ob); @@ -4506,7 +4456,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent if (!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - stroke = paint_stroke_new(C, sculpt_stroke_get_location, + stroke = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, NULL, sculpt_stroke_done, event->type); @@ -4521,10 +4471,13 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_PASS_THROUGH; } + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -4536,7 +4489,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) if (!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, + op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, NULL, sculpt_stroke_done, 0); /* frees op->customdata */ @@ -5062,11 +5015,11 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->paint.flags |= PAINT_SHOW_BRUSH; /* Make sure at least dyntopo subdivision is enabled */ - ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE; + ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; } if (!ts->sculpt->detail_size) { - ts->sculpt->detail_size = 30; + ts->sculpt->detail_size = 12; } if (ts->sculpt->constant_detail == 0.0f) diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 0d49049c78e..614c1f3ef1d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -543,7 +543,7 @@ static void sculpt_undo_free(ListBase *lb) } } -bool sculpt_undo_cleanup(bContext *C, ListBase *lb) +static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) { Object *ob = CTX_data_active_object(C); SculptUndoNode *unode; @@ -551,10 +551,8 @@ bool sculpt_undo_cleanup(bContext *C, ListBase *lb) unode = lb->first; if (unode && strcmp(unode->idname, ob->id.name) != 0) { - for (unode = lb->first; unode; unode = unode->next) { - if (unode->bm_entry) - BM_log_cleanup_entry(unode->bm_entry); - } + if (unode->bm_entry) + BM_log_cleanup_entry(unode->bm_entry); return true; } @@ -881,7 +879,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, void sculpt_undo_push_begin(const char *name) { ED_undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free); + sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup); } void sculpt_undo_push_end(void) diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 7a74a58c69d..b171e7a5f88 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -144,6 +144,7 @@ void ED_spacetypes_init(void) ED_operatormacros_curve(); ED_operatormacros_mask(); ED_operatormacros_sequencer(); + ED_operatormacros_paint(); /* register dropboxes (can use macros) */ spacetypes = BKE_spacetypes_list(); diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index a2f7d9e7d6c..24b1c54dd9f 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -28,6 +28,7 @@ * \ingroup spimage */ +#include "DNA_brush_types.h" #include "DNA_mask_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -297,44 +298,51 @@ bool ED_space_image_show_render(SpaceImage *sima) bool ED_space_image_show_paint(SpaceImage *sima) { if (ED_space_image_show_render(sima)) - return 0; + return false; return (sima->mode == SI_MODE_PAINT); } +bool ED_space_image_show_texpaint(SpaceImage *sima, Object *ob) +{ + return (ob && ob->type == OB_MESH && + ob->mode == OB_MODE_TEXTURE_PAINT && + !(sima->flag & SI_NO_DRAW_TEXPAINT)); +} + bool ED_space_image_show_uvedit(SpaceImage *sima, Object *obedit) { if (sima && (ED_space_image_show_render(sima) || ED_space_image_show_paint(sima))) - return 0; + return false; if (obedit && obedit->type == OB_MESH) { struct BMEditMesh *em = BKE_editmesh_from_object(obedit); - int ret; + bool ret; ret = EDBM_mtexpoly_check(em); return ret; } - return 0; + return false; } bool ED_space_image_show_uvshadow(SpaceImage *sima, Object *obedit) { if (ED_space_image_show_render(sima)) - return 0; + return false; if (ED_space_image_show_paint(sima)) if (obedit && obedit->type == OB_MESH) { struct BMEditMesh *em = BKE_editmesh_from_object(obedit); - int ret; + bool ret; ret = EDBM_mtexpoly_check(em); - return ret; + return ret && !(sima->flag & SI_NO_DRAW_TEXPAINT); } - return 0; + return false; } /* matches clip function */ @@ -361,6 +369,21 @@ int ED_space_image_maskedit_poll(bContext *C) return false; } +bool ED_space_image_paint_curve(const bContext *C) +{ + SpaceImage *sima = CTX_wm_space_image(C); + + if (sima && sima->mode == SI_MODE_PAINT) { + Brush *br = CTX_data_tool_settings(C)->imapaint.paint.brush; + + if (br && (br->flag & BRUSH_CURVE)) + return true; + } + + return false; +} + + int ED_space_image_maskedit_mask_poll(bContext *C) { if (ED_space_image_maskedit_poll(C)) { diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 69c141c2bbb..6a72066f031 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1896,6 +1896,7 @@ static int image_new_exec(bContext *C, wmOperator *op) SpaceImage *sima; Scene *scene; Object *obedit; + Object *ob; Image *ima; Main *bmain; PointerRNA ptr, idptr; @@ -1910,6 +1911,7 @@ static int image_new_exec(bContext *C, wmOperator *op) scene = CTX_data_scene(C); obedit = CTX_data_edit_object(C); bmain = CTX_data_main(C); + ob = OBACT; prop = RNA_struct_find_property(op->ptr, "name"); RNA_property_string_get(op->ptr, prop, name); @@ -1955,6 +1957,13 @@ static int image_new_exec(bContext *C, wmOperator *op) tex->ima = ima; ED_area_tag_redraw(CTX_wm_area(C)); } + else if (ob && ob->mode == OB_MODE_TEXTURE_PAINT) { + ImagePaintSettings *imapaint = &(CTX_data_tool_settings(C)->imapaint); + + if (imapaint->stencil) + id_us_min(&imapaint->stencil->id); + imapaint->stencil = ima; + } } BKE_image_signal(ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_USER_NEW_IMAGE); @@ -2037,7 +2046,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) if (support_undo) { ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); + ED_image_undo_restore, ED_image_undo_free, NULL); /* not strictly needed, because we only imapaint_dirty_region to invalidate all tiles * but better do this right in case someone copies this for a tool that uses partial redraw better */ ED_imapaint_clear_partial_redraw(); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 03517f5e4d9..375a0ddeac3 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -633,6 +633,12 @@ static void image_main_area_init(wmWindowManager *wm, ARegion *ar) WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); /* image paint polls for mode */ + keymap = WM_keymap_find(wm->defaultconf, "Curve", 0, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + keymap = WM_keymap_find(wm->defaultconf, "Paint Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Image Paint", 0, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); @@ -657,6 +663,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) Object *obact = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Mask *mask = NULL; + bool curve = false; Scene *scene = CTX_data_scene(C); View2D *v2d = &ar->v2d; //View2DScrollers *scrollers; @@ -702,6 +709,9 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) else if (sima->mode == SI_MODE_MASK) { mask = ED_space_image_get_mask(sima); } + else if (ED_space_image_paint_curve(C)) { + curve = true; + } ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW); @@ -753,6 +763,11 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) draw_image_cursor(ar, sima->cursor); UI_view2d_view_restore(C); } + else if (curve) { + UI_view2d_view_ortho(v2d); + draw_image_cursor(ar, sima->cursor); + UI_view2d_view_restore(C); + } draw_image_cache(C, ar); diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index bf0b7850839..348e6e526fe 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -212,12 +212,16 @@ static Material *give_current_material_or_def(Object *ob, int matnr) static struct TextureDrawState { Object *ob; + Image *stencil; + bool stencil_invert; bool use_game_mat; int is_lit, is_tex; int color_profile; bool use_backface_culling; unsigned char obcol[4]; -} Gtexdraw = {NULL, false, 0, 0, 0, false, {0, 0, 0, 0}}; + float stencil_col[4]; + bool is_texpaint; +} Gtexdraw = {NULL, NULL, false, false, 0, 0, 0, false, {0, 0, 0, 0}, {0.0f, 0.0f, 0.0f, 1.0f}, false}; static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material *ma, struct TextureDrawState gtexdraw) { @@ -229,13 +233,15 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * static int c_lit; static int c_has_texface; - Object *litob = NULL; /* to get mode to turn off mipmap in painting mode */ int backculled = 1; int alphablend = GPU_BLEND_SOLID; int textured = 0; int lit = 0; int has_texface = texface != NULL; bool need_set_tpage = false; + bool texpaint = ((gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) != 0); + + Image *ima = NULL; if (ma != NULL) { if (ma->mode & MA_TRANSP) { @@ -248,10 +254,10 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * memset(&c_texface, 0, sizeof(MTFace)); c_badtex = false; c_has_texface = -1; + c_ma = NULL; } else { textured = gtexdraw.is_tex; - litob = gtexdraw.ob; } /* convert number of lights into boolean */ @@ -266,14 +272,16 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * } } - if (texface) { + if (texface && !texpaint) { textured = textured && (texface->tpage); /* no material, render alpha if texture has depth=32 */ if (!ma && BKE_image_has_alpha(texface->tpage)) alphablend = GPU_BLEND_ALPHA; } - + else if (texpaint && ma) { + ima = ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + } else textured = 0; @@ -287,11 +295,25 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * /* need to re-set tpage if textured flag changed or existsment of texface changed.. */ need_set_tpage = textured != c_textured || has_texface != c_has_texface; /* ..or if settings inside texface were changed (if texface was used) */ - need_set_tpage |= texface && memcmp(&c_texface, texface, sizeof(c_texface)); + need_set_tpage |= (texpaint && c_ma != ma) || (texface && memcmp(&c_texface, texface, sizeof(c_texface))); if (need_set_tpage) { if (textured) { - c_badtex = !GPU_set_tpage(texface, !(litob->mode & OB_MODE_TEXTURE_PAINT), alphablend); + if (texpaint) { + c_badtex = false; + if (GPU_verify_image(ima, NULL, 0, 1, 0, false)) { + glEnable(GL_TEXTURE_2D); + } + else { + c_badtex = true; + GPU_clear_tpage(true); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + } + } + else { + c_badtex = !GPU_set_tpage(texface, !texpaint, alphablend); + } } else { GPU_set_tpage(NULL, 0, 0); @@ -325,6 +347,7 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * glDisable(GL_COLOR_MATERIAL); } c_lit = lit; + c_ma = ma; } return c_badtex; @@ -335,6 +358,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O unsigned char obcol[4]; bool is_tex, solidtex; Mesh *me = ob->data; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; /* XXX scene->obedit warning */ @@ -364,8 +388,34 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O else is_tex = false; Gtexdraw.ob = ob; + Gtexdraw.stencil = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? imapaint->stencil : NULL; + Gtexdraw.stencil_invert = ((imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0); + Gtexdraw.is_texpaint = (ob->mode == OB_MODE_TEXTURE_PAINT); + copy_v3_v3(Gtexdraw.stencil_col, imapaint->stencil_col); Gtexdraw.is_tex = is_tex; + /* load the stencil texture here */ + if (Gtexdraw.is_texpaint && (Gtexdraw.stencil != NULL)) { + glActiveTexture(GL_TEXTURE1); + if (GPU_verify_image(Gtexdraw.stencil, NULL, false, false, false, false)) { + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, Gtexdraw.stencil_col); + if (!Gtexdraw.stencil_invert) { + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_COLOR); + } + else { + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + } + } + glActiveTexture(GL_TEXTURE0); + } + Gtexdraw.color_profile = BKE_scene_check_color_management_enabled(scene); Gtexdraw.use_game_mat = (RE_engines_find(scene->r.engine)->flag & RE_GAME) != 0; Gtexdraw.use_backface_culling = (v3d->flag2 & V3D_BACKFACE_CULLING) != 0; @@ -379,8 +429,24 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O static void draw_textured_end(void) { - /* switch off textures */ - GPU_set_tpage(NULL, 0, 0); + if (Gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) { + if (Gtexdraw.stencil != NULL) { + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + } + /* manual reset, since we don't use tpage */ + glBindTexture(GL_TEXTURE_2D, 0); + /* force switch off textures */ + GPU_clear_tpage(true); + } + else { + /* switch off textures */ + GPU_set_tpage(NULL, 0, 0); + } glShadeModel(GL_FLAT); glDisable(GL_CULL_FACE); @@ -456,7 +522,7 @@ static DMDrawOption draw_tface__set_draw(MTFace *tface, const bool UNUSED(has_mc if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return 0; - if (tface) + if (tface || Gtexdraw.is_texpaint) set_draw_settings_cached(0, tface, ma, Gtexdraw); /* always use color from mcol, as set in update_tface_color_layer */ @@ -770,7 +836,8 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d Object *ob, DerivedMesh *dm, const int draw_flags) { Mesh *me = ob->data; - + DMDrawFlag uvflag = DM_DRAW_USE_ACTIVE_UV; + /* correct for negative scale */ if (ob->transflag & OB_NEG_SCALE) glFrontFace(GL_CW); else glFrontFace(GL_CCW); @@ -780,6 +847,10 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + uvflag = DM_DRAW_USE_TEXPAINT_UV; + } + if (ob->mode & OB_MODE_EDIT) { drawEMTFMapped_userData data; @@ -789,7 +860,7 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d data.mf = DM_get_tessface_data_layer(dm, CD_MFACE); data.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); - dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, compareDrawOptionsEm, &data); + dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, compareDrawOptionsEm, &data, 0); } else if (draw_flags & DRAW_FACE_SELECT) { if (ob->mode & OB_MODE_WEIGHT_PAINT) @@ -801,15 +872,15 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d userData.mf = DM_get_tessface_data_layer(dm, CD_MFACE); userData.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); userData.me = me; - dm->drawMappedFacesTex(dm, me->mpoly ? draw_tface_mapped__set_draw : NULL, compareDrawOptions, &userData); + dm->drawMappedFacesTex(dm, me->mpoly ? draw_tface_mapped__set_draw : NULL, compareDrawOptions, &userData, uvflag); } } else { if (GPU_buffer_legacy(dm)) { if (draw_flags & DRAW_MODIFIERS_PREVIEW) - dm->drawFacesTex(dm, draw_mcol__set_draw_legacy, NULL, NULL); + dm->drawFacesTex(dm, draw_mcol__set_draw_legacy, NULL, NULL, uvflag); else - dm->drawFacesTex(dm, draw_tface__set_draw_legacy, NULL, NULL); + dm->drawFacesTex(dm, draw_tface__set_draw_legacy, NULL, NULL, uvflag); } else { drawTFace_userData userData; @@ -820,7 +891,7 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d userData.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); userData.me = NULL; - dm->drawFacesTex(dm, draw_tface__set_draw, compareDrawOptions, &userData); + dm->drawFacesTex(dm, draw_tface__set_draw, compareDrawOptions, &userData, uvflag); } } @@ -955,7 +1026,8 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, /* if not cycles, or preview-modifiers, or drawing matcaps */ if ((draw_flags & DRAW_MODIFIERS_PREVIEW) || (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) || - (BKE_scene_use_new_shading_nodes(scene) == false)) + (BKE_scene_use_new_shading_nodes(scene) == false) || + ((ob->mode & OB_MODE_TEXTURE_PAINT) && ELEM(v3d->drawtype, OB_TEXTURE, OB_SOLID))) { draw_mesh_textured_old(scene, v3d, rv3d, ob, dm, draw_flags); return; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index e7e92073244..82fef4a85e8 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -301,7 +301,7 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt) if (BKE_scene_use_new_shading_nodes(scene)) return false; - return (scene->gm.matmode == GAME_MAT_GLSL) && (dt > OB_SOLID); + return ((scene->gm.matmode == GAME_MAT_GLSL) || (v3d->drawtype == OB_MATERIAL)) && (dt > OB_SOLID); } static bool check_alpha_pass(Base *base) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index ce82a96bcf6..eb2310fa791 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -483,6 +483,12 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar) keymap = WM_keymap_find(wm->defaultconf, "Object Mode", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Paint Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + + keymap = WM_keymap_find(wm->defaultconf, "Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Image Paint", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); @@ -673,7 +679,7 @@ static void view3d_dropboxes(void) WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_mesh_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy); - WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy); + WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy); } @@ -860,14 +866,18 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN switch (wmn->data) { case ND_SHADING: case ND_NODES: + { + Object *ob = OBACT; if ((v3d->drawtype == OB_MATERIAL) || + (ob && (ob->mode == OB_MODE_TEXTURE_PAINT)) || (v3d->drawtype == OB_TEXTURE && - (scene->gm.matmode == GAME_MAT_GLSL || - BKE_scene_use_new_shading_nodes(scene)))) + (scene->gm.matmode == GAME_MAT_GLSL || + BKE_scene_use_new_shading_nodes(scene)))) { ED_region_tag_redraw(ar); } break; + } case ND_SHADING_DRAW: case ND_SHADING_LINKS: ED_region_tag_redraw(ar); @@ -1099,6 +1109,11 @@ static void view3d_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa if (wmn->data == ND_DATA || wmn->action == NA_EDITED) ED_region_tag_redraw(ar); break; + case NC_IMAGE: + /* Update for the image layers in texture paint. */ + if (wmn->action == NA_EDITED) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index d34b3d8be21..004b3e1b7d3 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1287,6 +1287,12 @@ static void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d) { /* do nothing */ } + /* texture paint mode sampling */ + else if (base && (base->object->mode & OB_MODE_TEXTURE_PAINT) && + (v3d->drawtype > OB_WIRE)) + { + /* do nothing */ + } else if ((base && (base->object->mode & OB_MODE_PARTICLE_EDIT)) && v3d->drawtype > OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)) { @@ -2509,7 +2515,7 @@ CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) mask |= CD_MASK_ORCO; } else { - if (scene->gm.matmode == GAME_MAT_GLSL) + if (scene->gm.matmode == GAME_MAT_GLSL || v3d->drawtype == OB_MATERIAL) mask |= CD_MASK_ORCO; } } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 888d80b529f..7430dfae750 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4435,7 +4435,7 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2]) } } -static void view3d_cursor3d_update(bContext *C, const int *mval) +void ED_view3d_cursor3d_update(bContext *C, const int mval[2]) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); @@ -4451,7 +4451,7 @@ static void view3d_cursor3d_update(bContext *C, const int *mval) static int view3d_cursor3d_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - view3d_cursor3d_update(C, event->mval); + ED_view3d_cursor3d_update(C, event->mval); op->customdata = SET_INT_IN_POINTER(event->type); WM_event_add_modal_handler(C, op); @@ -4468,7 +4468,7 @@ static int view3d_cursor3d_modal(bContext *C, wmOperator *op, const wmEvent *eve switch (event->type) { case MOUSEMOVE: - view3d_cursor3d_update(C, event->mval); + ED_view3d_cursor3d_update(C, event->mval); break; case LEFTMOUSE: return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 24f29fe9ea9..a88724a1cdd 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <stdlib.h> +#include "DNA_brush_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -45,6 +46,7 @@ #include "BKE_depsgraph.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_paint.h" #include "BKE_screen.h" #include "BKE_editmesh.h" @@ -336,8 +338,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) uiItemR(layout, &v3dptr, "viewport_shade", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); if (obedit == NULL && is_paint) { - - if (ob->mode & OB_MODE_WEIGHT_PAINT) { + if (ob->mode & OB_MODE_ALL_PAINT) { /* Only for Weight Paint. makes no sense in other paint modes. */ row = uiLayoutRow(layout, true); uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 2f7d2b3cfc5..bc721af231e 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -192,7 +192,7 @@ static bool transdata_check_local_center(TransInfo *t, short around) (t->flag & (T_OBJECT | T_POSE)) || (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || (t->spacetype == SPACE_IPO) || - (t->options & (CTX_MOVIECLIP | CTX_MASK))) + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE))) ); } @@ -263,17 +263,27 @@ static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy) void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) { if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) { - const float mval_f[2] = {(float)dx, (float)dy}; - ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac); + if (t->options & CTX_PAINT_CURVE) { + r_vec[0] = dx; + r_vec[1] = dy; + } + else { const float mval_f[2] = {(float)dx, (float)dy}; + ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac); + } } else if (t->spacetype == SPACE_IMAGE) { float aspx, aspy; if (t->options & CTX_MASK) { - convertViewVec2D_mask(t->view, r_vec, dx, dy); ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy); } + else if (t->options & CTX_PAINT_CURVE) { + r_vec[0] = dx; + r_vec[1] = dy; + + aspx = aspy = 1.0; + } else { convertViewVec2D(t->view, r_vec, dx, dy); ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); @@ -351,6 +361,10 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr adr[0] = v[0]; adr[1] = v[1]; } + else if (t->options & CTX_PAINT_CURVE) { + adr[0] = vec[0]; + adr[1] = vec[1]; + } else { float aspx, aspy, v[2]; @@ -452,7 +466,11 @@ void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV switch (t->spacetype) { case SPACE_VIEW3D: { - if (t->ar->regiontype == RGN_TYPE_WINDOW) { + if (t->options & CTX_PAINT_CURVE) { + adr[0] = vec[0]; + adr[1] = vec[1]; + } + else if (t->ar->regiontype == RGN_TYPE_WINDOW) { /* allow points behind the view [#33643] */ if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) { /* XXX, 2.64 and prior did this, weak! */ @@ -480,7 +498,7 @@ void projectFloatView(TransInfo *t, const float vec[3], float adr[2]) void applyAspectRatio(TransInfo *t, float vec[2]) { - if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { + if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) { SpaceImage *sima = t->sa->spacedata.first; float aspx, aspy; @@ -557,17 +575,23 @@ void removeAspectRatio(TransInfo *t, float vec[2]) static void viewRedrawForce(const bContext *C, TransInfo *t) { if (t->spacetype == SPACE_VIEW3D) { - /* Do we need more refined tags? */ - if (t->flag & T_POSE) - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); - else - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + if (t->options & CTX_PAINT_CURVE) { + wmWindow *window = CTX_wm_window(C); + WM_paint_cursor_tag_redraw(window, t->ar); + } + else { + /* Do we need more refined tags? */ + if (t->flag & T_POSE) + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); + else + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - /* for realtime animation record - send notifiers recognised by animation editors */ - // XXX: is this notifier a lame duck? - if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); - + /* for realtime animation record - send notifiers recognised by animation editors */ + // XXX: is this notifier a lame duck? + if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); + + } } else if (t->spacetype == SPACE_ACTION) { //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first; @@ -593,6 +617,10 @@ static void viewRedrawForce(const bContext *C, TransInfo *t) WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); } + else if (t->options & CTX_PAINT_CURVE) { + wmWindow *window = CTX_wm_window(C); + WM_paint_cursor_tag_redraw(window, t->ar); + } else { // XXX how to deal with lock? SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; @@ -3592,8 +3620,15 @@ static void initRotation(TransInfo *t) if (t->flag & T_2D_EDIT) t->flag |= T_NO_CONSTRAINT; - negate_v3_v3(t->axis, t->viewinv[2]); - normalize_v3(t->axis); + if (t->options & CTX_PAINT_CURVE) { + t->axis[0] = 0.0; + t->axis[1] = 0.0; + t->axis[2] = -1.0; + } + else { + negate_v3_v3(t->axis, t->viewinv[2]); + normalize_v3(t->axis); + } copy_v3_v3(t->axis_orig, t->axis); } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 0bccf177128..d3f233905c4 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -532,6 +532,7 @@ void flushTransNodes(TransInfo *t); void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); +void flushTransPaintCurve(TransInfo *t); void restoreBones(TransInfo *t); /*********************** exported from transform_manipulator.c ********** */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index b5dcdea55f1..d8f17315c01 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -756,6 +756,9 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) /* untested - mask aspect is TODO */ ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy); } + else if (t->options & CTX_PAINT_CURVE) { + aspx = aspy = 1.0; + } else { ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index ea70ba6c1ed..ddc50cbd0fe 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -33,6 +33,7 @@ #include <math.h> #include "DNA_anim_types.h" +#include "DNA_brush_types.h" #include "DNA_armature_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -80,6 +81,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_paint.h" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_rigidbody.h" @@ -5847,6 +5849,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) DAG_id_tag_update(&ob->id, OB_RECALC_DATA); } + else if (t->options & CTX_PAINT_CURVE) { + /* pass */ + } else if ((t->scene->basact) && (ob = t->scene->basact->object) && (ob->mode & OB_MODE_PARTICLE_EDIT) && @@ -7026,6 +7031,172 @@ void flushTransMasking(TransInfo *t) } } +typedef struct TransDataPaintCurve { + PaintCurvePoint *pcp; /* initial curve point */ + char id; +} TransDataPaintCurve; + + +#define PC_IS_ANY_SEL(pc) (((pc)->bez.f1 | (pc)->bez.f2 | (pc)->bez.f3) & SELECT) + +static void PaintCurveConvertHandle(PaintCurvePoint *pcp, int id, TransData2D *td2d, TransDataPaintCurve *tdpc, TransData *td) { + BezTriple *bezt = &pcp->bez; + copy_v2_v2(td2d->loc, bezt->vec[id]); + td2d->loc[2] = 0.0f; + td2d->loc2d = bezt->vec[id]; + + td->flag = 0; + td->loc = td2d->loc; + copy_v3_v3(td->center, bezt->vec[1]); + copy_v3_v3(td->iloc, td->loc); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext = NULL; + td->val = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdpc->id = id; + tdpc->pcp = pcp; +} + +static void PaintCurvePointToTransData(PaintCurvePoint *pcp, TransData *td, TransData2D *td2d, TransDataPaintCurve *tdpc) +{ + BezTriple *bezt = &pcp->bez; + + if (pcp->bez.f2 == SELECT) { + int i; + for (i = 0; i < 3; i++) { + copy_v2_v2(td2d->loc, bezt->vec[i]); + td2d->loc[2] = 0.0f; + td2d->loc2d = bezt->vec[i]; + + td->flag = 0; + td->loc = td2d->loc; + copy_v3_v3(td->center, bezt->vec[1]); + copy_v3_v3(td->iloc, td->loc); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext = NULL; + td->val = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdpc->id = i; + tdpc->pcp = pcp; + + td++; + td2d++; + tdpc++; + } + } + else { + if (bezt->f3 & SELECT) { + PaintCurveConvertHandle(pcp, 2, td2d, tdpc, td); + td2d++; + tdpc++; + td++; + } + + if (bezt->f1 & SELECT) { + PaintCurveConvertHandle(pcp, 0, td2d, tdpc, td); + } + } +} + +static void createTransPaintCurveVerts(bContext *C, TransInfo *t) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + PaintCurve *pc; + PaintCurvePoint *pcp; + Brush *br; + TransData *td = NULL; + TransData2D *td2d = NULL; + TransDataPaintCurve *tdpc = NULL; + int i; + int total = 0; + + t->total = 0; + + if (!paint || !paint->brush || !paint->brush->paint_curve) + return; + + br = paint->brush; + pc = br->paint_curve; + + for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { + if (PC_IS_ANY_SEL(pcp)) { + if (pcp->bez.f2 & SELECT) { + total += 3; + continue; + } + else { + if (pcp->bez.f1 & SELECT) + total++; + if (pcp->bez.f3 & SELECT) + total++; + } + } + } + + if (!total) + return; + + t->total = total; + td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D"); + td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransData"); + tdpc = t->customData = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); + t->flag |= T_FREE_CUSTOMDATA; + + for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { + if (PC_IS_ANY_SEL(pcp)) { + PaintCurvePointToTransData (pcp, td, td2d, tdpc); + + if (pcp->bez.f2 & SELECT) { + td += 3; + td2d += 3; + tdpc += 3; + } + else { + if (pcp->bez.f1 & SELECT) { + td++; + td2d++; + tdpc++; + } + if (pcp->bez.f3 & SELECT) { + td++; + td2d++; + tdpc++; + } + } + } + } +} + + +void flushTransPaintCurve(TransInfo *t) +{ + int i; + TransData2D *td2d = t->data2d; + TransDataPaintCurve *tdpc = (TransDataPaintCurve *)t->customData; + + for (i = 0; i < t->total; i++, tdpc++, td2d++) { + PaintCurvePoint *pcp = tdpc->pcp; + copy_v2_v2(pcp->bez.vec[tdpc->id], td2d->loc); + } +} + + void createTransData(bContext *C, TransInfo *t) { Scene *scene = t->scene; @@ -7058,6 +7229,10 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } + else if (t->options & CTX_PAINT_CURVE) { + if(!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) + createTransPaintCurveVerts(C, t); + } else if (t->obedit) { createTransUVs(C, t); if (t->data && (t->flag & T_PROP_EDIT)) { @@ -7164,7 +7339,7 @@ void createTransData(bContext *C, TransInfo *t) // XXX active-layer checking isn't done as that should probably be checked through context instead createTransPose(t, ob); } - else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) { + else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { /* important that ob_armature can be set even when its not selected [#23412] * lines below just check is also visible */ Object *ob_armature = modifiers_isDeformedByArmature(ob); @@ -7189,12 +7364,11 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } - else if (ob && (ob->mode & (OB_MODE_ALL_PAINT))) { - /* sculpt mode and project paint have own undo stack - * transform ops redo clears sculpt/project undo stack. - * - * Could use 'OB_MODE_ALL_PAINT' since there are key conflicts, - * transform + paint isn't well supported. */ + else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { + if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { + t->flag |= T_POINTS | T_2D_EDIT; + createTransPaintCurveVerts(C, t); + } } else { createTransObject(C, t); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 882a3ea8f6c..24dc6f6e74b 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -38,6 +38,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_lattice_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" @@ -74,6 +75,7 @@ #include "BKE_lattice.h" #include "BKE_nla.h" #include "BKE_context.h" +#include "BKE_paint.h" #include "BKE_sequencer.h" #include "BKE_editmesh.h" #include "BKE_tracking.h" @@ -98,6 +100,7 @@ #include "WM_api.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "transform.h" @@ -653,6 +656,9 @@ static void recalcData_image(TransInfo *t) if (t->options & CTX_MASK) { recalcData_mask_common(t); } + else if (t->options & CTX_PAINT_CURVE) { + flushTransPaintCurve(t); + } else if (t->obedit && t->obedit->type == OB_MESH) { SpaceImage *sima = t->sa->spacedata.first; @@ -965,6 +971,9 @@ void recalcData(TransInfo *t) else if (t->options & CTX_EDGE) { recalcData_objects(t); } + else if (t->options & CTX_PAINT_CURVE) { + flushTransPaintCurve(t); + } else if (t->spacetype == SPACE_IMAGE) { recalcData_image(t); } @@ -1073,6 +1082,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve ARegion *ar = CTX_wm_region(C); ScrArea *sa = CTX_wm_area(C); Object *obedit = CTX_data_edit_object(C); + Object *ob = CTX_data_active_object(C); PropertyRNA *prop; t->scene = sce; @@ -1198,6 +1208,13 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } + if (ob && ob->mode & OB_MODE_ALL_PAINT) { + Paint *p = BKE_paint_get_active_from_context(C); + if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + t->options |= CTX_PAINT_CURVE; + } + } + /* initialize UV transform from */ if (op && ((prop = RNA_struct_find_property(op->ptr, "correct_uv")))) { if (RNA_property_is_set(op->ptr, prop)) { @@ -1226,9 +1243,13 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (sima->mode == SI_MODE_MASK) { t->options |= CTX_MASK; } - else { - /* image not in uv edit, nor in mask mode, can happen for some tools */ + else if (sima->mode == SI_MODE_PAINT) { + Paint *p = &sce->toolsettings->imapaint.paint; + if (p->brush && (p->brush->flag & BRUSH_CURVE)) { + t->options |= CTX_PAINT_CURVE; + } } + /* image not in uv edit, nor in mask mode, can happen for some tools */ } else if (t->spacetype == SPACE_NODE) { // XXX for now, get View2D from the active region @@ -1409,7 +1430,7 @@ void postTrans(bContext *C, TransInfo *t) } if (t->spacetype == SPACE_IMAGE) { - if (t->options & CTX_MASK) { + if (t->options & (CTX_MASK | CTX_PAINT_CURVE)) { /* pass */ } else { @@ -1539,6 +1560,13 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) invert_m3_m3(imat, mat); mul_m3_v3(imat, r_center); } + else if (t->options & CTX_PAINT_CURVE) { + if (ED_view3d_project_float_global(t->ar, cursor, r_center, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { + r_center[0] = t->ar->winx / 2.0f; + r_center[1] = t->ar->winy / 2.0f; + } + r_center[2] = 0.0f; + } } void calculateCenterCursor2D(TransInfo *t, float r_center[2]) @@ -1586,6 +1614,12 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) r_center[0] = co[0] * aspx; r_center[1] = co[1] * aspy; } + else if (t->options & CTX_PAINT_CURVE) { + if (t->spacetype == SPACE_IMAGE) { + r_center[0] = UI_view2d_view_to_region_x(&t->ar->v2d, cursor[0]); + r_center[1] = UI_view2d_view_to_region_y(&t->ar->v2d, cursor[1]); + } + } else { r_center[0] = cursor[0] * aspx; r_center[1] = cursor[1] * aspy; @@ -1720,6 +1754,14 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) } } } + else if (t->options & CTX_PAINT_CURVE) { + Paint *p = BKE_paint_get_active(t->scene); + Brush *br = p->brush; + PaintCurve *pc = br->paint_curve; + copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]); + r_center[2] = 0.0f; + ok = true; + } else { /* object mode */ Scene *scene = t->scene; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 954f34cba4b..da9853db4fb 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -2383,6 +2383,9 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, float fa if (t->options & CTX_MASK) { ED_space_image_get_aspect(t->sa->spacedata.first, asp, asp + 1); } + else if (t->options & CTX_PAINT_CURVE) { + asp[0] = asp[1] = 1.0; + } else { ED_space_image_get_uv_aspect(t->sa->spacedata.first, asp, asp + 1); } diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index 386c9bbcb84..6eb8e525c96 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <string.h> +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -48,6 +49,8 @@ #include "BKE_DerivedMesh.h" #include "BKE_editmesh.h" +#include "BKE_material.h" + #include "BKE_scene.h" #include "BIF_gl.h" @@ -479,13 +482,39 @@ static void draw_uvs_texpaint(SpaceImage *sima, Scene *scene, Object *ob) { const bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); Image *curimage = ED_space_image(sima); + Mesh *me = ob->data; + Material *ma; if (sima->flag & SI_DRAW_OTHER) { draw_uvs_other(scene, ob, curimage, new_shading_nodes); } UI_ThemeColor(TH_UV_SHADOW); - draw_uvs_other_mesh(ob, curimage, new_shading_nodes); + + ma = give_current_material(ob, ob->actcol); + + if (me->mtpoly) { + MPoly *mpoly = me->mpoly; + MLoopUV *mloopuv, *mloopuv_base; + int a, b; + if (!(ma && ma->texpaintslot && ma->texpaintslot[ma->paint_active_slot].uvname[0] && + (mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ma->texpaintslot[ma->paint_active_slot].uvname)))) + { + mloopuv = me->mloopuv; + } + + mloopuv_base = mloopuv; + + for (a = me->totpoly; a > 0; a--, mpoly++) { + glBegin(GL_LINE_LOOP); + + mloopuv = mloopuv_base + mpoly->loopstart; + for (b = 0; b < mpoly->totloop; b++, mloopuv++) { + glVertex2fv(mloopuv->uv); + } + glEnd(); + } + } } #ifdef USE_EDBM_LOOPTRIS @@ -922,7 +951,7 @@ void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedi ToolSettings *toolsettings = scene->toolsettings; int show_uvedit, show_uvshadow, show_texpaint_uvshadow; - show_texpaint_uvshadow = (obact && obact->type == OB_MESH && obact->mode == OB_MODE_TEXTURE_PAINT); + show_texpaint_uvshadow = ED_space_image_show_texpaint(sima, obact); show_uvedit = ED_space_image_show_uvedit(sima, obedit); show_uvshadow = ED_space_image_show_uvshadow(sima, obedit); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 6cf34d9f93f..ed88c72edc4 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -3827,7 +3827,8 @@ static void UV_OT_reveal(wmOperatorType *ot) static int uv_set_2d_cursor_poll(bContext *C) { return ED_operator_uvedit_space_image(C) || - ED_space_image_maskedit_poll(C); + ED_space_image_maskedit_poll(C) || + ED_space_image_paint_curve(C); } static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op) diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 096b2080b2b..461995e37b5 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -139,6 +139,7 @@ void GPU_drawobject_free(struct DerivedMesh *dm); void GPU_vertex_setup(struct DerivedMesh *dm); void GPU_normal_setup(struct DerivedMesh *dm); void GPU_uv_setup(struct DerivedMesh *dm); +void GPU_texpaint_uv_setup(struct DerivedMesh *dm); /* colType is the cddata MCol type to use! */ void GPU_color_setup(struct DerivedMesh *dm, int colType); void GPU_edge_setup(struct DerivedMesh *dm); /* does not mix with other data */ diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index bdd70a49e7a..3ddec157c49 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -87,7 +87,7 @@ int GPU_get_material_alpha_blend(void); * - passing NULL clears the state again */ int GPU_set_tpage(struct MTFace *tface, int mipmap, int transp); - +void GPU_clear_tpage(bool force); /* Lights * - returns how many lights were enabled * - this affects fixed functions materials and texface, not glsl */ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index f5256f18897..0cd463555d7 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -46,11 +46,13 @@ #include "BLI_ghash.h" #include "BLI_threads.h" +#include "DNA_material_types.h" #include "DNA_meshdata_types.h" #include "BKE_ccg.h" #include "BKE_DerivedMesh.h" #include "BKE_paint.h" +#include "BKE_material.h" #include "BKE_pbvh.h" #include "DNA_userdef_types.h" @@ -63,13 +65,16 @@ typedef enum { GPU_BUFFER_VERTEX_STATE = 1, GPU_BUFFER_NORMAL_STATE = 2, - GPU_BUFFER_TEXCOORD_STATE = 4, - GPU_BUFFER_COLOR_STATE = 8, - GPU_BUFFER_ELEMENT_STATE = 16, + GPU_BUFFER_TEXCOORD_UNIT_0_STATE = 4, + GPU_BUFFER_TEXCOORD_UNIT_1_STATE = 8, + GPU_BUFFER_COLOR_STATE = 16, + GPU_BUFFER_ELEMENT_STATE = 32, } GPUBufferState; #define MAX_GPU_ATTRIB_DATA 32 +#define BUFFER_OFFSET(n) ((GLubyte *)NULL + (n)) + /* -1 - undefined, 0 - vertex arrays, 1 - VBOs */ static int useVBOs = -1; static GPUBufferState GLStates = 0; @@ -836,6 +841,61 @@ static void GPU_buffer_copy_uv(DerivedMesh *dm, float *varray, int *index, int * } } + +static void GPU_buffer_copy_uv_texpaint(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *UNUSED(user)) +{ + int start; + int i, totface; + + int totmaterial = dm->totmat; + MTFace **mtface_base; + MTFace *stencil_base; + int stencil; + MFace *mf; + + /* should have been checked for before, reassert */ + BLI_assert(DM_get_tessface_data_layer(dm, CD_MTFACE)); + mf = dm->getTessFaceArray(dm); + mtface_base = MEM_mallocN(totmaterial * sizeof(*mtface_base), "texslots"); + + for (i = 0; i < totmaterial; i++) { + mtface_base[i] = DM_paint_uvlayer_active_get(dm, i); + } + + stencil = CustomData_get_stencil_layer(&dm->faceData, CD_MTFACE); + stencil_base = CustomData_get_layer_n(&dm->faceData, CD_MTFACE, stencil); + + totface = dm->getNumTessFaces(dm); + + for (i = 0; i < totface; i++, mf++) { + int mat_i = mf->mat_nr; + start = index[mat_orig_to_new[mat_i]]; + + /* v1 v2 v3 */ + copy_v2_v2(&varray[start], mtface_base[mat_i][i].uv[0]); + copy_v2_v2(&varray[start + 2], stencil_base[i].uv[0]); + copy_v2_v2(&varray[start + 4], mtface_base[mat_i][i].uv[1]); + copy_v2_v2(&varray[start + 6], stencil_base[i].uv[1]); + copy_v2_v2(&varray[start + 8], mtface_base[mat_i][i].uv[2]); + copy_v2_v2(&varray[start + 10], stencil_base[i].uv[2]); + index[mat_orig_to_new[mat_i]] += 12; + + if (mf->v4) { + /* v3 v4 v1 */ + copy_v2_v2(&varray[start + 12], mtface_base[mat_i][i].uv[2]); + copy_v2_v2(&varray[start + 14], stencil_base[i].uv[2]); + copy_v2_v2(&varray[start + 16], mtface_base[mat_i][i].uv[3]); + copy_v2_v2(&varray[start + 18], stencil_base[i].uv[3]); + copy_v2_v2(&varray[start + 20], mtface_base[mat_i][i].uv[0]); + copy_v2_v2(&varray[start + 22], stencil_base[i].uv[0]); + index[mat_orig_to_new[mat_i]] += 12; + } + } + + MEM_freeN(mtface_base); +} + + static void copy_mcol_uc3(unsigned char *v, unsigned char *col) { v[0] = col[3]; @@ -925,6 +985,7 @@ typedef enum { GPU_BUFFER_NORMAL, GPU_BUFFER_COLOR, GPU_BUFFER_UV, + GPU_BUFFER_UV_TEXPAINT, GPU_BUFFER_EDGE, GPU_BUFFER_UVEDGE, } GPUBufferType; @@ -940,6 +1001,7 @@ const GPUBufferTypeSettings gpu_buffer_type_settings[] = { {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3}, {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3}, {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2}, + {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4}, {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2}, {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4} }; @@ -956,6 +1018,8 @@ static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBuffer return &gdo->colors; case GPU_BUFFER_UV: return &gdo->uv; + case GPU_BUFFER_UV_TEXPAINT: + return &gdo->uv; case GPU_BUFFER_EDGE: return &gdo->edges; case GPU_BUFFER_UVEDGE: @@ -977,6 +1041,8 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type) return sizeof(char) * 3 * dm->drawObject->tot_triangle_point; case GPU_BUFFER_UV: return sizeof(float) * 2 * dm->drawObject->tot_triangle_point; + case GPU_BUFFER_UV_TEXPAINT: + return sizeof(float) * 4 * dm->drawObject->tot_triangle_point; case GPU_BUFFER_EDGE: return sizeof(int) * 2 * dm->drawObject->totedge; case GPU_BUFFER_UVEDGE: @@ -1005,7 +1071,7 @@ static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type) if (!(user_data = DM_get_tessface_data_layer(dm, dm->drawObject->colType))) return NULL; } - else if (type == GPU_BUFFER_UV) { + else if (ELEM(type, GPU_BUFFER_UV, GPU_BUFFER_UV_TEXPAINT)) { if (!DM_get_tessface_data_layer(dm, CD_MTFACE)) return NULL; } @@ -1081,9 +1147,35 @@ void GPU_uv_setup(DerivedMesh *dm) glTexCoordPointer(2, GL_FLOAT, 0, dm->drawObject->uv->pointer); } - GLStates |= GPU_BUFFER_TEXCOORD_STATE; + GLStates |= GPU_BUFFER_TEXCOORD_UNIT_0_STATE; } +void GPU_texpaint_uv_setup(DerivedMesh *dm) +{ + if (!gpu_buffer_setup_common(dm, GPU_BUFFER_UV_TEXPAINT)) + return; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if (useVBOs) { + glBindBufferARB(GL_ARRAY_BUFFER_ARB, dm->drawObject->uv->id); + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), 0); + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), BUFFER_OFFSET(2 * sizeof(float))); + glClientActiveTexture(GL_TEXTURE0); + } + else { + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), dm->drawObject->uv->pointer); + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), (char *)dm->drawObject->uv->pointer + 2 * sizeof(float)); + glClientActiveTexture(GL_TEXTURE0); + } + + GLStates |= GPU_BUFFER_TEXCOORD_UNIT_0_STATE | GPU_BUFFER_TEXCOORD_UNIT_1_STATE; +} + + void GPU_color_setup(DerivedMesh *dm, int colType) { if (!dm->drawObject) { @@ -1241,8 +1333,13 @@ void GPU_buffer_unbind(void) glDisableClientState(GL_VERTEX_ARRAY); if (GLStates & GPU_BUFFER_NORMAL_STATE) glDisableClientState(GL_NORMAL_ARRAY); - if (GLStates & GPU_BUFFER_TEXCOORD_STATE) + if (GLStates & GPU_BUFFER_TEXCOORD_UNIT_0_STATE) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (GLStates & GPU_BUFFER_TEXCOORD_UNIT_1_STATE) { + glClientActiveTexture(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + } if (GLStates & GPU_BUFFER_COLOR_STATE) glDisableClientState(GL_COLOR_ARRAY); if (GLStates & GPU_BUFFER_ELEMENT_STATE) { @@ -1251,8 +1348,8 @@ void GPU_buffer_unbind(void) } } GLStates &= ~(GPU_BUFFER_VERTEX_STATE | GPU_BUFFER_NORMAL_STATE | - GPU_BUFFER_TEXCOORD_STATE | GPU_BUFFER_COLOR_STATE | - GPU_BUFFER_ELEMENT_STATE); + GPU_BUFFER_TEXCOORD_UNIT_0_STATE | GPU_BUFFER_TEXCOORD_UNIT_1_STATE | + GPU_BUFFER_COLOR_STATE | GPU_BUFFER_ELEMENT_STATE); for (i = 0; i < MAX_GPU_ATTRIB_DATA; i++) { if (attribData[i].index != -1) { diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index fae24465e17..fa9bc73dcbe 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -370,9 +370,9 @@ static void gpu_make_repbind(Image *ima) BKE_image_release_ibuf(ima, ibuf, NULL); } -static void gpu_clear_tpage(void) +void GPU_clear_tpage(bool force) { - if (GTS.lasttface==NULL) + if (GTS.lasttface==NULL && !force) return; GTS.lasttface= NULL; @@ -866,7 +866,7 @@ int GPU_set_tpage(MTFace *tface, int mipmap, int alphablend) /* check if we need to clear the state */ if (tface==NULL) { - gpu_clear_tpage(); + GPU_clear_tpage(false); return 0; } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 090e7a66d41..ce6a7eb1c47 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -163,6 +163,22 @@ typedef enum IMB_BlendMode { IMB_BLEND_DARKEN = 5, IMB_BLEND_ERASE_ALPHA = 6, IMB_BLEND_ADD_ALPHA = 7, + IMB_BLEND_OVERLAY = 8, + IMB_BLEND_HARDLIGHT = 9, + IMB_BLEND_COLORBURN = 10, + IMB_BLEND_LINEARBURN = 11, + IMB_BLEND_COLORDODGE = 12, + IMB_BLEND_SCREEN = 13, + IMB_BLEND_SOFTLIGHT = 14, + IMB_BLEND_PINLIGHT = 15, + IMB_BLEND_VIVIDLIGHT = 16, + IMB_BLEND_LINEARLIGHT = 17, + IMB_BLEND_DIFFERENCE = 18, + IMB_BLEND_EXCLUSION = 19, + IMB_BLEND_HUE = 20, + IMB_BLEND_SATURATION = 21, + IMB_BLEND_LUMINOSITY = 22, + IMB_BLEND_COLOR = 23, IMB_BLEND_COPY = 1000, IMB_BLEND_COPY_RGB = 1001, @@ -179,9 +195,9 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, void IMB_rectcpy(struct ImBuf *drect, struct ImBuf *srect, int destx, int desty, int srcx, int srcy, int width, int height); void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf, - unsigned short *dmask, unsigned short *smask, unsigned short mask_max, + unsigned short *dmask, unsigned short *curvemask, unsigned short *mmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, - int width, int height, IMB_BlendMode mode); + int width, int height, IMB_BlendMode mode, bool accumulate); /** * diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c index 6df7587ee5c..dd2406e234e 100644 --- a/source/blender/imbuf/intern/rectop.c +++ b/source/blender/imbuf/intern/rectop.c @@ -65,6 +65,39 @@ void IMB_blend_color_byte(unsigned char dst[4], unsigned char src1[4], unsigned blend_color_erase_alpha_byte(dst, src1, src2); break; case IMB_BLEND_ADD_ALPHA: blend_color_add_alpha_byte(dst, src1, src2); break; + case IMB_BLEND_OVERLAY: + blend_color_overlay_byte(dst, src1, src2); break; + case IMB_BLEND_HARDLIGHT: + blend_color_hardlight_byte(dst, src1, src2); break; + case IMB_BLEND_COLORBURN: + blend_color_burn_byte(dst, src1, src2); break; + case IMB_BLEND_LINEARBURN: + blend_color_linearburn_byte(dst, src1, src2); break; + case IMB_BLEND_COLORDODGE: + blend_color_dodge_byte(dst, src1, src2); break; + case IMB_BLEND_SCREEN: + blend_color_screen_byte(dst, src1, src2); break; + case IMB_BLEND_SOFTLIGHT: + blend_color_softlight_byte(dst, src1, src2); break; + case IMB_BLEND_PINLIGHT: + blend_color_pinlight_byte(dst, src1, src2); break; + case IMB_BLEND_LINEARLIGHT: + blend_color_linearlight_byte(dst, src1, src2); break; + case IMB_BLEND_VIVIDLIGHT: + blend_color_vividlight_byte(dst, src1, src2); break; + case IMB_BLEND_DIFFERENCE: + blend_color_difference_byte(dst, src1, src2); break; + case IMB_BLEND_EXCLUSION: + blend_color_exclusion_byte(dst, src1, src2); break; + case IMB_BLEND_COLOR: + blend_color_color_byte(dst, src1, src2); break; + case IMB_BLEND_HUE: + blend_color_hue_byte(dst, src1, src2); break; + case IMB_BLEND_SATURATION: + blend_color_saturation_byte(dst, src1, src2); break; + case IMB_BLEND_LUMINOSITY: + blend_color_luminosity_byte(dst, src1, src2); break; + default: dst[0] = src1[0]; dst[1] = src1[1]; @@ -93,6 +126,38 @@ void IMB_blend_color_float(float dst[4], float src1[4], float src2[4], IMB_Blend blend_color_erase_alpha_float(dst, src1, src2); break; case IMB_BLEND_ADD_ALPHA: blend_color_add_alpha_float(dst, src1, src2); break; + case IMB_BLEND_OVERLAY: + blend_color_overlay_float(dst, src1, src2); break; + case IMB_BLEND_HARDLIGHT: + blend_color_hardlight_float(dst, src1, src2); break; + case IMB_BLEND_COLORBURN: + blend_color_burn_float(dst, src1, src2); break; + case IMB_BLEND_LINEARBURN: + blend_color_linearburn_float(dst, src1, src2); break; + case IMB_BLEND_COLORDODGE: + blend_color_dodge_float(dst, src1, src2); break; + case IMB_BLEND_SCREEN: + blend_color_screen_float(dst, src1, src2); break; + case IMB_BLEND_SOFTLIGHT: + blend_color_softlight_float(dst, src1, src2); break; + case IMB_BLEND_PINLIGHT: + blend_color_pinlight_float(dst, src1, src2); break; + case IMB_BLEND_LINEARLIGHT: + blend_color_linearlight_float(dst, src1, src2); break; + case IMB_BLEND_VIVIDLIGHT: + blend_color_vividlight_float(dst, src1, src2); break; + case IMB_BLEND_DIFFERENCE: + blend_color_difference_float(dst, src1, src2); break; + case IMB_BLEND_EXCLUSION: + blend_color_exclusion_float(dst, src1, src2); break; + case IMB_BLEND_COLOR: + blend_color_color_float(dst, src1, src2); break; + case IMB_BLEND_HUE: + blend_color_hue_float(dst, src1, src2); break; + case IMB_BLEND_SATURATION: + blend_color_saturation_float(dst, src1, src2); break; + case IMB_BLEND_LUMINOSITY: + blend_color_luminosity_float(dst, src1, src2); break; default: dst[0] = src1[0]; dst[1] = src1[1]; @@ -226,22 +291,23 @@ static void imb_rectclip3(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, int *destx, void IMB_rectcpy(ImBuf *dbuf, ImBuf *sbuf, int destx, int desty, int srcx, int srcy, int width, int height) { - IMB_rectblend(dbuf, dbuf, sbuf, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, width, height, IMB_BLEND_COPY); + IMB_rectblend(dbuf, dbuf, sbuf, NULL, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, width, height, IMB_BLEND_COPY, false); } typedef void (*IMB_blend_func)(unsigned char *dst, const unsigned char *src1, const unsigned char *src2); typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2); -void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, - unsigned short *smask, unsigned short mask_max, +void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, unsigned short *curvemask, + unsigned short *texmask, float mask_max, int destx, int desty, int origx, int origy, int srcx, int srcy, int width, int height, - IMB_BlendMode mode) + IMB_BlendMode mode, bool accumulate) { unsigned int *drect = NULL, *orect, *srect = NULL, *dr, *or, *sr; float *drectf = NULL, *orectf, *srectf = NULL, *drf, *orf, *srf; - unsigned short *smaskrect = smask, *smr; + unsigned short *cmaskrect = curvemask, *cmr; unsigned short *dmaskrect = dmask, *dmr; + unsigned short *texmaskrect = texmask, *tmr; int do_float, do_char, srcskip, destskip, origskip, x; IMB_blend_func func = NULL; IMB_blend_func_float func_float = NULL; @@ -277,8 +343,11 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, if (do_float) srectf = sbuf->rect_float + (srcy * sbuf->x + srcx) * 4; srcskip = sbuf->x; - if (smaskrect) - smaskrect += srcy * sbuf->x + srcx; + if (cmaskrect) + cmaskrect += srcy * sbuf->x + srcx; + + if (texmaskrect) + texmaskrect += srcy * sbuf->x + srcx; } else { srect = drect; @@ -388,6 +457,70 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, func = blend_color_add_alpha_byte; func_float = blend_color_add_alpha_float; break; + case IMB_BLEND_OVERLAY: + func = blend_color_overlay_byte; + func_float = blend_color_overlay_float; + break; + case IMB_BLEND_HARDLIGHT: + func = blend_color_hardlight_byte; + func_float = blend_color_hardlight_float; + break; + case IMB_BLEND_COLORBURN: + func = blend_color_burn_byte; + func_float = blend_color_burn_float; + break; + case IMB_BLEND_LINEARBURN: + func = blend_color_linearburn_byte; + func_float = blend_color_linearburn_float; + break; + case IMB_BLEND_COLORDODGE: + func = blend_color_dodge_byte; + func_float = blend_color_dodge_float; + break; + case IMB_BLEND_SCREEN: + func = blend_color_screen_byte; + func_float = blend_color_screen_float; + break; + case IMB_BLEND_SOFTLIGHT: + func = blend_color_softlight_byte; + func_float = blend_color_softlight_float; + break; + case IMB_BLEND_PINLIGHT: + func = blend_color_pinlight_byte; + func_float = blend_color_pinlight_float; + break; + case IMB_BLEND_LINEARLIGHT: + func = blend_color_linearlight_byte; + func_float = blend_color_linearlight_float; + break; + case IMB_BLEND_VIVIDLIGHT: + func = blend_color_vividlight_byte; + func_float = blend_color_vividlight_float; + break; + case IMB_BLEND_DIFFERENCE: + func = blend_color_difference_byte; + func_float = blend_color_difference_float; + break; + case IMB_BLEND_EXCLUSION: + func = blend_color_exclusion_byte; + func_float = blend_color_exclusion_float; + break; + case IMB_BLEND_COLOR: + func = blend_color_color_byte; + func_float = blend_color_color_float; + break; + case IMB_BLEND_HUE: + func = blend_color_hue_byte; + func_float = blend_color_hue_float; + break; + case IMB_BLEND_SATURATION: + func = blend_color_saturation_byte; + func_float = blend_color_saturation_float; + break; + case IMB_BLEND_LUMINOSITY: + func = blend_color_luminosity_byte; + func_float = blend_color_luminosity_float; + break; default: break; } @@ -399,21 +532,60 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, or = orect; sr = srect; - if (dmaskrect && smaskrect) { + if (cmaskrect) { /* mask accumulation for painting */ - dmr = dmaskrect; - smr = smaskrect; + cmr = cmaskrect; + tmr = texmaskrect; - for (x = width; x > 0; x--, dr++, or++, sr++, dmr++, smr++) { - unsigned char *src = (unsigned char *)sr; + /* destination mask present, do max alpha masking */ + if (dmaskrect) { + dmr = dmaskrect; + for (x = width; x > 0; x--, dr++, or++, sr++, dmr++, cmr++) { + unsigned char *src = (unsigned char *)sr; + float mask_lim = mask_max * (*cmr); - if (src[3] && *smr) { - unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535); + if (texmaskrect) + mask_lim *= ((*tmr++) / 65535.0f); - if (mask > *dmr) { - unsigned char mask_src[4]; + if (src[3] && mask_lim) { + float mask; + + if (accumulate) + mask = *dmr + mask_lim; + else + mask = *dmr + mask_lim - (*dmr * (*cmr / 65535.0f)); + + mask = min_ff(mask, 65535.0); + + if (mask > *dmr) { + unsigned char mask_src[4]; + + *dmr = mask; - *dmr = mask; + mask_src[0] = src[0]; + mask_src[1] = src[1]; + mask_src[2] = src[2]; + mask_src[3] = divide_round_i(src[3] * mask, 65535); + + func((unsigned char *)dr, (unsigned char *)or, mask_src); + } + } + } + dmaskrect += origskip; + } + /* no destination mask buffer, do regular blend with masktexture if present */ + else { + for (x = width; x > 0; x--, dr++, or++, sr++, cmr++) { + unsigned char *src = (unsigned char *)sr; + float mask = (float)mask_max * ((float)(*cmr)); + + if (texmaskrect) + mask *= ((float)(*tmr++) / 65535.0f); + + mask = min_ff(mask, 65535.0); + + if (src[3] && (mask > 0.0f)) { + unsigned char mask_src[4]; mask_src[0] = src[0]; mask_src[1] = src[1]; @@ -425,8 +597,9 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, } } - dmaskrect += origskip; - smaskrect += srcskip; + cmaskrect += srcskip; + if (texmaskrect) + texmaskrect += srcskip; } else { /* regular blending */ @@ -446,28 +619,65 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, orf = orectf; srf = srectf; - if (dmaskrect && smaskrect) { + if (cmaskrect) { /* mask accumulation for painting */ - dmr = dmaskrect; - smr = smaskrect; + cmr = cmaskrect; + tmr = texmaskrect; + + /* destination mask present, do max alpha masking */ + if (dmaskrect) { + dmr = dmaskrect; + for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, cmr++) { + float mask_lim = mask_max * (*cmr); + + if (texmaskrect) + mask_lim *= ((*tmr++) / 65535.0f); + + if (srf[3] && mask_lim) { + float mask; + + if (accumulate) + mask = min_ff(*dmr + mask_lim, 65535.0); + else + mask = *dmr + mask_lim - (*dmr * (*cmr / 65535.0f)); + + mask = min_ff(mask, 65535.0); + + if (mask > *dmr) { + float mask_srf[4]; + + *dmr = mask; + mul_v4_v4fl(mask_srf, srf, mask / 65535.0f); + + func_float(drf, orf, mask_srf); + } + } + } + dmaskrect += origskip; + } + /* no destination mask buffer, do regular blend with masktexture if present */ + else { + for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, cmr++) { + float mask = (float)mask_max * ((float)(*cmr)); + + if (texmaskrect) + mask *= ((float)(*tmr++) / 65535.0f); - for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, smr++) { - if (srf[3] != 0 && *smr) { - unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535); + mask = min_ff(mask, 65535.0); - if (mask > *dmr) { + if (srf[3] && (mask > 0.0f)) { float mask_srf[4]; - *dmr = mask; - mul_v4_v4fl(mask_srf, srf, mask * (1.0f / 65535.0f)); + mul_v4_v4fl(mask_srf, srf, mask / 65535.0f); func_float(drf, orf, mask_srf); } } } - dmaskrect += origskip; - smaskrect += srcskip; + cmaskrect += srcskip; + if (texmaskrect) + texmaskrect += srcskip; } else { /* regular blending */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index f429360e1cf..cb6d17ab6c7 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -213,6 +213,8 @@ typedef struct PreviewImage { #define ID_MC MAKE_ID2('M', 'C') /* MovieClip */ #define ID_MSK MAKE_ID2('M', 'S') /* Mask */ #define ID_LS MAKE_ID2('L', 'S') /* FreestyleLineStyle */ +#define ID_PAL MAKE_ID2('P', 'L') /* Palette */ +#define ID_PC MAKE_ID2('P', 'C') /* Paint Curve */ /* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */ #define ID_SEQ MAKE_ID2('S', 'Q') diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 86fa7058f97..9fbd70419f4 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -35,6 +35,7 @@ #include "DNA_ID.h" #include "DNA_texture_types.h" /* for MTex */ +#include "DNA_curve_types.h" //#ifndef MAX_MTEX // XXX Not used? //#define MAX_MTEX 18 @@ -62,6 +63,9 @@ typedef struct Brush { struct ImBuf *icon_imbuf; PreviewImage *preview; + struct ColorBand *gradient; /* color gradient */ + struct PaintCurve *paint_curve; + char icon_filepath[1024]; /* 1024 = FILE_MAX */ float normal_weight; @@ -71,6 +75,7 @@ typedef struct Brush { float weight; /* brush weight */ int size; /* brush diameter */ int flag; /* general purpose flag */ + int mask_pressure; /* pressure influence for mask */ float jitter; /* jitter the position of the brush */ int jitter_absolute; /* absolute jitter in pixels */ int overlay_flags; @@ -82,10 +87,17 @@ typedef struct Brush { float rgb[3]; /* color */ float alpha; /* opacity */ + float secondary_rgb[3]; /* background color */ + int sculpt_plane; /* the direction of movement for sculpt vertices */ float plane_offset; /* offset for plane brushes (clay, flatten, fill, scrape) */ + float pad; + int gradient_spacing; + int gradient_stroke_mode; /* source for stroke color gradient application */ + int gradient_fill_mode; /* source for fill tool color gradient application */ + char sculpt_tool; /* active sculpt tool */ char vertexpaint_tool; /* active vertex/weight paint blend mode (poorly named) */ char imagepaint_tool; /* active image paint tool */ @@ -100,12 +112,21 @@ typedef struct Brush { float texture_sample_bias; + /* overlay */ int texture_overlay_alpha; int mask_overlay_alpha; int cursor_overlay_alpha; float unprojected_radius; + /* soften/sharpen */ + float sharp_threshold; + int blur_kernel_radius; + int blur_mode; + + /* fill tool */ + float fill_threshold; + float add_col[3]; float sub_col[3]; @@ -116,6 +137,52 @@ typedef struct Brush { float mask_stencil_dimension[2]; } Brush; +typedef struct PaletteColor +{ + struct PaletteColor *next, *prev; + /* two values, one to store rgb, other to store values for sculpt/weight */ + float rgb[3]; + float value; +} PaletteColor; + +typedef struct Palette +{ + ID id; + + /* pointer to individual colours */ + ListBase colors; + ListBase deleted; + + int num_of_colours; + int active_color; +} Palette; + +typedef struct PaintCurvePoint +{ + BezTriple bez; /* bezier handle */ + float pressure; /* pressure on that point */ +} PaintCurvePoint; + +typedef struct PaintCurve +{ + ID id; + PaintCurvePoint *points; /* points of curve */ + int tot_points; + int add_index; /* index where next point will be added */ +} PaintCurve; + +/* Brush.gradient_source */ +typedef enum BrushGradientSourceStroke { + BRUSH_GRADIENT_PRESSURE = 0, /* gradient from pressure */ + BRUSH_GRADIENT_SPACING_REPEAT = 1, /* gradient from spacing */ + BRUSH_GRADIENT_SPACING_CLAMP = 2 /* gradient from spacing */ +} BrushGradientSourceStroke; + +typedef enum BrushGradientSourceFill { + BRUSH_GRADIENT_LINEAR = 0, /* gradient from pressure */ + BRUSH_GRADIENT_RADIAL = 1 /* gradient from spacing */ +} BrushGradientSourceFill; + /* Brush.flag */ typedef enum BrushFlags { BRUSH_AIRBRUSH = (1 << 0), @@ -124,7 +191,7 @@ typedef enum BrushFlags { BRUSH_SIZE_PRESSURE = (1 << 3), BRUSH_JITTER_PRESSURE = (1 << 4), BRUSH_SPACING_PRESSURE = (1 << 5), - // BRUSH_FIXED_TEX = (1 << 6), /* obsolete, use mtex->brush_map_mode = MTEX_MAP_MODE_TILED instead */ + BRUSH_UNUSED = (1 << 6), BRUSH_RAKE = (1 << 7), BRUSH_ANCHORED = (1 << 8), BRUSH_DIR_IN = (1 << 9), @@ -138,7 +205,7 @@ typedef enum BrushFlags { BRUSH_SPACE_ATTEN = (1 << 18), BRUSH_ADAPTIVE_SPACE = (1 << 19), BRUSH_LOCK_SIZE = (1 << 20), -// BRUSH_TEXTURE_OVERLAY = (1 << 21), /* obsolete, use overlay_flags |= BRUSH_OVERLAY_PRIMARY instead */ + BRUSH_USE_GRADIENT = (1 << 21), BRUSH_EDGE_TO_EDGE = (1 << 22), BRUSH_DRAG_DOT = (1 << 23), BRUSH_INVERSE_SMOOTH_PRESSURE = (1 << 24), @@ -146,13 +213,16 @@ typedef enum BrushFlags { BRUSH_PLANE_TRIM = (1 << 26), BRUSH_FRONTFACE = (1 << 27), BRUSH_CUSTOM_ICON = (1 << 28), - - /* temporary flag which sets up automatically for correct brush - * drawing when inverted modal operator is running */ - BRUSH_INVERTED = (1 << 29), - BRUSH_ABSOLUTE_JITTER = (1 << 30) + BRUSH_LINE = (1 << 29), + BRUSH_ABSOLUTE_JITTER = (1 << 30), + BRUSH_CURVE = (1 << 31) } BrushFlags; +typedef enum { + BRUSH_MASK_PRESSURE_RAMP = (1 << 1), + BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2) +} BrushMaskPressureFlags; + /* Brush.overlay_flags */ typedef enum OverlayFlags { BRUSH_OVERLAY_CURSOR = (1), @@ -195,7 +265,9 @@ typedef enum BrushImagePaintTool { PAINT_TOOL_DRAW = 0, PAINT_TOOL_SOFTEN = 1, PAINT_TOOL_SMEAR = 2, - PAINT_TOOL_CLONE = 3 + PAINT_TOOL_CLONE = 3, + PAINT_TOOL_FILL = 4, + PAINT_TOOL_MASK = 5 } BrushImagePaintTool; /* direction that the brush displaces along */ @@ -222,6 +294,12 @@ typedef enum { BRUSH_MASK_SMOOTH = 1 } BrushMaskTool; +/* blur kernel types, Brush.blur_mode */ +typedef enum BlurKernelType { + KERNEL_GAUSSIAN, + KERNEL_BOX +} BlurKernelType; + #define MAX_BRUSH_PIXEL_RADIUS 200 #endif diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index 5fda38f52a1..02d1103c08e 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -41,6 +41,7 @@ #endif struct MTex; +struct Image; struct ColorBand; struct Group; struct bNodeTree; @@ -82,6 +83,11 @@ typedef struct GameSettings { int pad1; } GameSettings; +typedef struct TexPaintSlot { + struct Image *ima; /* image to be painted on */ + char uvname[64]; /* customdata index for uv layer, MAX_NAME*/ +} TexPaintSlot; + typedef struct Material { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -182,8 +188,15 @@ typedef struct Material { float line_col[4]; short line_priority; short vcol_alpha; - int pad4; + /* texture painting */ + short paint_active_slot; + short paint_clone_slot; + short tot_slots; + short pad4[3]; + + struct TexPaintSlot *texpaintslot; /* cached slot for painting. Make sure to recalculate before use + * with refresh_texpaint_image_cache */ ListBase gpumaterial; /* runtime */ } Material; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c4c1736c058..5e27fffcc97 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -811,7 +811,8 @@ typedef struct TimeMarker { /* Paint Tool Base */ typedef struct Paint { struct Brush *brush; - + struct Palette *palette; + /* WM Paint cursor */ void *paint_cursor; unsigned char paint_cursor_col[4]; @@ -840,9 +841,17 @@ typedef struct ImagePaintSettings { short seam_bleed, normal_angle; short screen_grab_size[2]; /* capture size for re-projection */ + /* new layer default resolution */ + int slot_xresolution_default; + int slot_yresolution_default; + int pad1; void *paintcursor; /* wm handle */ + struct Image *stencil; /* workaround until we support true layer masks */ + float slot_color_default[4]; + float stencil_col[3]; + float pad2; } ImagePaintSettings; /* ------------------------------------------- */ @@ -966,6 +975,11 @@ typedef struct UnifiedPaintSettings { /* unified brush weight, [0, 1] */ float weight; + /* unified brush color */ + float rgb[3]; + /* unified brush secondary color */ + float secondary_rgb[3]; + /* user preferences for sculpt and paint */ int flag; @@ -973,7 +987,6 @@ typedef struct UnifiedPaintSettings { /* record movement of mouse so that rake can start at an intuitive angle */ float last_rake[2]; - int pad; float brush_rotation; @@ -981,7 +994,14 @@ typedef struct UnifiedPaintSettings { * all data below are used to communicate with cursor drawing and tex sampling * *********************************************************************************/ int draw_anchored; - int anchored_size; + int anchored_size; + + char draw_inverted; + char pad3[7]; + + float overlap_factor; /* normalization factor due to accumulated value of curve along spacing. + * Calculated when brush spacing changes to dampen strength of stroke + * if space attenuation is used*/ float anchored_initial_mouse[2]; /* check is there an ongoing stroke right now */ @@ -1001,15 +1021,16 @@ typedef struct UnifiedPaintSettings { struct ColorSpace *colorspace; /* radius of brush, premultiplied with pressure. - * In case of anchored brushes contains that radius */ + * In case of anchored brushes contains the anchored radius */ float pixel_radius; - int pad2; + int pad4; } UnifiedPaintSettings; typedef enum { UNIFIED_PAINT_SIZE = (1 << 0), UNIFIED_PAINT_ALPHA = (1 << 1), UNIFIED_PAINT_WEIGHT = (1 << 5), + UNIFIED_PAINT_COLOR = (1 << 6), /* only used if unified size is enabled, mirrors the brush flags * BRUSH_LOCK_SIZE and BRUSH_SIZE_PRESSURE */ @@ -1647,7 +1668,7 @@ enum { typedef enum { PAINT_SHOW_BRUSH = (1 << 0), PAINT_FAST_NAVIGATE = (1 << 1), - PAINT_SHOW_BRUSH_ON_SURFACE = (1 << 2), + PAINT_SHOW_BRUSH_ON_SURFACE = (1 << 2) } PaintFlags; /* Paint.symmetry_flags diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index c7a6d4809a5..e7a98246ecc 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -809,6 +809,8 @@ typedef enum eSpaceImage_Flag { SI_DRAW_OTHER = (1 << 23), SI_COLOR_CORRECTION = (1 << 24), + + SI_NO_DRAW_TEXPAINT = (1 << 25) } eSpaceImage_Flag; /* Text Editor ============================================ */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index cb2341b0b7f..d7a33638fa2 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -154,7 +154,6 @@ typedef struct uiGradientColors { char high_gradient[4]; int show_grad; int pad2; - } uiGradientColors; typedef struct ThemeUI { @@ -328,6 +327,9 @@ typedef struct ThemeSpace { char info_warning[4], info_warning_text[4]; char info_info[4], info_info_text[4]; char info_debug[4], info_debug_text[4]; + + char paint_curve_pivot[4]; + char paint_curve_handle[4]; } ThemeSpace; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index add59bafc3f..a2bbaf67c1a 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -427,6 +427,9 @@ extern StructRNA RNA_OrController; extern StructRNA RNA_OutflowFluidSettings; extern StructRNA RNA_PackedFile; extern StructRNA RNA_Paint; +extern StructRNA RNA_PaintCurve; +extern StructRNA RNA_Palette; +extern StructRNA RNA_PaletteColor; extern StructRNA RNA_Panel; extern StructRNA RNA_Particle; extern StructRNA RNA_ParticleBrush; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 7959e60da33..c0c045593ca 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -153,6 +153,8 @@ short RNA_type_to_ID_code(StructRNA *type) if (RNA_struct_is_a(type, &RNA_WindowManager)) return ID_WM; if (RNA_struct_is_a(type, &RNA_MovieClip)) return ID_MC; if (RNA_struct_is_a(type, &RNA_Mask)) return ID_MSK; + if (RNA_struct_is_a(type, &RNA_Palette)) return ID_PAL; + if (RNA_struct_is_a(type, &RNA_PaintCurve)) return ID_PC; return 0; } @@ -190,6 +192,9 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_WM: return &RNA_WindowManager; case ID_MC: return &RNA_MovieClip; case ID_MSK: return &RNA_Mask; + case ID_PAL: return &RNA_Palette; + case ID_PC: return &RNA_PaintCurve; + default: return &RNA_ID; } } diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 05cb29bcf27..4aa476f2d6d 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -55,6 +55,8 @@ static EnumPropertyItem sculpt_stroke_method_items[] = { {BRUSH_SPACE, "SPACE", 0, "Space", "Limit brush application to the distance specified by spacing"}, {BRUSH_AIRBRUSH, "AIRBRUSH", 0, "Airbrush", "Keep applying paint effect while holding mouse (spray)"}, {BRUSH_ANCHORED, "ANCHORED", 0, "Anchored", "Keep the brush anchored to the initial location"}, + {BRUSH_LINE, "LINE", 0, "Line", "Draw a line with dabs separated according to spacing"}, + {BRUSH_CURVE, "CURVE", 0, "Curve", "Define the stroke curve with a bezier curve. Dabs are separated according to spacing"}, {0, NULL, 0, NULL, NULL} }; @@ -99,6 +101,8 @@ EnumPropertyItem brush_image_tool_items[] = { {PAINT_TOOL_SOFTEN, "SOFTEN", ICON_BRUSH_SOFTEN, "Soften", ""}, {PAINT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SMEAR, "Smear", ""}, {PAINT_TOOL_CLONE, "CLONE", ICON_BRUSH_CLONE, "Clone", ""}, + {PAINT_TOOL_FILL, "FILL", ICON_BRUSH_TEXFILL, "Fill", ""}, + {PAINT_TOOL_MASK, "MASK", ICON_BRUSH_TEXMASK, "Mask", ""}, {0, NULL, 0, NULL, NULL} }; @@ -222,17 +226,35 @@ static int rna_SculptToolCapabilities_has_smooth_stroke_get(PointerRNA *ptr) Brush *br = (Brush *)ptr->data; return (!(br->flag & BRUSH_ANCHORED) && !(br->flag & BRUSH_DRAG_DOT) && - !ELEM(br->sculpt_tool, - SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, - SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)); + !(br->flag & BRUSH_LINE) && + !(br->flag & BRUSH_CURVE) && + !ELEM(br->sculpt_tool, + SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)); +} + +static int rna_BrushCapabilities_has_smooth_stroke_get(PointerRNA *ptr) +{ + Brush *br = (Brush *)ptr->data; + return (!(br->flag & BRUSH_ANCHORED) && + !(br->flag & BRUSH_DRAG_DOT) && + !(br->flag & BRUSH_LINE) && + !(br->flag & BRUSH_CURVE)); } static int rna_SculptToolCapabilities_has_space_attenuation_get(PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - return ((br->flag & BRUSH_SPACE) && - !ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, - SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SNAKE_HOOK)); + return ((br->flag & (BRUSH_SPACE | BRUSH_LINE | BRUSH_CURVE)) && + !ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SNAKE_HOOK)); +} + +static int rna_ImapaintToolCapabilities_has_space_attenuation_get(PointerRNA *ptr) +{ + Brush *br = (Brush *)ptr->data; + return (br->flag & (BRUSH_SPACE | BRUSH_LINE | BRUSH_CURVE)) && + br->imagepaint_tool != PAINT_TOOL_FILL; } static int rna_BrushCapabilities_has_spacing_get(PointerRNA *ptr) @@ -273,11 +295,40 @@ static int rna_BrushCapabilities_has_texture_angle_source_get(PointerRNA *ptr) MTEX_MAP_MODE_RANDOM); } -static PointerRNA rna_Sculpt_sculpt_tool_capabilities_get(PointerRNA *ptr) +static int rna_ImapaintToolCapabilities_has_accumulate_get(PointerRNA *ptr) +{ + /* only support for draw tool */ + Brush *br = (Brush *)ptr->data; + + return ((br->flag & BRUSH_AIRBRUSH) || + (br->flag & BRUSH_DRAG_DOT) || + (br->flag & BRUSH_ANCHORED) || + (br->imagepaint_tool == PAINT_TOOL_SOFTEN) || + (br->imagepaint_tool == PAINT_TOOL_SMEAR) || + (br->imagepaint_tool == PAINT_TOOL_FILL) || + (br->mtex.tex && !ELEM(br->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D)) + ) ? false : true; +} + +static int rna_ImapaintToolCapabilities_has_radius_get(PointerRNA *ptr) +{ + /* only support for draw tool */ + Brush *br = (Brush *)ptr->data; + + return (br->imagepaint_tool != PAINT_TOOL_FILL); +} + + +static PointerRNA rna_Sculpt_tool_capabilities_get(PointerRNA *ptr) { return rna_pointer_inherit_refine(ptr, &RNA_SculptToolCapabilities, ptr->id.data); } +static PointerRNA rna_Imapaint_tool_capabilities_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_ImapaintToolCapabilities, ptr->id.data); +} + static PointerRNA rna_Brush_capabilities_get(PointerRNA *ptr) { return rna_pointer_inherit_refine(ptr, &RNA_BrushCapabilities, ptr->id.data); @@ -328,7 +379,6 @@ static void rna_Brush_size_update(Main *bmain, Scene *scene, PointerRNA *ptr) static void rna_Brush_sculpt_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - BKE_paint_invalidate_overlay_all(); rna_Brush_reset_icon(br, "sculpt"); rna_Brush_update(bmain, scene, ptr); } @@ -336,7 +386,6 @@ static void rna_Brush_sculpt_tool_update(Main *bmain, Scene *scene, PointerRNA * static void rna_Brush_vertex_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - BKE_paint_invalidate_overlay_all(); rna_Brush_reset_icon(br, "vertex_paint"); rna_Brush_update(bmain, scene, ptr); } @@ -344,11 +393,16 @@ static void rna_Brush_vertex_tool_update(Main *bmain, Scene *scene, PointerRNA * static void rna_Brush_imagepaint_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; - BKE_paint_invalidate_overlay_all(); rna_Brush_reset_icon(br, "image_paint"); rna_Brush_update(bmain, scene, ptr); } +static void rna_Brush_stroke_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene); + rna_Brush_update(bmain, scene, ptr); +} + static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; @@ -388,6 +442,17 @@ static void rna_Brush_set_size(PointerRNA *ptr, int value) brush->size = value; } +static void rna_Brush_use_gradient_set(PointerRNA *ptr, int value) +{ + Brush *br = (Brush *)ptr->data; + + if (value) br->flag |= BRUSH_USE_GRADIENT; + else br->flag &= ~BRUSH_USE_GRADIENT; + + if ((br->flag & BRUSH_USE_GRADIENT) && br->gradient == NULL) + br->gradient = add_colorband(true); +} + static void rna_Brush_set_unprojected_radius(PointerRNA *ptr, float value) { Brush *brush = ptr->data; @@ -397,13 +462,16 @@ static void rna_Brush_set_unprojected_radius(PointerRNA *ptr, float value) brush->unprojected_radius = value; } -static EnumPropertyItem *rna_Brush_direction_itemf(bContext *UNUSED(C), PointerRNA *ptr, +static EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) { + PaintMode mode = BKE_paintmode_get_active_from_context(C); + static EnumPropertyItem prop_default_items[] = { {0, NULL, 0, NULL, NULL} }; + /* sculpt mode */ static EnumPropertyItem prop_flatten_contrast_items[] = { {0, "FLATTEN", 0, "Flatten", "Add effect of brush"}, {BRUSH_DIR_IN, "CONTRAST", 0, "Contrast", "Subtract effect of brush"}, @@ -434,41 +502,66 @@ static EnumPropertyItem *rna_Brush_direction_itemf(bContext *UNUSED(C), PointerR {0, NULL, 0, NULL, NULL} }; + /* texture paint mode */ + static EnumPropertyItem prop_soften_sharpen_items[] = { + {0, "SOFTEN", 0, "Soften", "Blur effect of brush"}, + {BRUSH_DIR_IN, "SHARPEN", 0, "Sharpen", "Sharpen effect of brush"}, + {0, NULL, 0, NULL, NULL} + }; + Brush *me = (Brush *)(ptr->data); - switch (me->sculpt_tool) { - case SCULPT_TOOL_DRAW: - case SCULPT_TOOL_CREASE: - case SCULPT_TOOL_BLOB: - case SCULPT_TOOL_LAYER: - case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_CLAY_STRIPS: - return prop_direction_items; - - case SCULPT_TOOL_MASK: - switch ((BrushMaskTool)me->mask_tool) { - case BRUSH_MASK_DRAW: + switch (mode) { + case PAINT_SCULPT: + switch (me->sculpt_tool) { + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + case SCULPT_TOOL_LAYER: + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_STRIPS: return prop_direction_items; - break; - case BRUSH_MASK_SMOOTH: - return prop_default_items; - break; - } - case SCULPT_TOOL_FLATTEN: - return prop_flatten_contrast_items; + case SCULPT_TOOL_MASK: + switch ((BrushMaskTool)me->mask_tool) { + case BRUSH_MASK_DRAW: + return prop_direction_items; + break; + case BRUSH_MASK_SMOOTH: + return prop_default_items; + break; + } + + case SCULPT_TOOL_FLATTEN: + return prop_flatten_contrast_items; - case SCULPT_TOOL_FILL: - return prop_fill_deepen_items; + case SCULPT_TOOL_FILL: + return prop_fill_deepen_items; - case SCULPT_TOOL_SCRAPE: - return prop_scrape_peaks_items; + case SCULPT_TOOL_SCRAPE: + return prop_scrape_peaks_items; - case SCULPT_TOOL_PINCH: - return prop_pinch_magnify_items; + case SCULPT_TOOL_PINCH: + return prop_pinch_magnify_items; + + case SCULPT_TOOL_INFLATE: + return prop_inflate_deflate_items; + + default: + return prop_default_items; + } + break; - case SCULPT_TOOL_INFLATE: - return prop_inflate_deflate_items; + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: + switch (me->imagepaint_tool) { + case PAINT_TOOL_SOFTEN: + return prop_soften_sharpen_items; + + default: + return prop_default_items; + } + break; default: return prop_default_items; @@ -484,11 +577,15 @@ static EnumPropertyItem *rna_Brush_stroke_itemf(bContext *C, PointerRNA *UNUSED( {0, "DOTS", 0, "Dots", "Apply paint on each mouse move step"}, {BRUSH_SPACE, "SPACE", 0, "Space", "Limit brush application to the distance specified by spacing"}, {BRUSH_AIRBRUSH, "AIRBRUSH", 0, "Airbrush", "Keep applying paint effect while holding mouse (spray)"}, + {BRUSH_LINE, "LINE", 0, "Line", "Drag a line with dabs separated according to spacing"}, + {BRUSH_CURVE, "CURVE", 0, "Curve", "Define the stroke curve with a bezier curve. Dabs are separated according to spacing"}, {0, NULL, 0, NULL, NULL} }; switch (mode) { case PAINT_SCULPT: + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: return sculpt_stroke_method_items; default: @@ -622,10 +719,39 @@ static void rna_def_brush_capabilities(BlenderRNA *brna) BRUSH_CAPABILITY(has_texture_angle, "Has Texture Angle"); BRUSH_CAPABILITY(has_texture_angle_source, "Has Texture Angle Source"); BRUSH_CAPABILITY(has_spacing, "Has Spacing"); + BRUSH_CAPABILITY(has_smooth_stroke, "Has Smooth Stroke"); + #undef BRUSH_CAPABILITY } +static void rna_def_image_paint_capabilities(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ImapaintToolCapabilities", NULL); + RNA_def_struct_sdna(srna, "Brush"); + RNA_def_struct_nested(brna, srna, "Brush"); + RNA_def_struct_ui_text(srna, "Image Paint Capabilities", + "Read-only indications of which brush operations " + "are supported by the current image paint brush"); + +#define IMAPAINT_TOOL_CAPABILITY(prop_name_, ui_name_) \ + prop = RNA_def_property(srna, #prop_name_, \ + PROP_BOOLEAN, PROP_NONE); \ + RNA_def_property_clear_flag(prop, PROP_EDITABLE); \ + RNA_def_property_boolean_funcs(prop, "rna_ImapaintToolCapabilities_" \ + #prop_name_ "_get", NULL); \ + RNA_def_property_ui_text(prop, ui_name_, NULL) + + IMAPAINT_TOOL_CAPABILITY(has_accumulate, "Has Accumulate"); + IMAPAINT_TOOL_CAPABILITY(has_space_attenuation, "Has Space Attenuation"); + IMAPAINT_TOOL_CAPABILITY(has_radius, "Has Radius"); + +#undef IMAPAINT_TOOL_CAPABILITY +} + static void rna_def_brush(BlenderRNA *brna) { StructRNA *srna; @@ -640,6 +766,22 @@ static void rna_def_brush(BlenderRNA *brna) {IMB_BLEND_DARKEN, "DARKEN", 0, "Darken", "Use darken blending mode while painting"}, {IMB_BLEND_ERASE_ALPHA, "ERASE_ALPHA", 0, "Erase Alpha", "Erase alpha while painting"}, {IMB_BLEND_ADD_ALPHA, "ADD_ALPHA", 0, "Add Alpha", "Add alpha while painting"}, + {IMB_BLEND_OVERLAY, "OVERLAY", 0, "Overlay", "Use overlay blending mode while painting"}, + {IMB_BLEND_HARDLIGHT, "HARDLIGHT", 0, "Hard light", "Use hard light blending mode while painting"}, + {IMB_BLEND_COLORBURN, "COLORBURN", 0, "Color burn", "Use color burn blending mode while painting"}, + {IMB_BLEND_LINEARBURN, "LINEARBURN", 0, "Linear burn", "Use linear burn blending mode while painting"}, + {IMB_BLEND_COLORDODGE, "COLORDODGE", 0, "Color dodge", "Use color dodge blending mode while painting"}, + {IMB_BLEND_SCREEN, "SCREEN", 0, "Screen", "Use screen blending mode while painting"}, + {IMB_BLEND_SOFTLIGHT, "SOFTLIGHT", 0, "Soft light", "Use softlight blending mode while painting"}, + {IMB_BLEND_PINLIGHT, "PINLIGHT", 0, "Pin light", "Use pinlight blending mode while painting"}, + {IMB_BLEND_VIVIDLIGHT, "VIVIDLIGHT", 0, "Vivid light", "Use vividlight blending mode while painting"}, + {IMB_BLEND_LINEARLIGHT, "LINEARLIGHT", 0, "Linear light", "Use linearlight blending mode while painting"}, + {IMB_BLEND_DIFFERENCE, "DIFFERENCE", 0, "Difference", "Use difference blending mode while painting"}, + {IMB_BLEND_EXCLUSION, "EXCLUSION", 0, "Exclusion", "Use exclusion blending mode while painting"}, + {IMB_BLEND_HUE, "HUE", 0, "Hue", "Use hue blending mode while painting"}, + {IMB_BLEND_SATURATION, "SATURATION", 0, "Saturation", "Use saturation blending mode while painting"}, + {IMB_BLEND_LUMINOSITY, "LUMINOSITY", 0, "Luminosity", "Use luminosity blending mode while painting"}, + {IMB_BLEND_COLOR, "COLOR", 0, "Color", "Use color blending mode while painting"}, {0, NULL, 0, NULL, NULL} }; @@ -671,6 +813,32 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; + static EnumPropertyItem brush_blur_mode_items[] = { + {KERNEL_BOX, "BOX", 0, "Box", ""}, + {KERNEL_GAUSSIAN, "GAUSSIAN", 0, "Gaussian", ""}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem brush_gradient_items[] = { + {BRUSH_GRADIENT_PRESSURE, "PRESSURE", 0, "Pressure", ""}, + {BRUSH_GRADIENT_SPACING_REPEAT, "SPACING_REPEAT", 0, "Repeat", ""}, + {BRUSH_GRADIENT_SPACING_CLAMP, "SPACING_CLAMP", 0, "Clamp", ""}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem brush_gradient_fill_items[] = { + {BRUSH_GRADIENT_LINEAR, "LINEAR", 0, "Linear", ""}, + {BRUSH_GRADIENT_RADIAL, "RADIAL", 0, "Radial", ""}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem brush_mask_pressure_items[] = { + {0, "NONE", 0, "Off", ""}, + {BRUSH_MASK_PRESSURE_RAMP, "RAMP", ICON_STYLUS_PRESSURE, "Ramp", ""}, + {BRUSH_MASK_PRESSURE_CUTOFF, "CUTOFF", ICON_STYLUS_PRESSURE, "Cutoff", ""}, + {0, NULL, 0, NULL, NULL} + }; + srna = RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text(srna, "Brush", "Brush datablock for storing brush settings for painting and sculpting"); RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); @@ -710,7 +878,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_enum_items(prop, sculpt_stroke_method_items); RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Brush_stroke_itemf"); RNA_def_property_ui_text(prop, "Stroke Method", ""); - RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_update(prop, 0, "rna_Brush_stroke_update"); prop = RNA_def_property(srna, "texture_angle_source_random", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); @@ -769,6 +937,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Spacing", "Spacing between brush daubs as a percentage of brush diameter"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "grad_spacing", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "gradient_spacing"); + RNA_def_property_range(prop, 1, 10000); + RNA_def_property_ui_range(prop, 1, 10000, 5, -1); + RNA_def_property_ui_text(prop, "Gradient Spacing", "Spacing before brush gradient goes full circle"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smooth_stroke_radius", PROP_INT, PROP_NONE); RNA_def_property_range(prop, 10, 200); RNA_def_property_ui_text(prop, "Smooth Stroke Radius", "Minimum distance from last point before stroke continues"); @@ -791,7 +966,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "rgb"); RNA_def_property_ui_text(prop, "Color", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); - + + prop = RNA_def_property(srna, "secondary_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "secondary_rgb"); + RNA_def_property_ui_text(prop, "Secondary Color", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -884,6 +1065,32 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mask Stencil Dimensions", "Dimensions of mask stencil in viewport"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1, 3); + RNA_def_property_float_sdna(prop, NULL, "sharp_threshold"); + RNA_def_property_ui_text(prop, "Sharp Threshold", "Threshold below which, no sharpening is done"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "fill_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 1, 3); + RNA_def_property_float_sdna(prop, NULL, "fill_threshold"); + RNA_def_property_ui_text(prop, "Fill Threshold", "Threshold above which filling is not propagated"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "blur_kernel_radius", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "blur_kernel_radius"); + RNA_def_property_range(prop, 1, 10000); + RNA_def_property_ui_range(prop, 1, 50, 1, -1); + RNA_def_property_ui_text(prop, "Kernel Radius", "Radius of kernel used for soften and sharpen in pixels"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "blur_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_blur_mode_items); + RNA_def_property_ui_text(prop, "Blur Mode", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* flag */ prop = RNA_def_property(srna, "use_airbrush", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_AIRBRUSH); @@ -919,7 +1126,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); RNA_def_property_ui_text(prop, "Size Pressure", "Enable tablet pressure sensitivity for size"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - + + prop = RNA_def_property(srna, "use_gradient", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_USE_GRADIENT); + RNA_def_property_boolean_funcs(prop, NULL, "rna_Brush_use_gradient_set"); + RNA_def_property_ui_text(prop, "Use Gradient", "Use Gradient by utilizing a sampling method"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_pressure_jitter", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_JITTER_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); @@ -932,6 +1145,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Spacing Pressure", "Enable tablet pressure sensitivity for spacing"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_pressure_masking", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mask_pressure"); + RNA_def_property_enum_items(prop, brush_mask_pressure_items); + RNA_def_property_ui_text(prop, "Mask Pressure Mode", "Pen pressure makes texture influence smaller"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_inverse_smooth_pressure", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_INVERSE_SMOOTH_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); @@ -974,6 +1193,16 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Space", "Limit brush application to the distance specified by spacing"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_line", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_LINE); + RNA_def_property_ui_text(prop, "Line", "Draw a line with dabs separated according to spacing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_curve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_CURVE); + RNA_def_property_ui_text(prop, "Curve", "Define the stroke curve with a bezier curve. Dabs are separated according to spacing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_smooth_stroke", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SMOOTH_STROKE); RNA_def_property_ui_text(prop, "Smooth Stroke", "Brush lags behind mouse and follows a smoother path"); @@ -1015,7 +1244,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Edge-to-edge", "Drag anchor brush from edge-to-edge"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "use_drag_dot", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_restore_mesh", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_DRAG_DOT); RNA_def_property_ui_text(prop, "Restore Mesh", "Allow a single dot to be carefully positioned"); RNA_def_property_update(prop, 0, "rna_Brush_update"); @@ -1031,6 +1260,28 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Curve", "Editable falloff curve"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "paint_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Paint Curve", "Active Paint Curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "gradient", PROP_POINTER, PROP_NEVER_NULL); + RNA_def_property_pointer_sdna(prop, NULL, "gradient"); + RNA_def_property_struct_type(prop, "ColorRamp"); + RNA_def_property_ui_text(prop, "Gradient", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + /* gradient source */ + prop = RNA_def_property(srna, "gradient_stroke_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_gradient_items); + RNA_def_property_ui_text(prop, "Gradient Stroke Mode", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "gradient_fill_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_gradient_fill_items); + RNA_def_property_ui_text(prop, "Gradient Fill Mode", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* overlay flags */ prop = RNA_def_property(srna, "use_primary_overlay", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay_flags", BRUSH_OVERLAY_PRIMARY); @@ -1173,8 +1424,14 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "sculpt_capabilities", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "SculptToolCapabilities"); - RNA_def_property_pointer_funcs(prop, "rna_Sculpt_sculpt_tool_capabilities_get", NULL, NULL, NULL); + RNA_def_property_pointer_funcs(prop, "rna_Sculpt_tool_capabilities_get", NULL, NULL, NULL); RNA_def_property_ui_text(prop, "Sculpt Capabilities", "Brush's capabilities in sculpt mode"); + + prop = RNA_def_property(srna, "image_paint_capabilities", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + 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"); } @@ -1211,6 +1468,11 @@ static void rna_def_operator_stroke_element(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Pressure", "Tablet pressure"); + prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_range(prop, 0.0f, FLT_MAX); + RNA_def_property_ui_text(prop, "Brush Size", "Brush Size in screen space"); + prop = RNA_def_property(srna, "pen_flip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_IDPROPERTY); RNA_def_property_ui_text(prop, "Flip", ""); @@ -1237,6 +1499,7 @@ void RNA_def_brush(BlenderRNA *brna) rna_def_brush(brna); rna_def_brush_capabilities(brna); rna_def_sculpt_capabilities(brna); + rna_def_image_paint_capabilities(brna); rna_def_brush_texture_slot(brna); rna_def_operator_stroke_element(brna); } diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index be275058957..9a1053b4666 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -202,6 +202,7 @@ void rna_def_texmat_common(struct StructRNA *srna, const char *texspace_editable void rna_def_mtex_common(struct BlenderRNA *brna, struct StructRNA *srna, const char *begin, const char *activeget, const char *activeset, const char *activeeditable, const char *structname, const char *structname_slots, const char *update, const char *update_index); +void rna_def_mtex_texpaint(struct StructRNA *srna); void rna_def_render_layer_common(struct StructRNA *srna, int scene); void rna_def_actionbone_group_common(struct StructRNA *srna, int update_flag, const char *update_cb); diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 0a4799937cd..3c9eaf3c5db 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -82,6 +82,8 @@ EnumPropertyItem ramp_blend_items[] = { #include "DNA_node_types.h" #include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "BKE_context.h" #include "BKE_depsgraph.h" @@ -92,6 +94,8 @@ EnumPropertyItem ramp_blend_items[] = { #include "BKE_paint.h" #include "ED_node.h" +#include "ED_image.h" +#include "BKE_scene.h" static void rna_Material_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { @@ -165,6 +169,50 @@ static void rna_Material_mtex_begin(CollectionPropertyIterator *iter, PointerRNA rna_iterator_array_begin(iter, (void *)ma->mtex, sizeof(MTex *), MAX_MTEX, 0, NULL); } +static void rna_Material_texpaint_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Material *ma = (Material *)ptr->data; + rna_iterator_array_begin(iter, (void *)ma->texpaintslot, sizeof(TexPaintSlot), ma->tot_slots, 0, NULL); +} + + +static void rna_Material_active_paint_texture_index_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bScreen *sc; + Material *ma = ptr->id.data; + + + if (ma->use_nodes && ma->nodetree && BKE_scene_use_new_shading_nodes(scene)) { + struct bNode *node; + int index = 0; + for (node = ma->nodetree->nodes.first; node; node = node->next) { + if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) { + if (index++ == ma->paint_active_slot) { + break; + } + } + } + if (node) + nodeSetActive(ma->nodetree, node); + } + + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + ED_space_image_set(sima, scene, scene->obedit, ma->texpaintslot[ma->paint_active_slot].ima); + } + } + } + } + + DAG_id_tag_update(&ma->id, 0); + WM_main_add_notifier(NC_MATERIAL | ND_SHADING, ma); +} + static PointerRNA rna_Material_active_texture_get(PointerRNA *ptr) { Material *ma = (Material *)ptr->data; @@ -2059,6 +2107,8 @@ void RNA_def_material(BlenderRNA *brna) "rna_Material_active_texture_set", "rna_Material_active_texture_editable", "MaterialTextureSlot", "MaterialTextureSlots", "rna_Material_update", "rna_Material_update"); + rna_def_mtex_texpaint(srna); + /* only material has this one */ prop = RNA_def_property(srna, "use_textures", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "septex", 1); @@ -2147,4 +2197,27 @@ void rna_def_mtex_common(BlenderRNA *brna, StructRNA *srna, const char *begin, RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING_LINKS, update_index); } +void rna_def_mtex_texpaint(StructRNA *srna) +{ + PropertyRNA *prop; + + /* mtex */ + prop = RNA_def_property(srna, "texture_paint_slots", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_Material_texpaint_begin", "rna_iterator_array_next", "rna_iterator_array_end", + "rna_iterator_array_dereference_get", NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_ui_text(prop, "Textures", "Texture slots defining the mapping and influence of textures"); + + prop = RNA_def_property(srna, "paint_active_slot", PROP_INT, PROP_UNSIGNED); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Active Paint Texture Index", "Index of active texture paint slot"); + RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING_LINKS, "rna_Material_active_paint_texture_index_update"); + + prop = RNA_def_property(srna, "paint_clone_slot", PROP_INT, PROP_UNSIGNED); + RNA_def_property_range(prop, 0, INT_MAX); + RNA_def_property_ui_text(prop, "Clone Paint Texture Index", "Index of clone texture paint slot"); + RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING_LINKS, NULL); +} + + #endif diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 8c0f9980108..b0b99dcd2ca 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3121,11 +3121,13 @@ static void rna_def_mesh(BlenderRNA *brna) "rna_Mesh_uv_texture_stencil_set", NULL, NULL); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Mask UV Map", "UV map to mask the painted area"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); prop = RNA_def_property(srna, "uv_texture_stencil_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_funcs(prop, "rna_Mesh_uv_texture_stencil_index_get", "rna_Mesh_uv_texture_stencil_index_set", "rna_Mesh_uv_texture_index_range"); RNA_def_property_ui_text(prop, "Mask UV Map Index", "Mask UV map index"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); /* Tessellated face colors - used by renderers */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0d85cfa2435..192ec0b36a9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2120,6 +2120,11 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Unified Weight", "Instead of per-brush weight, the weight is shared across brushes"); + prop = RNA_def_property(srna, "use_unified_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_COLOR); + RNA_def_property_ui_text(prop, "Use Unified Color", + "Instead of per-brush color, the color is shared across brushes"); + /* unified paint settings that override the equivalent settings * from the active brush */ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); @@ -2152,6 +2157,18 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Weight", "Weight to assign in vertex groups"); RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_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_ui_text(prop, "Color", ""); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "secondary_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "secondary_rgb"); + RNA_def_property_ui_text(prop, "Secondary Color", ""); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "use_pressure_size", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_BRUSH_SIZE_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index a3a06893522..f335ce04d96 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -285,8 +285,67 @@ static void rna_Paint_brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po BKE_paint_invalidate_overlay_all(); WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); } + +static void rna_ImaPaint_stencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + /* not the best solution maybe, but will refresh the 3D viewport */ + WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); +} #else +static void rna_def_palettecolor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "PaletteColor", NULL); + RNA_def_struct_ui_text(srna, "Palette Color", ""); + + 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_ui_text(prop, "Color", ""); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "value"); + RNA_def_property_ui_text(prop, "Value", ""); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 1.0); + 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) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Palette", "ID"); + RNA_def_struct_ui_text(srna, "Palette", ""); + RNA_def_struct_ui_icon(srna, ICON_COLOR); + + prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "PaletteColor"); + RNA_def_property_ui_text(prop, "Palette Color", "Colors that are part of this palette"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); +} + +static void rna_def_paint_curve(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "PaintCurve", "ID"); + RNA_def_struct_ui_text(srna, "Paint Curve", ""); + RNA_def_struct_ui_icon(srna, ICON_CURVE_BEZCURVE); +} + + static void rna_def_paint(BlenderRNA *brna) { StructRNA *srna; @@ -302,6 +361,11 @@ static void rna_def_paint(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Brush", "Active Brush"); RNA_def_property_update(prop, 0, "rna_Paint_brush_update"); + prop = RNA_def_property(srna, "palette", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Palette", "Active Palette"); + prop = RNA_def_property(srna, "show_brush", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SHOW_BRUSH); RNA_def_property_ui_text(prop, "Show Brush", ""); @@ -532,11 +596,28 @@ static void rna_def_image_paint(BlenderRNA *brna) prop = RNA_def_property(srna, "use_stencil_layer", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMAGEPAINT_PROJECT_LAYER_STENCIL); RNA_def_property_ui_text(prop, "Stencil Layer", "Set the mask layer from the UV map buttons"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); prop = RNA_def_property(srna, "invert_stencil", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMAGEPAINT_PROJECT_LAYER_STENCIL_INV); RNA_def_property_ui_text(prop, "Invert", "Invert the stencil layer"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); + + prop = RNA_def_property(srna, "stencil_image", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stencil"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Stencil Image", "Image used as stencil"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); + + prop = RNA_def_property(srna, "stencil_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_float_sdna(prop, NULL, "stencil_col"); + RNA_def_property_ui_text(prop, "Stencil Color", "Stencil color in the viewport"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); + + prop = RNA_def_property(srna, "slot_color_default", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_text(prop, "New Layer Color", "Color/Alpha used for new"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "use_clone_layer", PROP_BOOLEAN, PROP_NONE); @@ -558,6 +639,16 @@ static void rna_def_image_paint(BlenderRNA *brna) prop = RNA_def_int_array(srna, "screen_grab_size", 2, NULL, 0, 0, "screen_grab_size", "Size to capture the image for re-projecting", 0, 0); RNA_def_property_range(prop, 512, 16384); + + prop = RNA_def_property(srna, "slot_xresolution_default", PROP_INT, PROP_UNSIGNED); + RNA_def_property_range(prop, 1, SHRT_MAX); + RNA_def_property_ui_range(prop, 64, 4096, 0, -1); + RNA_def_property_ui_text(prop, "X resolution", "X Resolution of new image"); + + prop = RNA_def_property(srna, "slot_yresolution_default", PROP_INT, PROP_UNSIGNED); + RNA_def_property_range(prop, 1, SHRT_MAX); + RNA_def_property_ui_range(prop, 64, 4096, 0, -1); + RNA_def_property_ui_text(prop, "Y resolution", "Y Resolution of new image"); } static void rna_def_particle_edit(BlenderRNA *brna) @@ -741,6 +832,9 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) { /* *** Non-Animated *** */ RNA_define_animate_sdna(false); + rna_def_palettecolor(brna); + rna_def_palette(brna); + rna_def_paint_curve(brna); rna_def_paint(brna); rna_def_sculpt(brna); rna_def_uv_sculpt(brna); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 39d6e665077..653ba2713a3 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -87,6 +87,18 @@ EnumPropertyItem space_type_items[] = { {0, NULL, 0, NULL, NULL} }; +static EnumPropertyItem pivot_items_full[] = { + {V3D_CENTER, "BOUNDING_BOX_CENTER", ICON_ROTATE, "Bounding Box Center", + "Pivot around bounding box center of selected object(s)"}, + {V3D_CURSOR, "CURSOR", ICON_CURSOR, "3D Cursor", "Pivot around the 3D cursor"}, + {V3D_LOCAL, "INDIVIDUAL_ORIGINS", ICON_ROTATECOLLECTION, + "Individual Origins", "Pivot around each object's own origin"}, + {V3D_CENTROID, "MEDIAN_POINT", ICON_ROTATECENTER, "Median Point", + "Pivot around the median point of selected objects"}, + {V3D_ACTIVE, "ACTIVE_ELEMENT", ICON_ROTACTIVE, "Active Element", "Pivot around active object"}, + {0, NULL, 0, NULL, NULL} +}; + static EnumPropertyItem draw_channels_items[] = { {SI_USE_ALPHA, "COLOR_ALPHA", ICON_IMAGE_RGB_ALPHA, "Color and Alpha", "Draw image with RGB colors and alpha transparency"}, @@ -616,9 +628,7 @@ static int rna_SpaceView3D_viewport_shade_get(PointerRNA *ptr) View3D *v3d = (View3D *)ptr->data; int drawtype = v3d->drawtype; - if (drawtype == OB_MATERIAL && !BKE_scene_use_new_shading_nodes(scene)) - return OB_SOLID; - else if (drawtype == OB_RENDER && !(type && type->view_draw)) + if (drawtype == OB_RENDER && !(type && type->view_draw)) return OB_SOLID; return drawtype; @@ -637,9 +647,7 @@ static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_WIRE); RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_SOLID); RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_TEXTURE); - - if (BKE_scene_use_new_shading_nodes(scene)) - RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_MATERIAL); + RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_MATERIAL); if (type && type->view_draw) RNA_enum_items_add_value(&item, &totitem, viewport_shade_items, OB_RENDER); @@ -803,6 +811,24 @@ static void rna_SpaceImageEditor_scopes_update(Main *UNUSED(bmain), Scene *scene ED_space_image_release_buffer(sima, ibuf, lock); } +static EnumPropertyItem *rna_SpaceImageEditor_pivot_itemf(bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + static EnumPropertyItem pivot_items[] = { + {V3D_CENTER, "CENTER", ICON_ROTATE, "Bounding Box Center", ""}, + {V3D_CENTROID, "MEDIAN", ICON_ROTATECENTER, "Median Point", ""}, + {V3D_CURSOR, "CURSOR", ICON_CURSOR, "2D Cursor", ""}, + {0, NULL, 0, NULL, NULL} + }; + + SpaceImage *sima = (SpaceImage *)ptr->data; + + if (sima->mode == SI_MODE_PAINT) + return pivot_items_full; + else + return pivot_items; +} + /* Space Text Editor */ static void rna_SpaceTextEditor_word_wrap_set(PointerRNA *ptr, int value) @@ -1492,6 +1518,11 @@ static void rna_def_space_image_uv(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw Other Objects", "Draw other selected objects that share the same image"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "show_texpaint", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SI_NO_DRAW_TEXPAINT); + RNA_def_property_ui_text(prop, "Draw Texture Paint UVs", "Draw overlay of texture paint uv layer"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "show_normalized_coords", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_COORDFLOATS); RNA_def_property_ui_text(prop, "Normalized Coordinates", @@ -1752,18 +1783,6 @@ static void rna_def_space_view3d(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static EnumPropertyItem pivot_items[] = { - {V3D_CENTER, "BOUNDING_BOX_CENTER", ICON_ROTATE, "Bounding Box Center", - "Pivot around bounding box center of selected object(s)"}, - {V3D_CURSOR, "CURSOR", ICON_CURSOR, "3D Cursor", "Pivot around the 3D cursor"}, - {V3D_LOCAL, "INDIVIDUAL_ORIGINS", ICON_ROTATECOLLECTION, - "Individual Origins", "Pivot around each object's own origin"}, - {V3D_CENTROID, "MEDIAN_POINT", ICON_ROTATECENTER, "Median Point", - "Pivot around the median point of selected objects"}, - {V3D_ACTIVE, "ACTIVE_ELEMENT", ICON_ROTACTIVE, "Active Element", "Pivot around active object"}, - {0, NULL, 0, NULL, NULL} - }; - static EnumPropertyItem manipulators_items[] = { {V3D_MANIP_TRANSLATE, "TRANSLATE", ICON_MAN_TRANS, "Manipulator Translate", "Use the manipulator for movement transformations"}, @@ -2042,7 +2061,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "around"); - RNA_def_property_enum_items(prop, pivot_items); + RNA_def_property_enum_items(prop, pivot_items_full); RNA_def_property_ui_text(prop, "Pivot Point", "Pivot center for rotation/scaling"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_pivot_update"); @@ -2310,13 +2329,6 @@ static void rna_def_space_image(BlenderRNA *brna) {0, NULL, 0, NULL, NULL} }; - static EnumPropertyItem pivot_items[] = { - {V3D_CENTER, "CENTER", ICON_ROTATE, "Bounding Box Center", ""}, - {V3D_CENTROID, "MEDIAN", ICON_ROTATECENTER, "Median Point", ""}, - {V3D_CURSOR, "CURSOR", ICON_CURSOR, "2D Cursor", ""}, - {0, NULL, 0, NULL, NULL} - }; - StructRNA *srna; PropertyRNA *prop; @@ -2404,7 +2416,8 @@ static void rna_def_space_image(BlenderRNA *brna) prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "around"); - RNA_def_property_enum_items(prop, pivot_items); + RNA_def_property_enum_items(prop, pivot_items_full); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceImageEditor_pivot_itemf"); RNA_def_property_ui_text(prop, "Pivot", "Rotation/Scaling Pivot"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index d3d17a90f99..da3d7b029ed 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -740,6 +740,11 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_boolean(func, "lock_luminosity", false, "", "Keep the color at its original vector length"); RNA_def_boolean(func, "cubic", false, "", "Cubic saturation for picking values close to white"); + func = RNA_def_function(srna, "template_palette", "uiTemplatePalette"); + RNA_def_function_ui_description(func, "Item. A palette used to pick colors"); + api_ui_item_rna_common(func); + RNA_def_boolean(func, "color", 0, "", "Display the colors as colors or values"); + func = RNA_def_function(srna, "template_image_layers", "uiTemplateImageLayers"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); parm = RNA_def_pointer(func, "image", "Image", "", ""); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index f4974266f60..d43bd8c1ad4 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1292,6 +1292,21 @@ static void rna_def_userdef_theme_spaces_face(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_userdef_update"); } +static void rna_def_userdef_theme_spaces_paint_curves(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "paint_curve_handle", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Paint Curve Handle", ""); + RNA_def_property_update(prop, 0, "rna_userdef_update"); + + prop = RNA_def_property(srna, "paint_curve_pivot", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Paint Curve Pivot", ""); + RNA_def_property_update(prop, 0, "rna_userdef_update"); +} + static void rna_def_userdef_theme_spaces_curves(StructRNA *srna, bool incl_nurbs, bool incl_lastsel, bool incl_vector) { PropertyRNA *prop; @@ -1567,6 +1582,8 @@ static void rna_def_userdef_theme_space_view3d(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Skin Root", ""); RNA_def_property_update(prop, 0, "rna_userdef_update"); + + rna_def_userdef_theme_spaces_paint_curves(srna); } @@ -2259,6 +2276,8 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_userdef_update"); rna_def_userdef_theme_spaces_curves(srna, false, false, false); + + rna_def_userdef_theme_spaces_paint_curves(srna); } static void rna_def_userdef_theme_space_seq(BlenderRNA *brna) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index cfa795cb3b7..253976052fd 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -344,8 +344,10 @@ void WM_event_print(const struct wmEvent *event); void WM_operator_region_active_win_set(struct bContext *C); /* drag and drop */ -struct wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, double value); +struct wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, int sy); +void WM_drag_free(struct wmDrag *drag); +void WM_drag_free_list(struct ListBase *lb); struct wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, int (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event), void (*copy)(struct wmDrag *, struct wmDropBox *)); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 9ad1bc97f4d..123a07d281c 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -597,6 +597,12 @@ typedef struct wmReport { #define WM_DRAG_PATH 2 #define WM_DRAG_NAME 3 #define WM_DRAG_VALUE 4 +#define WM_DRAG_COLOR 5 + +typedef enum wmDragFlags { + WM_DRAG_NOP = 0, + WM_DRAG_FREE_DATA = 1, +} wmDragFlags; /* note: structs need not exported? */ @@ -613,6 +619,7 @@ typedef struct wmDrag { int sx, sy; char opname[200]; /* if set, draws operator name*/ + unsigned int flags; } wmDrag; /* dropboxes are like keymaps, part of the screen/area/region definition */ diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 28bddb47778..d05cc572c45 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -462,7 +462,8 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) BLI_freelistN(&wm->queue); BLI_freelistN(&wm->paintcursors); - BLI_freelistN(&wm->drags); + + WM_drag_free_list(&wm->drags); wm_reports_free(wm); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 2aa177602cb..e5bba9285b4 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -143,7 +143,7 @@ void wm_dropbox_free(void) /* *********************************** */ /* note that the pointer should be valid allocated and not on stack */ -wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, double value) +wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) { wmWindowManager *wm = CTX_wm_manager(C); wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag"); @@ -152,6 +152,7 @@ wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, /* if multiple drags are added, they're drawn as list */ BLI_addtail(&wm->drags, drag); + drag->flags = flags; drag->icon = icon; drag->type = type; if (type == WM_DRAG_PATH) @@ -171,6 +172,22 @@ void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) drag->sy = sy; } +void WM_drag_free(wmDrag *drag) +{ + if ((drag->flags & WM_DRAG_FREE_DATA) && drag->poin) { + MEM_freeN(drag->poin); + } + + MEM_freeN(drag); +} + +void WM_drag_free_list(struct ListBase *lb) +{ + wmDrag *drag; + while ((drag = BLI_pophead(lb))) { + WM_drag_free(drag); + } +} static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag, wmEvent *event) { diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 256f456a7a4..7e2b7f2eb65 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -111,10 +111,13 @@ void wm_event_free(wmEvent *event) if (event->customdata) { if (event->customdatafree) { /* note: pointer to listbase struct elsewhere */ - if (event->custom == EVT_DATA_LISTBASE) - BLI_freelistN(event->customdata); - else + if (event->custom == EVT_DATA_DRAGDROP) { + ListBase *lb = event->customdata; + WM_drag_free_list(lb); + } + else { MEM_freeN(event->customdata); + } } } @@ -1934,17 +1937,17 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers wmDropBox *drop = handler->dropboxes->first; for (; drop; drop = drop->next) { /* other drop custom types allowed */ - if (event->custom == EVT_DATA_LISTBASE) { + if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = (ListBase *)event->customdata; wmDrag *drag; for (drag = lb->first; drag; drag = drag->next) { if (drop->poll(C, drag, event)) { - drop->copy(drag, drop); /* free the drags before calling operator */ - BLI_freelistN(event->customdata); + WM_drag_free_list(lb); + event->customdata = NULL; event->custom = 0; @@ -2146,10 +2149,12 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even return; } - if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) + if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) { win->screen->do_draw_drag = true; + } else if (event->type == ESCKEY) { - BLI_freelistN(&wm->drags); + WM_drag_free_list(&wm->drags); + win->screen->do_draw_drag = true; } else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { @@ -2161,7 +2166,7 @@ static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *even MEM_freeN(event->customdata); } - event->custom = EVT_DATA_LISTBASE; + event->custom = EVT_DATA_DRAGDROP; event->customdata = &wm->drags; event->customdatafree = 1; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index b30fadd46e6..56e094891f5 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1016,7 +1016,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* make blender drop event with custom data pointing to wm drags */ event.type = EVT_DROP; event.val = KM_RELEASE; - event.custom = EVT_DATA_LISTBASE; + event.custom = EVT_DATA_DRAGDROP; event.customdata = &wm->drags; event.customdatafree = 1; @@ -1035,7 +1035,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* try to get icon type from extension */ icon = ED_file_extension_icon((char *)stra->strings[a]); - WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0); + WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0, WM_DRAG_NOP); /* void poin should point to string, it makes a copy */ break; /* only one drop element supported now */ } diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 4a274d25170..6d3cdf6a270 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -38,7 +38,7 @@ /* customdata type */ #define EVT_DATA_GESTURE 1 #define EVT_DATA_TIMER 2 -#define EVT_DATA_LISTBASE 3 +#define EVT_DATA_DRAGDROP 3 #define EVT_DATA_NDOF_MOTION 4 /* tablet active, matches GHOST_TTabletMode */ diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 7d16b9fe34b..450ac5fd484 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -563,6 +563,7 @@ void uiTemplateColorspaceSettings(struct uiLayout *layout, struct PointerRNA *pt void uiTemplateColormanagedViewSettings(struct uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname) RET_NONE void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name) RET_NONE void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE +void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE /* rna render */ struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) RET_NULL diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp index b138f84ea97..c8975c245cb 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp @@ -247,7 +247,7 @@ void RAS_StorageIM::IndexPrimitivesInternal(RAS_MeshSlot& ms, bool multi) //ms.m_pDerivedMesh->drawMappedFacesTex(ms.m_pDerivedMesh, CheckTexfaceDM, mcol); current_blmat_nr = current_polymat->GetMaterialIndex(); current_image = current_polymat->GetBlenderImage(); - ms.m_pDerivedMesh->drawFacesTex(ms.m_pDerivedMesh, CheckTexDM, NULL, NULL); + ms.m_pDerivedMesh->drawFacesTex(ms.m_pDerivedMesh, CheckTexDM, NULL, NULL, DM_DRAW_USE_ACTIVE_UV); } return; } diff --git a/source/gameengine/VideoTexture/ImageBuff.cpp b/source/gameengine/VideoTexture/ImageBuff.cpp index 6cc8d287e66..705d9136cbe 100644 --- a/source/gameengine/VideoTexture/ImageBuff.cpp +++ b/source/gameengine/VideoTexture/ImageBuff.cpp @@ -163,7 +163,7 @@ void ImageBuff::plot(unsigned char *img, short width, short height, short x, sho // assign temporarily our buffer to the ImBuf buffer, we use the same format tmpbuf->rect = (unsigned int*)img; m_imbuf->rect = m_image; - IMB_rectblend(m_imbuf, m_imbuf, tmpbuf, NULL, NULL, 0, x, y, x, y, 0, 0, width, height, (IMB_BlendMode)mode); + IMB_rectblend(m_imbuf, m_imbuf, tmpbuf, NULL, NULL, NULL, 0, x, y, x, y, 0, 0, width, height, (IMB_BlendMode)mode, false); // remove so that MB_freeImBuf will free our buffer m_imbuf->rect = NULL; tmpbuf->rect = NULL; @@ -186,7 +186,7 @@ void ImageBuff::plot(ImageBuff *img, short x, short y, short mode) // assign temporarily our buffer to the ImBuf buffer, we use the same format img->m_imbuf->rect = img->m_image; m_imbuf->rect = m_image; - IMB_rectblend(m_imbuf, m_imbuf, img->m_imbuf, NULL, NULL, 0, x, y, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode); + IMB_rectblend(m_imbuf, m_imbuf, img->m_imbuf, NULL, NULL, NULL, 0, x, y, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode, false); // remove so that MB_freeImBuf will free our buffer m_imbuf->rect = NULL; img->m_imbuf->rect = NULL; |