Sculpt: Grab Active Vertex and Dynamic Mesh Preview
Grab active vertex snaps the maximum strength of the grab brush to the highlighted active vertex, making it easier to manipulate low poly models or meshes with subdivision surfaces. Dynamic Mesh Preview generates a list of connected vertices from the active vertex and draws them from the cursor code. This helps to visualize the real geometry the user is manipulating from sculpt mode when there are active modifiers. Reviewed By: brecht Differential Revision: https://developer.blender.org/D5646
This commit is contained in:
parent
e0f7ada0d2
commit
a3e7440cfd
|
@ -380,6 +380,11 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
|
|||
row = col.row()
|
||||
row.prop(brush, "use_automasking_topology")
|
||||
|
||||
if brush.sculpt_tool == 'GRAB':
|
||||
col.separator()
|
||||
row = col.row()
|
||||
row.prop(brush, "grab_active_vertex")
|
||||
|
||||
# topology_rake_factor
|
||||
if (
|
||||
capabilities.has_topology_rake and
|
||||
|
|
|
@ -269,6 +269,10 @@ typedef struct SculptSession {
|
|||
|
||||
float pivot_pos[3];
|
||||
|
||||
/* Dynamic mesh preview */
|
||||
int *preview_vert_index_list;
|
||||
int preview_vert_index_count;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct SculptVertexPaintGeomMap gmap;
|
||||
|
|
|
@ -1060,6 +1060,10 @@ void BKE_sculptsession_free(Object *ob)
|
|||
MEM_freeN(ss->deform_imats);
|
||||
}
|
||||
|
||||
if (ss->preview_vert_index_list) {
|
||||
MEM_freeN(ss->preview_vert_index_list);
|
||||
}
|
||||
|
||||
BKE_sculptsession_free_vwpaint_data(ob->sculpt);
|
||||
|
||||
MEM_freeN(ss);
|
||||
|
|
|
@ -1180,6 +1180,20 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr,
|
|||
}
|
||||
}
|
||||
|
||||
static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession *ss)
|
||||
{
|
||||
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f);
|
||||
GPU_depth_test(true);
|
||||
GPU_line_width(1.0f);
|
||||
if (ss->preview_vert_index_count > 0) {
|
||||
immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count);
|
||||
for (int i = 0; i < ss->preview_vert_index_count; i++) {
|
||||
immVertex3fv(gpuattr, sculpt_vertex_co_get(ss, ss->preview_vert_index_list[i]));
|
||||
}
|
||||
immEnd();
|
||||
}
|
||||
}
|
||||
|
||||
static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -1348,6 +1362,17 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
|
|||
imm_draw_circle_wire_3d(pos, 0, 0, rds, 40);
|
||||
GPU_matrix_pop();
|
||||
|
||||
/* Update and draw dynamic mesh preview lines */
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_mul(vc.obact->obmat);
|
||||
if (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush->flag2 & BRUSH_GRAB_ACTIVE_VERTEX) {
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) {
|
||||
sculpt_geometry_preview_lines_update(C, ss, rds);
|
||||
sculpt_geometry_preview_lines_draw(pos, ss);
|
||||
}
|
||||
}
|
||||
GPU_matrix_pop();
|
||||
|
||||
GPU_matrix_pop_projection();
|
||||
|
||||
wmWindowViewport(win);
|
||||
|
@ -1370,6 +1395,27 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
|
|||
add_v3_v3(cursor_location, ss->cache->grab_delta);
|
||||
}
|
||||
cursor_draw_point_with_symmetry(pos, ar, cursor_location, sd, vc.obact, ss->cache->radius);
|
||||
|
||||
/* Draw cached dynamic mesh preview lines */
|
||||
if (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush->flag2 & BRUSH_GRAB_ACTIVE_VERTEX) {
|
||||
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->modifiers_active) {
|
||||
GPU_matrix_push_projection();
|
||||
ED_view3d_draw_setup_view(CTX_wm_window(C),
|
||||
CTX_data_depsgraph_pointer(C),
|
||||
CTX_data_scene(C),
|
||||
ar,
|
||||
CTX_wm_view3d(C),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_mul(vc.obact->obmat);
|
||||
sculpt_geometry_preview_lines_draw(pos, ss);
|
||||
GPU_matrix_pop();
|
||||
GPU_matrix_pop_projection();
|
||||
}
|
||||
}
|
||||
|
||||
wmWindowViewport(win);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "BLI_gsqueue.h"
|
||||
#include "BLI_stack.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_stack.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_ghash.h"
|
||||
|
||||
|
@ -99,7 +100,7 @@
|
|||
|
||||
/* Do not use these functions while working with PBVH_GRIDS data in SculptSession */
|
||||
|
||||
static float *sculpt_vertex_co_get(SculptSession *ss, int index)
|
||||
float *sculpt_vertex_co_get(SculptSession *ss, int index)
|
||||
{
|
||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||
case PBVH_FACES:
|
||||
|
@ -5902,7 +5903,13 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||
float grab_location[3], imat[4][4], delta[3], loc[3];
|
||||
|
||||
if (cache->first_time) {
|
||||
copy_v3_v3(cache->orig_grab_location, cache->true_location);
|
||||
if (tool == SCULPT_TOOL_GRAB && brush->flag2 & BRUSH_GRAB_ACTIVE_VERTEX) {
|
||||
copy_v3_v3(cache->orig_grab_location,
|
||||
sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(cache->orig_grab_location, cache->true_location);
|
||||
}
|
||||
}
|
||||
else if (tool == SCULPT_TOOL_SNAKE_HOOK) {
|
||||
add_v3_v3(cache->true_location, cache->grab_delta);
|
||||
|
@ -5955,7 +5962,12 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
|
|||
copy_v3_v3(cache->old_grab_location, grab_location);
|
||||
|
||||
if (tool == SCULPT_TOOL_GRAB) {
|
||||
copy_v3_v3(cache->anchored_location, cache->true_location);
|
||||
if (brush->flag2 & BRUSH_GRAB_ACTIVE_VERTEX) {
|
||||
copy_v3_v3(cache->anchored_location, cache->orig_grab_location);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(cache->anchored_location, cache->true_location);
|
||||
}
|
||||
}
|
||||
else if (tool == SCULPT_TOOL_ELASTIC_DEFORM) {
|
||||
copy_v3_v3(cache->anchored_location, cache->true_location);
|
||||
|
@ -8995,6 +9007,72 @@ static void SCULPT_OT_mask_expand(wmOperatorType *ot)
|
|||
2000);
|
||||
}
|
||||
|
||||
void sculpt_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
ss->preview_vert_index_count = 0;
|
||||
int totpoints = 0;
|
||||
|
||||
/* This function is called from the cursor drawing code, so the PBVH may not be build yet */
|
||||
if (!ss->pbvh) {
|
||||
return;
|
||||
}
|
||||
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
|
||||
|
||||
if (!ss->pmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
float brush_co[3];
|
||||
copy_v3_v3(brush_co, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
|
||||
|
||||
char *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(char),
|
||||
"visited vertices");
|
||||
|
||||
if (ss->preview_vert_index_list == NULL) {
|
||||
ss->preview_vert_index_list = MEM_callocN(4 * sizeof(int) * sculpt_vertex_count_get(ss),
|
||||
"preview lines");
|
||||
}
|
||||
|
||||
BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator),
|
||||
"Not visited vertices stack");
|
||||
VertexTopologyIterator mevit;
|
||||
mevit.v = sculpt_active_vertex_get(ss);
|
||||
BLI_stack_push(not_visited_vertices, &mevit);
|
||||
|
||||
while (!BLI_stack_is_empty(not_visited_vertices)) {
|
||||
VertexTopologyIterator c_mevit;
|
||||
BLI_stack_pop(not_visited_vertices, &c_mevit);
|
||||
SculptVertexNeighborIter ni;
|
||||
sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni)
|
||||
{
|
||||
VertexTopologyIterator new_entry;
|
||||
new_entry.v = ni.index;
|
||||
new_entry.it = c_mevit.it + 1;
|
||||
ss->preview_vert_index_list[totpoints] = c_mevit.v;
|
||||
totpoints++;
|
||||
ss->preview_vert_index_list[totpoints] = new_entry.v;
|
||||
totpoints++;
|
||||
if (visited_vertices[(int)ni.index] == 0) {
|
||||
visited_vertices[(int)ni.index] = 1;
|
||||
if (len_squared_v3v3(brush_co, sculpt_vertex_co_get(ss, new_entry.v)) < radius * radius) {
|
||||
BLI_stack_push(not_visited_vertices, &new_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
sculpt_vertex_neighbors_iter_end(ni)
|
||||
}
|
||||
|
||||
BLI_stack_free(not_visited_vertices);
|
||||
|
||||
MEM_freeN(visited_vertices);
|
||||
|
||||
ss->preview_vert_index_count = totpoints;
|
||||
}
|
||||
|
||||
void ED_operatortypes_sculpt(void)
|
||||
{
|
||||
WM_operatortype_append(SCULPT_OT_brush_stroke);
|
||||
|
|
|
@ -57,6 +57,10 @@ bool sculpt_cursor_geometry_info_update(bContext *C,
|
|||
SculptCursorGeometryInfo *out,
|
||||
const float mouse[2],
|
||||
bool use_sampled_normal);
|
||||
void sculpt_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, float radius);
|
||||
|
||||
/* Sculpt PBVH abstraction API */
|
||||
float *sculpt_vertex_co_get(struct SculptSession *ss, int index);
|
||||
|
||||
/* Dynamic topology */
|
||||
void sculpt_pbvh_clear(Object *ob);
|
||||
|
|
|
@ -244,6 +244,8 @@ typedef struct Brush {
|
|||
int size;
|
||||
/** General purpose flag. */
|
||||
int flag;
|
||||
int flag2;
|
||||
char _pad[4];
|
||||
/** Pressure influence for mask. */
|
||||
int mask_pressure;
|
||||
/** Jitter the position of the brush. */
|
||||
|
@ -280,7 +282,7 @@ typedef struct Brush {
|
|||
/** Source for fill tool color gradient application. */
|
||||
char gradient_fill_mode;
|
||||
|
||||
char _pad;
|
||||
char _pad0;
|
||||
/** Projection shape (sphere, circle). */
|
||||
char falloff_shape;
|
||||
float falloff_angle;
|
||||
|
@ -289,7 +291,6 @@ typedef struct Brush {
|
|||
char sculpt_tool;
|
||||
/** Active sculpt tool. */
|
||||
char uv_sculpt_tool;
|
||||
/** Active vertex paint. */
|
||||
char vertexpaint_tool;
|
||||
/** Active weight paint. */
|
||||
char weightpaint_tool;
|
||||
|
@ -299,7 +300,7 @@ typedef struct Brush {
|
|||
char mask_tool;
|
||||
/** Active grease pencil tool. */
|
||||
char gpencil_tool;
|
||||
char _pad0[1];
|
||||
char _pad1[1];
|
||||
|
||||
float autosmooth_factor;
|
||||
|
||||
|
@ -318,7 +319,7 @@ typedef struct Brush {
|
|||
int curve_preset;
|
||||
int automasking_flags;
|
||||
|
||||
char _pad1[4];
|
||||
char _pad2[4];
|
||||
|
||||
int elastic_deform_type;
|
||||
float elastic_deform_compressibility;
|
||||
|
@ -431,6 +432,10 @@ typedef enum eBrushFlags {
|
|||
BRUSH_CURVE = (1u << 31),
|
||||
} eBrushFlags;
|
||||
|
||||
typedef enum eBrushFlags2 {
|
||||
BRUSH_GRAB_ACTIVE_VERTEX = (1 << 0),
|
||||
} eBrushFlags2;
|
||||
|
||||
typedef enum {
|
||||
BRUSH_MASK_PRESSURE_RAMP = (1 << 1),
|
||||
BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2),
|
||||
|
|
|
@ -1984,6 +1984,14 @@ static void rna_def_brush(BlenderRNA *brna)
|
|||
prop, "Spacing distance", "Calculate the brush spacing using view or scene distance");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
prop = RNA_def_property(srna, "grab_active_vertex", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_GRAB_ACTIVE_VERTEX);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Grab Active Vertex",
|
||||
"Apply the maximum grab strength to the active vertex instead of the cursor location");
|
||||
RNA_def_property_update(prop, 0, "rna_Brush_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_pressure_strength", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ALPHA_PRESSURE);
|
||||
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
|
||||
|
|
Loading…
Reference in New Issue