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:
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py9
-rw-r--r--source/blender/blenkernel/BKE_paint.h4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c44
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h21
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c171
-rw-r--r--source/blender/makesdna/DNA_scene_types.h11
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c20
7 files changed, 262 insertions, 18 deletions
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 31f25d016a6..5831aa52cc1 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -261,9 +261,15 @@ class _defs_annotate:
class _defs_transform:
+ def draw_transform_sculpt_tool_settings(context, layout):
+ if context.mode != 'SCULPT':
+ return
+ layout.prop(context.tool_settings.sculpt, "transform_mode")
+
@ToolDef.from_fn
def translate():
def draw_settings(context, layout, _tool):
+ _defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1)
return dict(
idname="builtin.move",
@@ -279,6 +285,7 @@ class _defs_transform:
@ToolDef.from_fn
def rotate():
def draw_settings(context, layout, _tool):
+ _defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2)
return dict(
idname="builtin.rotate",
@@ -294,6 +301,7 @@ class _defs_transform:
@ToolDef.from_fn
def scale():
def draw_settings(context, layout, _tool):
+ _defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3)
return dict(
idname="builtin.scale",
@@ -349,6 +357,7 @@ class _defs_transform:
props = tool.gizmo_group_properties("VIEW3D_GGT_xform_gizmo")
layout.prop(props, "drag_action")
+ _defs_transform.draw_transform_sculpt_tool_settings(context, layout)
_template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1)
return dict(
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 9e0ea5e13d5..a47e4a24f75 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -613,6 +613,10 @@ typedef struct SculptSession {
float init_pivot_rot[4];
float init_pivot_scale[3];
+ float prev_pivot_pos[3];
+ float prev_pivot_rot[4];
+ float prev_pivot_scale[3];
+
union {
struct {
struct SculptVertexPaintGeomMap gmap;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 2c6c4c82676..d9209b65276 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -3535,15 +3535,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
Object *ob = data->ob;
-
- /* These brushes start from original coordinates. */
- const bool use_orco = ELEM(data->brush->sculpt_tool,
- SCULPT_TOOL_GRAB,
- SCULPT_TOOL_ROTATE,
- SCULPT_TOOL_THUMB,
- SCULPT_TOOL_ELASTIC_DEFORM,
- SCULPT_TOOL_BOUNDARY,
- SCULPT_TOOL_POSE);
+ const bool use_orco = data->use_proxies_orco;
PBVHVertexIter vd;
PBVHProxyNode *proxies;
@@ -3598,17 +3590,49 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
return;
}
+ /* First line is tools that don't support proxies. */
+ const bool use_orco = ELEM(brush->sculpt_tool,
+ SCULPT_TOOL_GRAB,
+ SCULPT_TOOL_ROTATE,
+ SCULPT_TOOL_THUMB,
+ SCULPT_TOOL_ELASTIC_DEFORM,
+ SCULPT_TOOL_BOUNDARY,
+ SCULPT_TOOL_POSE);
+
+ BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .use_proxies_orco = use_orco,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
+ MEM_SAFE_FREE(nodes);
+}
+
+void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ PBVHNode **nodes;
+ int totnode;
+
BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
- .brush = brush,
.nodes = nodes,
+ .use_proxies_orco = false,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings);
+
MEM_SAFE_FREE(nodes);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 5fa115ed629..0693b445fe5 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -247,6 +247,10 @@ typedef struct SculptThreadedTaskData {
float (*mat)[4];
float (*vertCos)[3];
+ /* When true, the displacement stored in the proxies will be aplied to the original coordinates
+ * instead of to the current coordinates. */
+ bool use_proxies_orco;
+
/* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to
* the stroke direction are needed. */
float (*stroke_xz)[3];
@@ -290,6 +294,10 @@ typedef struct SculptThreadedTaskData {
bool mask_expand_create_face_set;
float transform_mats[8][4][4];
+ float elastic_transform_mat[4][4];
+ float elastic_transform_pivot[3];
+ float elastic_transform_pivot_init[3];
+ float elastic_transform_radius;
/* Boundary brush */
float boundary_deform_strength;
@@ -372,6 +380,14 @@ typedef enum SculptFilterOrientation {
SCULPT_FILTER_ORIENTATION_VIEW = 2,
} SculptFilterOrientation;
+/* Defines how transform tools are going to apply its displacement. */
+typedef enum SculptTransformDisplacementMode {
+ /* Displaces the elements from their original coordinates. */
+ SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL = 0,
+ /* Displaces the elements incrementally from their previous position. */
+ SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL = 1,
+} SculptTransformDisplacementMode;
+
#define SCULPT_CLAY_STABILIZER_LEN 10
typedef struct AutomaskingSettings {
@@ -440,6 +456,8 @@ typedef struct FilterCache {
int active_face_set;
+ SculptTransformDisplacementMode transform_displacement_mode;
+
/* Auto-masking. */
AutomaskingCache *automasking;
@@ -1137,12 +1155,15 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v);
*/
bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v);
+void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob);
+
/**
* Initialize a point-in-brush test with a given falloff shape.
*
* \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE.
* \return The brush falloff function.
*/
+
SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss,
SculptBrushTest *test,
char falloff_shape);
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index b3616254b26..5096d5fe3fd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -16,6 +16,7 @@
#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_kelvinlet.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
@@ -33,6 +34,7 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
+#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -54,6 +56,10 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale);
+ copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
+ copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
+ copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
+
SCULPT_undo_push_begin(ob, "Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
@@ -61,10 +67,18 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
SCULPT_vertex_random_access_ensure(ss);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
+
+ if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) {
+ ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL;
+ }
+ else {
+ ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL;
+ }
}
static void sculpt_transform_matrices_init(SculptSession *ss,
const char symm,
+ const SculptTransformDisplacementMode t_mode,
float r_transform_mats[8][4][4])
{
@@ -73,9 +87,18 @@ static void sculpt_transform_matrices_init(SculptSession *ss,
transform_mat[4][4];
float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3];
- copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
- copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
- copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
+ switch (t_mode) {
+ case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
+ copy_v3_v3(start_pivot_pos, ss->init_pivot_pos);
+ copy_v4_v4(start_pivot_rot, ss->init_pivot_rot);
+ copy_v3_v3(start_pivot_scale, ss->init_pivot_scale);
+ break;
+ case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
+ copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos);
+ copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot);
+ copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale);
+ break;
+ }
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
ePaintSymmetryAreas v_symm = i;
@@ -134,16 +157,26 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float *start_co;
float transformed_co[3], orig_co[3], disp[3];
float fade = vd.mask ? *vd.mask : 0.0f;
copy_v3_v3(orig_co, orig_data.co);
char symm_area = SCULPT_get_vertex_symm_area(orig_co);
- copy_v3_v3(transformed_co, orig_co);
+ switch (ss->filter_cache->transform_displacement_mode) {
+ case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL:
+ start_co = orig_co;
+ break;
+ case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL:
+ start_co = vd.co;
+ break;
+ }
+
+ copy_v3_v3(transformed_co, start_co);
mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co);
- sub_v3_v3v3(disp, transformed_co, orig_co);
+ sub_v3_v3v3(disp, transformed_co, start_co);
mul_v3_fl(disp, 1.0f - fade);
- add_v3_v3v3(vd.co, orig_co, disp);
+ add_v3_v3v3(vd.co, start_co, disp);
if (vd.mvert) {
BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
@@ -165,7 +198,8 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
.nodes = ss->filter_cache->nodes,
};
- sculpt_transform_matrices_init(ss, symm, data.transform_mats);
+ sculpt_transform_matrices_init(
+ ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
/* Regular transform applies all symmetry passes at once as it is split by symmetry areas
* (each vertex can only be transformed once by the transform matrix of its area). */
@@ -175,6 +209,98 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob)
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
}
+static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
+ const int i,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ PBVHNode *node = data->nodes[i];
+
+ float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co;
+
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+
+ KelvinletParams params;
+ /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
+ * preservation like in the elastic deform brushes. Setting them to the same default as elastic
+ * deform triscale grab because they work well in most cases. */
+ const float force = 1.0f;
+ const float shear_modulus = 1.0f;
+ const float poisson_ratio = 0.4f;
+ BKE_kelvinlet_init_params(
+ &params, data->elastic_transform_radius, force, shear_modulus, poisson_ratio);
+
+ SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float transformed_co[3], orig_co[3], disp[3];
+ const float fade = vd.mask ? *vd.mask : 0.0f;
+ copy_v3_v3(orig_co, orig_data.co);
+
+ copy_v3_v3(transformed_co, vd.co);
+ mul_m4_v3(data->elastic_transform_mat, transformed_co);
+ sub_v3_v3v3(disp, transformed_co, vd.co);
+
+ float final_disp[3];
+ BKE_kelvinlet_grab_triscale(final_disp, &params, vd.co, data->elastic_transform_pivot, disp);
+ mul_v3_fl(final_disp, 20.0f * (1.0f - fade));
+
+ copy_v3_v3(proxy[vd.i], final_disp);
+
+ if (vd.mvert) {
+ BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BKE_pbvh_node_mark_update(node);
+}
+
+static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius)
+{
+ BLI_assert(ss->filter_cache->transform_displacement_mode ==
+ SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL);
+
+ SculptSession *ss = ob->sculpt;
+ const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .nodes = ss->filter_cache->nodes,
+ .elastic_transform_radius = transform_radius,
+ };
+
+ sculpt_transform_matrices_init(
+ ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats);
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
+
+ /* Elastic transform needs to apply all transform matrices to all vertices and then combine the
+ * displacement proxies as all vertices are modified by all symmetry passes. */
+ for (ePaintSymmetryFlags symmpass = 0; symmpass <= symm; symmpass++) {
+ if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) {
+ flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass);
+ flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
+
+ printf(
+ "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]);
+
+ const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot);
+ copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]);
+ BLI_task_parallel_range(
+ 0, ss->filter_cache->totnode, &data, sculpt_elastic_transform_task_cb, &settings);
+ }
+ }
+ SCULPT_combine_transform_proxies(sd, ob);
+}
+
void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -184,7 +310,36 @@ void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob)
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- sculpt_transform_all_vertices(sd, ob);
+ switch (sd->transform_mode) {
+ case SCULPT_TRANSFORM_MODE_ALL_VERTICES: {
+ sculpt_transform_all_vertices(sd, ob);
+ break;
+ }
+ case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: {
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ Scene *scene = CTX_data_scene(C);
+ float transform_radius;
+
+ if (BKE_brush_use_locked_size(scene, brush)) {
+ transform_radius = BKE_brush_unprojected_radius_get(scene, brush);
+ }
+ else {
+ ViewContext vc;
+
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+
+ transform_radius = paint_calc_object_space_radius(
+ &vc, ss->init_pivot_pos, BKE_brush_size_get(scene, brush));
+ }
+
+ sculpt_transform_radius_elastic(sd, ob, transform_radius);
+ break;
+ }
+ }
+
+ copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos);
+ copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
+ copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
if (ss->deform_modifiers_active || ss->shapekey_active) {
SCULPT_flush_stroke_deform(sd, ob, true);
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 1be27e0354c..47463638706 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -991,6 +991,9 @@ typedef struct Sculpt {
// float pivot[3]; XXX not used?
int flags;
+ /* Transform tool. */
+ int transform_mode;
+
int automasking_flags;
/* Control tablet input */
@@ -1011,6 +1014,8 @@ typedef struct Sculpt {
float constant_detail;
float detail_percent;
+ char _pad[4];
+
struct Object *gravity_object;
} Sculpt;
@@ -2279,6 +2284,12 @@ typedef enum eSculptFlags {
SCULPT_HIDE_FACE_SETS = (1 << 17),
} eSculptFlags;
+/* Sculpt.transform_mode */
+typedef enum eSculptTransformMode {
+ SCULPT_TRANSFORM_MODE_ALL_VERTICES = 0,
+ SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC = 1,
+} eSculptTrasnformMode;
+
/** PaintModeSettings.mode */
typedef enum ePaintCanvasSource {
/** Paint on the active node of the active material slot. */
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index ab5e4c2f0d5..3de9e632ff6 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -774,6 +774,20 @@ static void rna_def_sculpt(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem sculpt_transform_mode_items[] = {
+ {SCULPT_TRANSFORM_MODE_ALL_VERTICES,
+ "ALL_VERTICES",
+ 0,
+ "All Vertices",
+ "Applies the transformation to all vertices in the mesh"},
+ {SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC,
+ "RADIUS_ELASTIC",
+ 0,
+ "Elastic",
+ "Applies the transformation simulating elasticity using the radius of the cursor"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
StructRNA *srna;
PropertyRNA *prop;
@@ -912,6 +926,12 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Gravity", "Amount of gravity after each dab");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+ prop = RNA_def_property(srna, "transform_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, sculpt_transform_mode_items);
+ RNA_def_property_ui_text(
+ prop, "Transform Mode", "How the transformation is going to be applied to the target");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
+
prop = RNA_def_property(srna, "gravity_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(