Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntony Riakiotakis <kalast@gmail.com>2014-07-21 14:02:05 +0400
committerAntony Riakiotakis <kalast@gmail.com>2014-07-21 14:02:05 +0400
commitf745564e4ee791e4faf804b09ce975b882f4f8d9 (patch)
tree90ebbe363ccd925cedc652c9bb018ce552b5a2ab
parent8489b94e07f9e73bd3c9c3e4f6a91f1f0a259827 (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.
-rw-r--r--SConstruct2
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py104
-rw-r--r--release/scripts/startup/bl_ui/space_image.py130
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py2
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py145
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h12
-rw-r--r--source/blender/blenkernel/BKE_blender.h2
-rw-r--r--source/blender/blenkernel/BKE_brush.h6
-rw-r--r--source/blender/blenkernel/BKE_library.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h2
-rw-r--r--source/blender/blenkernel/BKE_material.h4
-rw-r--r--source/blender/blenkernel/BKE_paint.h24
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.c32
-rw-r--r--source/blender/blenkernel/intern/brush.c56
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c54
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c11
-rw-r--r--source/blender/blenkernel/intern/editderivedmesh.c4
-rw-r--r--source/blender/blenkernel/intern/idcode.c2
-rw-r--r--source/blender/blenkernel/intern/library.c19
-rw-r--r--source/blender/blenkernel/intern/material.c115
-rw-r--r--source/blender/blenkernel/intern/paint.c103
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c50
-rw-r--r--source/blender/blenkernel/intern/texture.c5
-rw-r--r--source/blender/blenlib/BLI_math_color_blend.h36
-rw-r--r--source/blender/blenlib/BLI_math_vector.h2
-rw-r--r--source/blender/blenlib/intern/math_color_blend_inline.c1171
-rw-r--r--source/blender/blenloader/intern/readfile.c59
-rw-r--r--source/blender/blenloader/intern/versioning_270.c22
-rw-r--r--source/blender/blenloader/intern/writefile.c34
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt2
-rw-r--r--source/blender/editors/datafiles/SConscript2
-rw-r--r--source/blender/editors/include/ED_datafiles.h6
-rw-r--r--source/blender/editors/include/ED_image.h3
-rw-r--r--source/blender/editors/include/ED_paint.h6
-rw-r--r--source/blender/editors/include/ED_transform.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h1
-rw-r--r--source/blender/editors/include/UI_icons.h2
-rw-r--r--source/blender/editors/include/UI_interface.h15
-rw-r--r--source/blender/editors/include/UI_resources.h3
-rw-r--r--source/blender/editors/interface/interface_handlers.c148
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_intern.h4
-rw-r--r--source/blender/editors/interface/interface_ops.c91
-rw-r--r--source/blender/editors/interface/interface_templates.c59
-rw-r--r--source/blender/editors/interface/interface_widgets.c12
-rw-r--r--source/blender/editors/interface/resources.c57
-rw-r--r--source/blender/editors/render/render_update.c6
-rw-r--r--source/blender/editors/screen/screen_ops.c3
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c160
-rw-r--r--source/blender/editors/sculpt_paint/paint_curve.c800
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c687
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c872
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c968
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h63
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c176
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c478
-rw-r--r--source/blender/editors/sculpt_paint/paint_undo.c54
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c296
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c28
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c91
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c10
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_image/image_edit.c39
-rw-r--r--source/blender/editors/space_image/image_ops.c11
-rw-r--r--source/blender/editors/space_image/space_image.c15
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c106
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c21
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c5
-rw-r--r--source/blender/editors/transform/transform.c71
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_constraints.c3
-rw-r--r--source/blender/editors/transform/transform_conversions.c188
-rw-r--r--source/blender/editors/transform/transform_generics.c48
-rw-r--r--source/blender/editors/transform/transform_snap.c3
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c33
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c3
-rw-r--r--source/blender/gpu/GPU_buffers.h1
-rw-r--r--source/blender/gpu/GPU_draw.h2
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c113
-rw-r--r--source/blender/gpu/intern/gpu_draw.c6
-rw-r--r--source/blender/imbuf/IMB_imbuf.h20
-rw-r--r--source/blender/imbuf/intern/rectop.c270
-rw-r--r--source/blender/makesdna/DNA_ID.h2
-rw-r--r--source/blender/makesdna/DNA_brush_types.h94
-rw-r--r--source/blender/makesdna/DNA_material_types.h15
-rw-r--r--source/blender/makesdna/DNA_scene_types.h33
-rw-r--r--source/blender/makesdna/DNA_space_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h4
-rw-r--r--source/blender/makesrna/RNA_access.h3
-rw-r--r--source/blender/makesrna/intern/rna_ID.c5
-rw-r--r--source/blender/makesrna/intern/rna_brush.c349
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_material.c73
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c2
-rw-r--r--source/blender/makesrna/intern/rna_scene.c17
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c96
-rw-r--r--source/blender/makesrna/intern/rna_space.c67
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c19
-rw-r--r--source/blender/windowmanager/WM_api.h4
-rw-r--r--source/blender/windowmanager/WM_types.h7
-rw-r--r--source/blender/windowmanager/intern/wm.c3
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c19
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c23
-rw-r--r--source/blender/windowmanager/intern/wm_window.c4
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c1
-rw-r--r--source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_StorageIM.cpp2
-rw-r--r--source/gameengine/VideoTexture/ImageBuff.cpp4
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;