UV: Remove UV sculpt use of brushes

For the brush assets project (#116337) all editors with brushes are
getting an asset shelf, and the brush tools are combined, with
individual brushes accessed in the shelf. That design seems way
overkill for UV sculpting which is just three very simple tools.

In order to avoid one editor with inconsistent use of brushes, which
would significantly increase the complexity of the system after the
brush assets merge, port the three UV sculpt tools to be regular
modal operators that don't use the brush or paint system at all.

To be clear, this is a compromise that doesn't feel ideal, but no
one could think of a better solution. Theoretically this removes
some flexibility from UV edit "sculpting", in practice it probably
won't be a noticeable change.

Pull Request: https://projects.blender.org/blender/blender/pulls/120797
This commit is contained in:
Hans Goudey 2024-04-26 17:10:04 +02:00 committed by Hans Goudey
parent 5a458fe92a
commit e3894f0a07
25 changed files with 376 additions and 345 deletions

@ -7144,21 +7144,53 @@ def km_image_editor_tool_uv_rip_region(params):
)
def km_image_editor_tool_uv_sculpt_stroke(params):
def km_image_editor_tool_uv_grab(params):
return (
"Image Editor Tool: Uv, Sculpt Stroke",
"Image Editor Tool: Uv, Grab",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'INVERT')]}),
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS', "shift": True},
{"properties": [("mode", 'RELAX')]}),
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
*_template_paint_radial_control("uv_sculpt"),
("sculpt.uv_sculpt_grab", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_grab", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
def km_image_editor_tool_uv_relax(params):
return (
"Image Editor Tool: Uv, Relax",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
def km_image_editor_tool_uv_pinch(params):
return (
"Image Editor Tool: Uv, Pinch",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_pinch", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_pinch", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
@ -8920,7 +8952,9 @@ def generate_keymaps(params=None):
*(km_image_editor_tool_uv_select_circle(params, fallback=fallback) for fallback in (False, True)),
*(km_image_editor_tool_uv_select_lasso(params, fallback=fallback) for fallback in (False, True)),
km_image_editor_tool_uv_rip_region(params),
km_image_editor_tool_uv_sculpt_stroke(params),
km_image_editor_tool_uv_grab(params),
km_image_editor_tool_uv_relax(params),
km_image_editor_tool_uv_pinch(params),
km_image_editor_tool_uv_move(params),
km_image_editor_tool_uv_rotate(params),
km_image_editor_tool_uv_scale(params),

@ -714,10 +714,6 @@ class IMAGE_HT_tool_header(Header):
layout.popover("IMAGE_PT_tools_brush_display")
layout.popover("IMAGE_PT_tools_brush_texture")
layout.popover("IMAGE_PT_tools_mask_texture")
elif tool_mode == 'UV':
if (tool is not None) and tool.has_datablock:
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_mode_settings(self, context):
layout = self.layout
@ -1344,50 +1340,21 @@ class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
row.prop(ipaint, "tile_y", text="Y", toggle=True)
class UVSculptPanel(UnifiedPaintPanel):
@classmethod
def poll(cls, context):
return cls.get_brush_mode(context) == 'UV_SCULPT'
class IMAGE_PT_uv_sculpt_brush_select(Panel, BrushSelectPanel, ImagePaintPanel, UVSculptPanel):
bl_context = ".uv_sculpt"
bl_category = "Tool"
bl_label = "Brushes"
class IMAGE_PT_uv_sculpt_brush_settings(Panel, ImagePaintPanel, UVSculptPanel):
bl_context = ".uv_sculpt"
bl_category = "Tool"
bl_label = "Brush Settings"
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
uvsculpt = tool_settings.uv_sculpt
brush = uvsculpt.brush
brush_settings(layout.column(), context, brush)
if brush:
if brush.uv_sculpt_tool == 'RELAX':
# Although this settings is stored in the scene,
# it is only used by a single tool,
# so it doesn't make sense from a user perspective to move it to the Options panel.
layout.prop(tool_settings, "uv_relax_method")
class IMAGE_PT_uv_sculpt_curve(Panel, FalloffPanel, ImagePaintPanel, UVSculptPanel):
class IMAGE_PT_uv_sculpt_curve(Panel, ImagePaintPanel):
bl_context = ".uv_sculpt" # Dot on purpose (access from top-bar).
bl_parent_id = "IMAGE_PT_uv_sculpt_brush_settings"
bl_category = "Tool"
bl_label = "Falloff"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
props = context.scene.tool_settings.uv_sculpt
layout.prop(props, "curve_preset", text="")
if props.curve_preset == 'CUSTOM':
layout.template_curve_mapping(props, "strength_curve")
class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel):
class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel):
bl_context = ".uv_sculpt" # Dot on purpose (access from top-bar).
bl_category = "Tool"
bl_label = "Options"
@ -1396,12 +1363,10 @@ class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel):
layout = self.layout
tool_settings = context.tool_settings
uvsculpt = tool_settings.uv_sculpt
col = layout.column()
col.prop(tool_settings, "uv_sculpt_lock_borders")
col.prop(tool_settings, "uv_sculpt_all_islands")
col.prop(uvsculpt, "show_brush", text="Display Cursor")
class ImageScopesPanel:
@ -1776,8 +1741,6 @@ classes = (
IMAGE_PT_paint_curve,
IMAGE_PT_tools_brush_display,
IMAGE_PT_tools_imagepaint_symmetry,
IMAGE_PT_uv_sculpt_brush_select,
IMAGE_PT_uv_sculpt_brush_settings,
IMAGE_PT_uv_sculpt_options,
IMAGE_PT_uv_sculpt_curve,
IMAGE_PT_view_histogram,

@ -2181,36 +2181,82 @@ class _defs_image_uv_edit:
class _defs_image_uv_sculpt:
@staticmethod
def generate_from_brushes(context):
def draw_cursor(context, _tool, xy):
@ToolDef.from_fn
def grab():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
tool_settings = context.tool_settings
uv_sculpt = tool_settings.uv_sculpt
if not uv_sculpt.show_brush:
return
ups = tool_settings.unified_paint_settings
if ups.use_unified_size:
radius = ups.size
else:
brush = tool_settings.uv_sculpt.brush
if brush is None:
return
radius = brush.size
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.uv_sculpt.",
type=bpy.types.Brush,
attr="uv_sculpt_tool",
tooldef_keywords=dict(
operator="sculpt.uv_sculpt_stroke",
keymap="Image Editor Tool: Uv, Sculpt Stroke",
draw_cursor=draw_cursor,
options={'KEYMAP_FALLBACK'},
),
return dict(
idname="sculpt.uv_sculpt_grab",
label="Grab",
icon="brush.uv_sculpt.grab",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def relax():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
props = tool.operator_properties("sculpt.uv_sculpt_relax")
layout.prop(props, "relax_method", text="Method")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return dict(
idname="sculpt.uv_sculpt_relax",
label="Relax",
icon="brush.uv_sculpt.relax",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def pinch():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return dict(
idname="sculpt.uv_sculpt_pinch",
label="Pinch",
icon="brush.uv_sculpt.pinch",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ -3014,11 +3060,9 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_image_uv_edit.rip_region,
None,
lambda context: (
_defs_image_uv_sculpt.generate_from_brushes(context)
if _defs_image_generic.poll_uvedit(context)
else ()
),
_defs_image_uv_sculpt.grab,
_defs_image_uv_sculpt.relax,
_defs_image_uv_sculpt.pinch,
],
'MASK': [
None,

@ -10,6 +10,7 @@
* General operations for brushes.
*/
#include "DNA_brush_enums.h"
#include "DNA_color_types.h"
#include "DNA_object_enums.h"
@ -93,6 +94,10 @@ float BKE_brush_curve_strength_clamped(const Brush *br, float p, float len);
/**
* Uses the brush curve control to find a strength value.
*/
float BKE_brush_curve_strength(eBrushCurvePreset preset,
const CurveMapping *cumap,
float distance,
float brush_radius);
float BKE_brush_curve_strength(const Brush *br, float p, float len);
/* Sampling. */

@ -96,7 +96,6 @@ enum class PaintMode : int8_t {
Texture3D = 3,
/** Image space (2D painting). */
Texture2D = 4,
SculptUV = 5,
GPencil = 6,
/* Grease Pencil Vertex Paint */
VertexGPencil = 7,
@ -111,8 +110,6 @@ enum class PaintMode : int8_t {
Invalid = 12,
};
#define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PaintMode::SculptUV)
/* overlay invalidation */
enum ePaintOverlayControlFlags {
PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY = 1,

@ -2531,20 +2531,24 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
}
}
float BKE_brush_curve_strength(const Brush *br, float p, const float len)
float BKE_brush_curve_strength(const eBrushCurvePreset preset,
const CurveMapping *cumap,
const float distance,
const float brush_radius)
{
float p = distance;
float strength = 1.0f;
if (p >= len) {
if (p >= brush_radius) {
return 0;
}
p = p / len;
p = p / brush_radius;
p = 1.0f - p;
switch (br->curve_preset) {
switch (preset) {
case BRUSH_CURVE_CUSTOM:
strength = BKE_curvemapping_evaluateF(br->curve, 0, 1.0f - p);
strength = BKE_curvemapping_evaluateF(cumap, 0, 1.0f - p);
break;
case BRUSH_CURVE_SHARP:
strength = p * p;
@ -2578,6 +2582,11 @@ float BKE_brush_curve_strength(const Brush *br, float p, const float len)
return strength;
}
float BKE_brush_curve_strength(const Brush *br, float p, const float len)
{
return BKE_brush_curve_strength(eBrushCurvePreset(br->curve_preset), br->curve, p, len);
}
float BKE_brush_curve_strength_clamped(const Brush *br, float p, const float len)
{
float strength = BKE_brush_curve_strength(br, p, len);

@ -346,9 +346,6 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode)
paint_tmp = (Paint *)&ts->imapaint;
paint_ptr = &paint_tmp;
break;
case PaintMode::SculptUV:
paint_ptr = (Paint **)&ts->uvsculpt;
break;
case PaintMode::GPencil:
paint_ptr = (Paint **)&ts->gp_paint;
break;
@ -392,8 +389,6 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return &ts->imapaint.paint;
case PaintMode::SculptUV:
return &ts->uvsculpt->paint;
case PaintMode::GPencil:
return &ts->gp_paint->paint;
case PaintMode::VertexGPencil:
@ -428,8 +423,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return rna_enum_brush_image_tool_items;
case PaintMode::SculptUV:
return rna_enum_brush_uv_sculpt_tool_items;
case PaintMode::GPencil:
return rna_enum_brush_gpencil_types_items;
case PaintMode::VertexGPencil:
@ -460,8 +453,6 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return "image_tool";
case PaintMode::SculptUV:
return "uv_sculpt_tool";
case PaintMode::GPencil:
return "gpencil_tool";
case PaintMode::VertexGPencil:
@ -492,7 +483,6 @@ const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const Pai
return BLT_I18NCONTEXT_ID_BRUSH;
case PaintMode::Vertex:
case PaintMode::Weight:
case PaintMode::SculptUV:
case PaintMode::VertexGPencil:
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:
@ -533,8 +523,6 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
return &ts->gp_weightpaint->paint;
case OB_MODE_SCULPT_CURVES:
return &ts->curves_sculpt->paint;
case OB_MODE_EDIT:
return ts->uvsculpt ? &ts->uvsculpt->paint : nullptr;
default:
break;
}
@ -563,9 +551,6 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
if (sima->mode == SI_MODE_PAINT) {
return &ts->imapaint.paint;
}
if (sima->mode == SI_MODE_UV) {
return &ts->uvsculpt->paint;
}
}
else {
return &ts->imapaint.paint;
@ -594,9 +579,6 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
if (sima->mode == SI_MODE_PAINT) {
return PaintMode::Texture2D;
}
if (sima->mode == SI_MODE_UV) {
return PaintMode::SculptUV;
}
}
else {
return PaintMode::Texture2D;
@ -624,8 +606,6 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
return PaintMode::Weight;
case OB_MODE_TEXTURE_PAINT:
return PaintMode::Texture3D;
case OB_MODE_EDIT:
return PaintMode::SculptUV;
case OB_MODE_SCULPT_CURVES:
return PaintMode::SculptCurves;
default:
@ -674,8 +654,6 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
switch (tref->mode) {
case SI_MODE_PAINT:
return PaintMode::Texture2D;
case SI_MODE_UV:
return PaintMode::SculptUV;
}
}
@ -721,10 +699,6 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
}
else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) {
paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool);
paint->runtime.ob_mode = OB_MODE_EDIT;
}
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
@ -762,8 +736,6 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
return offsetof(Brush, vertexpaint_tool);
case PaintMode::Weight:
return offsetof(Brush, weightpaint_tool);
case PaintMode::SculptUV:
return offsetof(Brush, uv_sculpt_tool);
case PaintMode::GPencil:
return offsetof(Brush, gpencil_tool);
case PaintMode::VertexGPencil:
@ -1104,8 +1076,6 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return OB_MODE_TEXTURE_PAINT;
case PaintMode::SculptUV:
return OB_MODE_EDIT;
case PaintMode::SculptCurves:
return OB_MODE_SCULPT_CURVES;
case PaintMode::GPencil:
@ -1140,7 +1110,6 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
(Paint *)ts->sculpt,
(Paint *)ts->vpaint,
(Paint *)ts->wpaint,
(Paint *)ts->uvsculpt,
(Paint *)ts->curves_sculpt,
(Paint *)&ts->imapaint));
#ifndef NDEBUG
@ -1182,10 +1151,6 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
GpWeightPaint *data = MEM_cnew<GpWeightPaint>(__func__);
paint = &data->paint;
}
else if ((UvSculpt **)r_paint == &ts->uvsculpt) {
UvSculpt *data = MEM_cnew<UvSculpt>(__func__);
paint = &data->paint;
}
else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) {
CurvesSculpt *data = MEM_cnew<CurvesSculpt>(__func__);
paint = &data->paint;
@ -1211,17 +1176,15 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
BKE_paint_ensure_from_paintmode(sce, mode);
/* If there's no brush, create one */
if (PAINT_MODE_HAS_BRUSH(mode)) {
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
}
copy_v3_v3_uchar(paint->paint_cursor_col, col);

@ -89,9 +89,6 @@ void BKE_paint_toolslots_init_from_main(Main *bmain)
if (ts->wpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->wpaint->paint);
}
if (ts->uvsculpt) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->uvsculpt->paint);
}
if (ts->gp_paint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_paint->paint);
}

@ -725,14 +725,6 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
}
toolsett_old->sculpt->gravity_object = gravity_object_old;
}
if (toolsett_old->uvsculpt) {
paint = toolsett->uvsculpt ? &toolsett->uvsculpt->paint : nullptr;
paint_old = &toolsett_old->uvsculpt->paint;
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
data,
do_undo_restore,
scene_foreach_paint(data, paint, do_undo_restore, reader, paint_old));
}
if (toolsett_old->gp_paint) {
paint = toolsett->gp_paint ? &toolsett->gp_paint->paint : nullptr;
paint_old = &toolsett_old->gp_paint->paint;
@ -1055,9 +1047,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
BKE_paint_blend_write(writer, &tos->sculpt->paint);
}
if (tos->uvsculpt) {
BLO_write_struct(writer, UvSculpt, tos->uvsculpt);
BKE_paint_blend_write(writer, &tos->uvsculpt->paint);
if (tos->uvsculpt.strength_curve) {
BKE_curvemapping_blend_write(writer, tos->uvsculpt.strength_curve);
}
if (tos->gp_paint) {
BLO_write_struct(writer, GpPaint, tos->gp_paint);
@ -1259,7 +1250,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->sculpt);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->vpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->wpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->uvsculpt);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_paint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_vertexpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_sculptpaint);
@ -1272,6 +1262,11 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
sce->toolsettings->particle.scene = nullptr;
sce->toolsettings->particle.object = nullptr;
sce->toolsettings->gp_sculpt.paintcursor = nullptr;
if (sce->toolsettings->uvsculpt.strength_curve) {
BLO_read_struct(reader, CurveMapping, &sce->toolsettings->uvsculpt.strength_curve);
BKE_curvemapping_blend_read(reader, sce->toolsettings->uvsculpt.strength_curve);
BKE_curvemapping_init(sce->toolsettings->uvsculpt.strength_curve);
}
if (sce->toolsettings->sculpt) {
BLO_read_struct(reader, CurveMapping, &sce->toolsettings->sculpt->automasking_cavity_curve);
@ -1671,9 +1666,9 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
BKE_curvemapping_init(ts->sculpt->automasking_cavity_curve_op);
}
}
if (ts->uvsculpt) {
ts->uvsculpt = static_cast<UvSculpt *>(MEM_dupallocN(ts->uvsculpt));
BKE_paint_copy(&ts->uvsculpt->paint, &ts->uvsculpt->paint, flag);
if (ts->uvsculpt.strength_curve) {
ts->uvsculpt.strength_curve = BKE_curvemapping_copy(ts->uvsculpt.strength_curve);
BKE_curvemapping_init(ts->uvsculpt.strength_curve);
}
if (ts->gp_paint) {
ts->gp_paint = static_cast<GpPaint *>(MEM_dupallocN(ts->gp_paint));
@ -1737,9 +1732,8 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_paint_free(&toolsettings->sculpt->paint);
MEM_freeN(toolsettings->sculpt);
}
if (toolsettings->uvsculpt) {
BKE_paint_free(&toolsettings->uvsculpt->paint);
MEM_freeN(toolsettings->uvsculpt);
if (toolsettings->uvsculpt.strength_curve) {
BKE_curvemapping_free(toolsettings->uvsculpt.strength_curve);
}
if (toolsettings->gp_paint) {
BKE_paint_free(&toolsettings->gp_paint->paint);

@ -797,9 +797,6 @@ static void do_version_curvemapping_walker(Main *bmain, void (*callback)(CurveMa
if (ts->sculpt) {
callback(ts->sculpt->paint.cavity_curve);
}
if (ts->uvsculpt) {
callback(ts->uvsculpt->paint.cavity_curve);
}
if (ts->gp_paint) {
callback(ts->gp_paint->paint.cavity_curve);
}

@ -3491,12 +3491,6 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 302, 6)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts->uv_relax_method == 0) {
ts->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN;
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
tool_settings->snap_flag_seq = tool_settings->snap_flag &

@ -2946,9 +2946,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
input_sample_values[2] = ts->curves_sculpt != nullptr ?
ts->curves_sculpt->paint.num_input_samples_deprecated :
1;
input_sample_values[3] = ts->uvsculpt != nullptr ?
ts->uvsculpt->paint.num_input_samples_deprecated :
1;
input_sample_values[4] = ts->gp_paint != nullptr ?
ts->gp_paint->paint.num_input_samples_deprecated :
@ -3227,6 +3224,18 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 23)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (!ts->uvsculpt.strength_curve) {
ts->uvsculpt.size = 50;
ts->uvsculpt.strength = 1.0f;
ts->uvsculpt.curve_preset = BRUSH_CURVE_SMOOTH;
ts->uvsculpt.strength_curve = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

@ -1215,7 +1215,6 @@ static bool paint_use_2d_cursor(PaintMode mode)
return false;
case PaintMode::Texture3D:
case PaintMode::Texture2D:
case PaintMode::SculptUV:
case PaintMode::VertexGPencil:
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:

@ -320,7 +320,9 @@ void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache,
/* `sculpt_uv.cc` */
void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_grab(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_relax(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_pinch(wmOperatorType *ot);
/* paint_utils.cc */

@ -1517,7 +1517,9 @@ void ED_operatortypes_paint()
WM_operatortype_append(PAINT_OT_weight_sample_group);
/* uv */
WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke);
WM_operatortype_append(SCULPT_OT_uv_sculpt_grab);
WM_operatortype_append(SCULPT_OT_uv_sculpt_relax);
WM_operatortype_append(SCULPT_OT_uv_sculpt_pinch);
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);

@ -40,12 +40,25 @@
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "paint_intern.hh"
#include "uvedit_intern.hh"
#include "UI_view2d.hh"
typedef enum eBrushUVSculptTool {
UV_SCULPT_TOOL_GRAB = 0,
UV_SCULPT_TOOL_RELAX = 1,
UV_SCULPT_TOOL_PINCH = 2,
} eBrushUVSculptTool;
enum {
UV_SCULPT_TOOL_RELAX_LAPLACIAN = 0,
UV_SCULPT_TOOL_RELAX_HC = 1,
UV_SCULPT_TOOL_RELAX_COTAN = 2,
};
/* When set, the UV element is on the boundary of the graph.
* i.e. Instead of a 2-dimensional laplace operator, use a 1-dimensional version.
* Visually, UV elements on the graph boundary appear as borders of the UV Island. */
@ -73,7 +86,7 @@ struct UVInitialStrokeElement {
/** index to unique UV. */
int uv;
/** Strength of brush on initial position. */
/** Strength on initial position. */
float strength;
/** initial UV position. */
@ -91,7 +104,7 @@ struct UVInitialStroke {
float init_coord[2];
};
/** Custom data for UV smoothing brush. */
/** Custom data for UV smoothing. */
struct UvSculptData {
/**
* Contains the first of each set of coincident UVs.
@ -111,14 +124,14 @@ struct UvSculptData {
/** data for initial stroke, used by tools like grab */
UVInitialStroke *initial_stroke;
/** Timer to be used for airbrush-type brush. */
/** Timer to be used for airbrush-type. */
wmTimer *timer;
/** To determine quickly adjacent UVs. */
UvElementMap *elementMap;
/** UV-smooth Paint for fast reference. */
Paint *uvsculpt;
/** UV-smooth for fast reference. */
UvSculpt *uvsculpt;
/** Tool to use. duplicating here to change if modifier keys are pressed. */
char tool;
@ -144,6 +157,18 @@ static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2])
uv[1] = clamp_f(uv[1], v, v + 1.0f);
}
static float calc_strength(const UvSculptData *sculptdata, float p, const float len)
{
float strength = BKE_brush_curve_strength(eBrushCurvePreset(sculptdata->uvsculpt->curve_preset),
sculptdata->uvsculpt->strength_curve,
p,
len);
CLAMP(strength, 0.0f, 1.0f);
return strength;
}
/*********** Improved Laplacian Relaxation Operator ************************/
/* original code by Raul Fernandez Hernandez "farsthary" *
* adapted to uv smoothing by Antony Riakiatakis *
@ -164,7 +189,6 @@ static void HC_relaxation_iteration_uv(UvSculptData *sculptdata,
float diff[2];
int i;
const float radius = sqrtf(radius_sq);
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
Temp_UVData *tmp_uvdata = (Temp_UVData *)MEM_callocN(
sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data");
@ -205,7 +229,7 @@ static void HC_relaxation_iteration_uv(UvSculptData *sculptdata,
if (dist <= radius_sq) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius);
strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
strength *
@ -250,7 +274,6 @@ static void laplacian_relaxation_iteration_uv(UvSculptData *sculptdata,
float diff[2];
int i;
const float radius = sqrtf(radius_sq);
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
Temp_UVData *tmp_uvdata = (Temp_UVData *)MEM_callocN(
sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data");
@ -288,7 +311,7 @@ static void laplacian_relaxation_iteration_uv(UvSculptData *sculptdata,
if (dist <= radius_sq) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius);
strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] +
strength * tmp_uvdata[i].p[0];
@ -412,14 +435,13 @@ static void relaxation_iteration_uv(UvSculptData *sculptdata,
add_weighted_edge(delta_buf, storage, head_prev, head_curr, *luv_prev, *luv_curr, weight_next);
}
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
for (int i = 0; i < sculptdata->totalUniqueUvs; i++) {
UvAdjacencyElement *adj_el = &sculptdata->uv[i];
if (adj_el->is_locked) {
continue; /* Locked UVs can't move. */
}
/* Is UV within brush's influence? */
/* Is UV within influence? */
float diff[2];
sub_v2_v2v2(diff, adj_el->uv, mouse_coord);
diff[1] /= aspect_ratio;
@ -427,8 +449,7 @@ static void relaxation_iteration_uv(UvSculptData *sculptdata,
if (dist_sq > radius_sq) {
continue;
}
const float strength = alpha * BKE_brush_curve_strength_clamped(
brush, sqrtf(dist_sq), sqrtf(radius_sq));
const float strength = alpha * calc_strength(sculptdata, sqrtf(dist_sq), sqrtf(radius_sq));
const float *delta_sum = delta_buf[adj_el->element - storage];
@ -460,15 +481,12 @@ static void uv_sculpt_stroke_apply(bContext *C,
const wmEvent *event,
Object *obedit)
{
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
UvSculptData *sculptdata = (UvSculptData *)op->customdata;
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
ToolSettings *toolsettings = CTX_data_tool_settings(C);
eBrushUVSculptTool tool = eBrushUVSculptTool(sculptdata->tool);
int invert = sculptdata->invert ? -1 : 1;
float alpha = BKE_brush_alpha_get(scene, brush);
float alpha = sculptdata->uvsculpt->strength;
float co[2];
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
@ -481,7 +499,7 @@ static void uv_sculpt_stroke_apply(bContext *C,
float zoomx, zoomy;
ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
const float radius = BKE_brush_size_get(scene, brush) / (width * zoomx);
const float radius = sculptdata->uvsculpt->size / (width * zoomx);
float aspectRatio = width / float(height);
/* We will compare squares to save some computation */
@ -505,7 +523,7 @@ static void uv_sculpt_stroke_apply(bContext *C,
if (dist <= radius_sq) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius);
strength = alpha * calc_strength(sculptdata, sqrtf(dist), radius);
normalize_v2(diff);
sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f;
@ -531,7 +549,7 @@ static void uv_sculpt_stroke_apply(bContext *C,
alpha,
radius_sq,
aspectRatio,
toolsettings->uv_relax_method);
RNA_enum_get(op->ptr, "relax_method"));
break;
}
case UV_SCULPT_TOOL_GRAB: {
@ -637,11 +655,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
UvSculptData *data = MEM_cnew<UvSculptData>(__func__);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
Brush *brush = BKE_paint_brush(&ts->uvsculpt->paint);
op->customdata = data;
BKE_curvemapping_init(brush->curve);
BKE_curvemapping_init(ts->uvsculpt.strength_curve);
if (!data) {
return nullptr;
@ -658,12 +675,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
int island_index = 0;
data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ?
UV_SCULPT_TOOL_RELAX :
eBrushUVSculptTool(brush->uv_sculpt_tool);
data->invert = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT) ? 1 : 0;
if (STREQ(op->type->idname, "SCULPT_OT_uv_sculpt_relax")) {
data->tool = UV_SCULPT_TOOL_RELAX;
}
else if (STREQ(op->type->idname, "SCULPT_OT_uv_sculpt_grab")) {
data->tool = UV_SCULPT_TOOL_GRAB;
}
else {
data->tool = UV_SCULPT_TOOL_PINCH;
}
data->invert = RNA_boolean_get(op->ptr, "use_invert");
data->uvsculpt = &ts->uvsculpt->paint;
data->uvsculpt = &ts->uvsculpt;
/* Winding was added to island detection in 5197aa04c6bd
* However the sculpt tools can flip faces, potentially creating orphaned islands.
@ -834,12 +857,8 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
/* Allocate initial selection for grab tool */
if (data->tool == UV_SCULPT_TOOL_GRAB) {
UvSculptData *sculptdata = (UvSculptData *)op->customdata;
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
float alpha = BKE_brush_alpha_get(scene, brush);
float radius = BKE_brush_size_get(scene, brush);
float alpha = data->uvsculpt->strength;
float radius = data->uvsculpt->size;
int width, height;
ED_space_image_get_size(sima, &width, &height);
float zoomx, zoomy;
@ -875,7 +894,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
float dist = dot_v2v2(diff, diff);
if (dist <= radius_sq) {
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius);
strength = alpha * calc_strength(data, sqrtf(dist), radius);
data->initial_stroke->initialSelection[counter].uv = i;
data->initial_stroke->initialSelection[counter].strength = strength;
@ -946,57 +965,78 @@ static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *ev
return OPERATOR_RUNNING_MODAL;
}
static bool uv_sculpt_stroke_poll(bContext *C)
static void register_common_props(wmOperatorType *ot)
{
if (ED_operator_uvedit_space_image(C)) {
/* While these values could be initialized on demand,
* the only case this would be useful is running from the operator search popup.
* This is such a corner case that it's simpler to check a brush has already been created
* (something the tool system ensures). */
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
Brush *brush = BKE_paint_brush(&ts->uvsculpt->paint);
if (brush != nullptr) {
return true;
}
}
return false;
}
void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot)
{
static const EnumPropertyItem stroke_mode_items[] = {
{BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"},
{BRUSH_STROKE_INVERT,
"INVERT",
0,
"Invert",
"Invert action of brush for duration of stroke"},
{BRUSH_STROKE_SMOOTH,
"RELAX",
0,
"Relax",
"Switch brush to relax mode for duration of stroke"},
{0},
};
/* identifiers */
ot->name = "Sculpt UVs";
ot->description = "Sculpt UVs using a brush";
ot->idname = "SCULPT_OT_uv_sculpt_stroke";
/* api callbacks */
ot->invoke = uv_sculpt_stroke_invoke;
ot->modal = uv_sculpt_stroke_modal;
ot->poll = uv_sculpt_stroke_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
PropertyRNA *prop;
prop = RNA_def_enum(
ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, "Mode", "Stroke Mode");
prop = RNA_def_boolean(
ot->srna, "use_invert", false, "Invert", "Invert action for the duration of the stroke");
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE));
}
void SCULPT_OT_uv_sculpt_grab(wmOperatorType *ot)
{
ot->name = "Grab UVs";
ot->description = "Grab UVs";
ot->idname = "SCULPT_OT_uv_sculpt_grab";
ot->invoke = uv_sculpt_stroke_invoke;
ot->modal = uv_sculpt_stroke_modal;
ot->poll = ED_operator_uvedit_space_image;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
register_common_props(ot);
}
void SCULPT_OT_uv_sculpt_relax(wmOperatorType *ot)
{
ot->name = "Relax UVs";
ot->description = "Relax UVs";
ot->idname = "SCULPT_OT_uv_sculpt_relax";
ot->invoke = uv_sculpt_stroke_invoke;
ot->modal = uv_sculpt_stroke_modal;
ot->poll = ED_operator_uvedit_space_image;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
register_common_props(ot);
static const EnumPropertyItem relax_method_items[] = {
{UV_SCULPT_TOOL_RELAX_LAPLACIAN,
"LAPLACIAN",
0,
"Laplacian",
"Use Laplacian method for relaxation"},
{UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
{UV_SCULPT_TOOL_RELAX_COTAN,
"COTAN",
0,
"Geometry",
"Use Geometry (cotangent) relaxation, making UVs follow the underlying 3D geometry"},
{0, nullptr, 0, nullptr, nullptr},
};
RNA_def_enum(ot->srna,
"relax_method",
relax_method_items,
CURVE_PRESET_SMOOTH,
"Relax Method",
"Algorithm used for UV relaxation");
}
void SCULPT_OT_uv_sculpt_pinch(wmOperatorType *ot)
{
ot->name = "Pinch UVs";
ot->description = "Pinch UVs";
ot->idname = "SCULPT_OT_uv_sculpt_pinch";
ot->invoke = uv_sculpt_stroke_invoke;
ot->modal = uv_sculpt_stroke_modal;
ot->poll = ED_operator_uvedit_space_image;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
register_common_props(ot);
}

@ -330,10 +330,8 @@ bool space_image_main_region_poll(bContext *C)
static bool space_image_main_area_not_uv_brush_poll(bContext *C)
{
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *toolsettings = scene->toolsettings;
if (sima && !toolsettings->uvsculpt && (CTX_data_edit_object(C) == nullptr)) {
if (sima && (CTX_data_edit_object(C) == nullptr)) {
return true;
}

@ -483,13 +483,6 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_DISPLACEMENT_SMEAR = 32,
} eBrushSculptTool;
/** #Brush.uv_sculpt_tool */
typedef enum eBrushUVSculptTool {
UV_SCULPT_TOOL_GRAB = 0,
UV_SCULPT_TOOL_RELAX = 1,
UV_SCULPT_TOOL_PINCH = 2,
} eBrushUVSculptTool;
/* Brush.curves_sculpt_tool. */
typedef enum eBrushCurvesSculptTool {
CURVES_SCULPT_TOOL_COMB = 0,

@ -273,8 +273,6 @@ typedef struct Brush {
/** Active sculpt tool. */
char sculpt_tool;
/** Active sculpt tool. */
char uv_sculpt_tool;
/** Active vertex paint. */
char vertexpaint_tool;
/** Active weight paint. */
@ -293,7 +291,7 @@ typedef struct Brush {
char gpencil_weight_tool;
/** Active curves sculpt tool (#eBrushCurvesSculptTool). */
char curves_sculpt_tool;
char _pad1[5];
char _pad1[6];
float autosmooth_factor;

@ -408,8 +408,7 @@
\
/* UV painting */ \
.uv_sculpt_settings = 0, \
.uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN, \
\
\
/* Placement */ \
.snap_mode_tools = SCE_SNAP_TO_GEOM,\
.plane_axis = 2,\

@ -860,13 +860,6 @@ enum {
UV_SCULPT_ALL_ISLANDS = 2,
};
/** #ToolSettings::uv_relax_method */
enum {
UV_SCULPT_TOOL_RELAX_LAPLACIAN = 1,
UV_SCULPT_TOOL_RELAX_HC = 2,
UV_SCULPT_TOOL_RELAX_COTAN = 3,
};
/* Stereo Flags. */
#define STEREO_RIGHT_NAME "right"
#define STEREO_LEFT_NAME "left"
@ -1100,7 +1093,11 @@ typedef struct CurvesSculpt {
} CurvesSculpt;
typedef struct UvSculpt {
Paint paint;
struct CurveMapping *strength_curve;
int size;
float strength;
int8_t curve_preset; /* #eBrushCurvePreset. */
char _pad[7];
} UvSculpt;
/** Grease pencil drawing brushes. */
@ -1513,7 +1510,7 @@ typedef struct ToolSettings {
VPaint *wpaint;
Sculpt *sculpt;
/** UV smooth. */
UvSculpt *uvsculpt;
UvSculpt uvsculpt;
/** Gpencil paint. */
GpPaint *gp_paint;
/** Gpencil vertex paint. */
@ -1665,10 +1662,11 @@ typedef struct ToolSettings {
/* UV painting. */
char uv_sculpt_settings;
char uv_relax_method;
char workspace_tool_type;
char _pad5[1];
/**
* XXX: these `sculpt_paint_*` fields are deprecated, use the
* unified_paint_settings field instead!

@ -109,7 +109,6 @@ DEF_ENUM(rna_enum_operator_property_tag_items)
DEF_ENUM(rna_enum_brush_automasking_flag_items)
DEF_ENUM(rna_enum_brush_sculpt_tool_items)
DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items)
DEF_ENUM(rna_enum_brush_vertex_tool_items)
DEF_ENUM(rna_enum_brush_weight_tool_items)
DEF_ENUM(rna_enum_brush_gpencil_types_items)
@ -118,6 +117,7 @@ DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items)
DEF_ENUM(rna_enum_brush_gpencil_weight_types_items)
DEF_ENUM(rna_enum_brush_curves_sculpt_tool_items)
DEF_ENUM(rna_enum_brush_image_tool_items)
DEF_ENUM(rna_enum_brush_curve_preset_items)
DEF_ENUM(rna_enum_grease_pencil_selectmode_items)

@ -94,6 +94,20 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_texture_mode_items
};
#endif
const EnumPropertyItem rna_enum_brush_curve_preset_items[] = {
{BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""},
{BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""},
{BRUSH_CURVE_SMOOTHER, "SMOOTHER", ICON_SMOOTHCURVE, "Smoother", ""},
{BRUSH_CURVE_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", ""},
{BRUSH_CURVE_ROOT, "ROOT", ICON_ROOTCURVE, "Root", ""},
{BRUSH_CURVE_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", ""},
{BRUSH_CURVE_LIN, "LIN", ICON_LINCURVE, "Linear", ""},
{BRUSH_CURVE_POW4, "POW4", ICON_SHARPCURVE, "Sharper", ""},
{BRUSH_CURVE_INVSQUARE, "INVSQUARE", ICON_INVERSESQUARECURVE, "Inverse Square", ""},
{BRUSH_CURVE_CONSTANT, "CONSTANT", ICON_NOCURVE, "Constant", ""},
{0, nullptr, 0, nullptr, nullptr},
};
/* Note: we don't actually turn these into a single enum bit-mask property,
* instead we construct individual boolean properties. */
const EnumPropertyItem rna_enum_brush_automasking_flag_items[] = {
@ -185,13 +199,6 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_brush_uv_sculpt_tool_items[] = {
{UV_SCULPT_TOOL_GRAB, "GRAB", 0, "Grab", "Grab UVs"},
{UV_SCULPT_TOOL_RELAX, "RELAX", 0, "Relax", "Relax UVs"},
{UV_SCULPT_TOOL_PINCH, "PINCH", 0, "Pinch", "Pinch UVs"},
{0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem rna_enum_brush_vertex_tool_items[] = {
{VPAINT_TOOL_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""},
{VPAINT_TOOL_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
@ -2419,20 +2426,6 @@ static void rna_def_brush(BlenderRNA *brna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem brush_curve_preset_items[] = {
{BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""},
{BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""},
{BRUSH_CURVE_SMOOTHER, "SMOOTHER", ICON_SMOOTHCURVE, "Smoother", ""},
{BRUSH_CURVE_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", ""},
{BRUSH_CURVE_ROOT, "ROOT", ICON_ROOTCURVE, "Root", ""},
{BRUSH_CURVE_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", ""},
{BRUSH_CURVE_LIN, "LIN", ICON_LINCURVE, "Linear", ""},
{BRUSH_CURVE_POW4, "POW4", ICON_SHARPCURVE, "Sharper", ""},
{BRUSH_CURVE_INVSQUARE, "INVSQUARE", ICON_INVERSESQUARECURVE, "Inverse Square", ""},
{BRUSH_CURVE_CONSTANT, "CONSTANT", ICON_NOCURVE, "Constant", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem brush_deformation_target_items[] = {
{BRUSH_DEFORM_TARGET_GEOMETRY,
"GEOMETRY",
@ -2619,11 +2612,6 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_BRUSH);
RNA_def_property_update(prop, 0, "rna_Brush_update_and_reset_icon");
prop = RNA_def_property(srna, "uv_sculpt_tool", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_brush_uv_sculpt_tool_items);
RNA_def_property_ui_text(prop, "UV Sculpt Tool", "");
RNA_def_property_update(prop, 0, "rna_Brush_update_and_reset_icon");
prop = RNA_def_property(srna, "vertex_tool", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "vertexpaint_tool");
RNA_def_property_enum_items(prop, rna_enum_brush_vertex_tool_items);
@ -2703,7 +2691,7 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "curve_preset", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_curve_preset_items);
RNA_def_property_enum_items(prop, rna_enum_brush_curve_preset_items);
RNA_def_property_ui_text(prop, "Curve Preset", "");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVES); /* Abusing id_curves :/ */
RNA_def_property_update(prop, 0, "rna_Brush_update");

@ -85,23 +85,6 @@ const EnumPropertyItem rna_enum_exr_codec_items[] = {
};
#endif
#ifndef RNA_RUNTIME
static const EnumPropertyItem uv_sculpt_relaxation_items[] = {
{UV_SCULPT_TOOL_RELAX_LAPLACIAN,
"LAPLACIAN",
0,
"Laplacian",
"Use Laplacian method for relaxation"},
{UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
{UV_SCULPT_TOOL_RELAX_COTAN,
"COTAN",
0,
"Geometry",
"Use Geometry (cotangent) relaxation, making UVs follow the underlying 3D geometry"},
{0, nullptr, 0, nullptr, nullptr},
};
#endif
const EnumPropertyItem rna_enum_snap_source_items[] = {
{SCE_SNAP_SOURCE_CLOSEST, "CLOSEST", 0, "Closest", "Snap closest point onto target"},
{SCE_SNAP_SOURCE_CENTER, "CENTER", 0, "Center", "Snap transformation center onto target"},
@ -3328,12 +3311,6 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY);
RNA_def_property_ui_text(prop, "Sculpt All Islands", "Brush operates on all islands");
prop = RNA_def_property(srna, "uv_relax_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "uv_relax_method");
RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY);
RNA_def_property_enum_items(prop, uv_sculpt_relaxation_items);
RNA_def_property_ui_text(prop, "Relaxation Method", "Algorithm used for UV relaxation");
prop = RNA_def_property(srna, "lock_object_mode", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "object_flag", SCE_OBJECT_MODE_LOCK);
RNA_def_property_flag(prop, PROP_DEG_SYNC_ONLY);

@ -110,6 +110,7 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
# include "MEM_guardedalloc.h"
# include "BKE_collection.hh"
# include "BKE_colortools.hh"
# include "BKE_context.hh"
# include "BKE_gpencil_legacy.h"
# include "BKE_object.hh"
@ -323,12 +324,6 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value)
}
mode = OB_MODE_SCULPT;
}
else if (paint_contains_brush_slot(&ts->uvsculpt->paint, tslot, &slot_index)) {
if (slot_index != brush->uv_sculpt_tool) {
return false;
}
mode = OB_MODE_EDIT;
}
else if (paint_contains_brush_slot(&ts->vpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->vertexpaint_tool) {
return false;
@ -518,6 +513,18 @@ static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA * /*ptr*/)
}
}
static void rna_UvSculpt_curve_preset_set(PointerRNA *ptr, int value)
{
Scene *scene = reinterpret_cast<Scene *>(ptr->owner_id);
if (value == BRUSH_CURVE_CUSTOM) {
if (!scene->toolsettings->uvsculpt.strength_curve) {
scene->toolsettings->uvsculpt.strength_curve = BKE_curvemapping_add(
1, 0.0f, 0.0f, 1.0f, 1.0f);
}
}
scene->toolsettings->uvsculpt.curve_preset = int8_t(value);
}
/** \name Paint mode settings
* \{ */
@ -996,10 +1003,34 @@ static void rna_def_sculpt(BlenderRNA *brna)
static void rna_def_uv_sculpt(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "UvSculpt", "Paint");
RNA_def_struct_path_func(srna, "rna_UvSculpt_path");
RNA_def_struct_ui_text(srna, "UV Sculpting", "");
prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
RNA_def_property_ui_range(prop, 1, 500, 1, 1);
RNA_def_property_range(prop, 1, 5000);
RNA_def_property_ui_text(prop, "Size", "");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Strength", "");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
prop = RNA_def_property(srna, "strength_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "CurveMapping");
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, nullptr);
RNA_def_property_ui_text(prop, "Strength Curve", "");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
prop = RNA_def_property(srna, "curve_preset", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_brush_curve_preset_items);
RNA_def_property_ui_text(prop, "Strength Curve Preset", "");
RNA_def_property_enum_funcs(prop, nullptr, "rna_UvSculpt_curve_preset_set", nullptr);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr);
}
static void rna_def_gp_paint(BlenderRNA *brna)