diff options
Diffstat (limited to 'source/blender/editors')
29 files changed, 5271 insertions, 2251 deletions
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 22779fbba0b..c866b12ed22 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -58,6 +58,7 @@ struct CustomData; struct Material; struct Object; struct rcti; +struct CustomDataLayer; #define EM_FGON_DRAW 1 // face flag #define EM_FGON 2 // edge and face flag both @@ -220,6 +221,10 @@ void ED_mesh_faces_add(struct Mesh *mesh, struct ReportList *reports, int count) void ED_mesh_edges_add(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_vertices_add(struct Mesh *mesh, struct ReportList *reports, int count); +void ED_mesh_delete_customdata_layer(struct bContext *C, struct Object *ob, struct CustomDataLayer *layer); + +void ED_mesh_geometry_add(struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces); + void ED_mesh_transform(struct Mesh *me, float *mat); void ED_mesh_calc_normals(struct Mesh *me); void ED_mesh_material_link(struct Mesh *me, struct Material *ma); @@ -230,6 +235,8 @@ int ED_mesh_uv_texture_remove(struct bContext *C, struct Object *ob, struct Mesh int ED_mesh_color_add(struct bContext *C, struct Scene *scene, struct Object *ob, struct Mesh *me, const char *name, int active_set); int ED_mesh_color_remove(struct bContext *C, struct Object *ob, struct Mesh *me); +int ED_mesh_layers_poll(struct bContext *C); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 506813ce626..67e65409e6c 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -30,16 +30,16 @@ struct ARegion; struct bContext; +struct MultiresModifierData; struct Object; +struct PaintOverlay; struct RegionView3D; +struct Scene; struct wmKeyConfig; struct wmWindowManager; /* sculpt.c */ void ED_operatortypes_sculpt(void); -void sculpt_get_redraw_planes(float planes[4][4], struct ARegion *ar, - struct RegionView3D *rv3d, struct Object *ob); -void ED_sculpt_force_update(struct bContext *C); /* paint_ops.c */ void ED_operatortypes_paint(void); @@ -52,4 +52,21 @@ void ED_keymap_paint(struct wmKeyConfig *keyconf); int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name); void ED_undo_paint_free(void); +typedef struct PaintLayerUndoNode PaintLayerUndoNode; +PaintLayerUndoNode *paint_layer_undo_push(int type, char *description); +void paint_layer_undo_set_add(PaintLayerUndoNode *unode, char *name); +void paint_layer_undo_set_remove(PaintLayerUndoNode *unode, char *name, + struct CustomData *data, struct CustomData *fdata, + int totvert, int totface); + +/* paint_util.c */ +struct MultiresModifierData *ED_paint_multires_active(struct Scene *scene, struct Object *ob); +void paint_get_redraw_planes(float planes[4][4], struct ARegion *ar, + struct RegionView3D *rv3d, struct Object *ob); +void ED_paint_force_update(struct bContext *C); + +/* paint_vertex.c */ +void ED_paint_overlay_draw(const struct bContext *C, struct ARegion *ar); +void ED_paint_update_overlay(struct PaintOverlay *overlay); + #endif diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index bbbb5bec7c0..c7edec09340 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2013,6 +2013,11 @@ static void list_item_row(bContext *C, uiLayout *layout, PointerRNA *ptr, Pointe //uiItemR(row, itemptr, "mute", 0, "", ICON_MUTE_IPO_OFF); uiBlockSetEmboss(block, UI_EMBOSS); } + else if(itemptr->type == &RNA_MeshPaintMaskLayer) { + uiItemL(sub, name, icon); + uiBlockSetEmboss(block, UI_EMBOSS); + uiDefButR(block, OPTION, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, itemptr, "enabled", 0, 0, 0, 0, 0, NULL); + } else uiItemL(sub, name, icon); /* fails, backdrop LISTROW... */ diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 080151a8dca..39489dc7264 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -45,6 +45,7 @@ #include "BKE_library.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_multires.h" #include "BKE_report.h" #include "BLI_math.h" @@ -59,6 +60,7 @@ #include "ED_mesh.h" #include "ED_object.h" +#include "ED_sculpt.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -66,7 +68,7 @@ #include "mesh_intern.h" -static void delete_customdata_layer(bContext *C, Object *ob, CustomDataLayer *layer) +void ED_mesh_delete_customdata_layer(bContext *C, Object *ob, CustomDataLayer *layer) { Mesh *me = ob->data; CustomData *data= (me->edit_mesh)? &me->edit_mesh->fdata: &me->fdata; @@ -207,7 +209,7 @@ int ED_mesh_uv_texture_remove(bContext *C, Object *ob, Mesh *me) if(!cdl) return 0; - delete_customdata_layer(C, ob, cdl); + ED_mesh_delete_customdata_layer(C, ob, cdl); DAG_id_flush_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); @@ -270,7 +272,7 @@ int ED_mesh_color_remove(bContext *C, Object *ob, Mesh *me) if(!cdl) return 0; - delete_customdata_layer(C, ob, cdl); + ED_mesh_delete_customdata_layer(C, ob, cdl); DAG_id_flush_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); @@ -279,7 +281,7 @@ int ED_mesh_color_remove(bContext *C, Object *ob, Mesh *me) /*********************** UV texture operators ************************/ -static int layers_poll(bContext *C) +int ED_mesh_layers_poll(bContext *C) { Object *ob= CTX_data_pointer_get_type(C, "object", &RNA_Object).data; ID *data= (ob)? ob->data: NULL; @@ -305,7 +307,7 @@ void MESH_OT_uv_texture_add(wmOperatorType *ot) ot->idname= "MESH_OT_uv_texture_add"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->exec= uv_texture_add_exec; /* flags */ @@ -379,7 +381,7 @@ void MESH_OT_drop_named_image(wmOperatorType *ot) ot->idname= "MESH_OT_drop_named_image"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->invoke= drop_named_image_invoke; /* flags */ @@ -409,7 +411,7 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot) ot->idname= "MESH_OT_uv_texture_remove"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->exec= uv_texture_remove_exec; /* flags */ @@ -418,6 +420,37 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot) /*********************** vertex color operators ************************/ +static int vertex_color_multires_toggle(Object *ob) +{ + Mesh *me= ob->data; + CustomDataMultires *cdm; + CustomDataLayer *cdl; + int active; + + /* so that dm is recalculated correctly after */ + multires_force_update(ob); + + active = CustomData_get_active_layer_index(&me->fdata, CD_MCOL); + cdm = CustomData_get_layer(&me->fdata, CD_GRIDS); + + if(active == -1) + return 1; + + cdl = &me->fdata.layers[active]; + + if(cdm) { + if(cdl->flag & CD_FLAG_MULTIRES) + CustomData_multires_remove_layers(cdm, me->totface, CD_MCOL, cdl->name); + else + CustomData_multires_add_layers(cdm, me->totface, CD_MCOL, cdl->name); + } + + /* note - if there's no griddata, it can still be synced up later */ + cdl->flag ^= CD_FLAG_MULTIRES; + + return 1; +} + static int vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene= CTX_data_scene(C); @@ -427,6 +460,10 @@ static int vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op)) if(!ED_mesh_color_add(C, scene, ob, me, NULL, TRUE)) return OPERATOR_CANCELLED; + if((ob->mode & OB_MODE_VERTEX_PAINT) && + ED_paint_multires_active(scene, ob)) + vertex_color_multires_toggle(ob); + return OPERATOR_FINISHED; } @@ -438,7 +475,7 @@ void MESH_OT_vertex_color_add(wmOperatorType *ot) ot->idname= "MESH_OT_vertex_color_add"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->exec= vertex_color_add_exec; /* flags */ @@ -465,7 +502,30 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot) /* api callbacks */ ot->exec= vertex_color_remove_exec; - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int vertex_color_multires_toggle_exec(bContext *C, wmOperator *op) +{ + if(vertex_color_multires_toggle(CTX_data_active_object(C))) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +void MESH_OT_vertex_color_multiresolution_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Vertex Color Multiresolution Toggle"; + ot->description= "Add or remove multiresolution data from this layer"; + ot->idname= "MESH_OT_vertex_color_multiresolution_toggle"; + + /* api callbacks */ + ot->exec= vertex_color_multires_toggle_exec; + ot->poll= ED_mesh_layers_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; @@ -499,7 +559,7 @@ void MESH_OT_sticky_add(wmOperatorType *ot) ot->idname= "MESH_OT_sticky_add"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->exec= sticky_add_exec; /* flags */ @@ -531,7 +591,7 @@ void MESH_OT_sticky_remove(wmOperatorType *ot) ot->idname= "MESH_OT_sticky_remove"; /* api callbacks */ - ot->poll= layers_poll; + ot->poll= ED_mesh_layers_poll; ot->exec= sticky_remove_exec; /* flags */ diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index fcd1eb40a02..89186f2d1f6 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -249,6 +249,7 @@ void MESH_OT_uv_texture_add(struct wmOperatorType *ot); void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); void MESH_OT_vertex_color_add(struct wmOperatorType *ot); void MESH_OT_vertex_color_remove(struct wmOperatorType *ot); +void MESH_OT_vertex_color_multiresolution_toggle(struct wmOperatorType *ot); void MESH_OT_sticky_add(struct wmOperatorType *ot); void MESH_OT_sticky_remove(struct wmOperatorType *ot); void MESH_OT_drop_named_image(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 2ff7095cfea..5eb6fea362f 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -133,6 +133,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_uv_texture_remove); WM_operatortype_append(MESH_OT_vertex_color_add); WM_operatortype_append(MESH_OT_vertex_color_remove); + WM_operatortype_append(MESH_OT_vertex_color_multiresolution_toggle); WM_operatortype_append(MESH_OT_sticky_add); WM_operatortype_append(MESH_OT_sticky_remove); WM_operatortype_append(MESH_OT_drop_named_image); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ed75c4060a2..9222b33aa8f 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -153,6 +153,7 @@ void OBJECT_OT_multires_reshape(struct wmOperatorType *ot); void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot); void OBJECT_OT_multires_external_save(struct wmOperatorType *ot); void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot); +void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot); void OBJECT_OT_meshdeform_bind(struct wmOperatorType *ot); void OBJECT_OT_explode_refresh(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index c273b375a06..0eb2d89061a 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1147,6 +1147,47 @@ void OBJECT_OT_multires_external_pack(wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } +/********************* multires apply base ***********************/ +static int multires_base_apply_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(op, ob, eModifierType_Multires); + + if (!mmd) + return OPERATOR_CANCELLED; + + multiresModifier_base_apply(mmd, ob); + + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int multires_base_apply_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) + return multires_base_apply_exec(C, op); + else + return OPERATOR_CANCELLED; +} + + +void OBJECT_OT_multires_base_apply(wmOperatorType *ot) +{ + ot->name= "Multires Apply Base"; + ot->description= "Modify the base mesh to conform to the displaced mesh"; + ot->idname= "OBJECT_OT_multires_base_apply"; + + ot->poll= multires_poll; + ot->invoke= multires_base_apply_invoke; + ot->exec= multires_base_apply_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + edit_modifier_properties(ot); +} + /************************ mdef bind operator *********************/ static int meshdeform_poll(bContext *C) diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 221d1211930..d09cc9a7626 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -136,6 +136,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete); WM_operatortype_append(OBJECT_OT_multires_external_save); WM_operatortype_append(OBJECT_OT_multires_external_pack); + WM_operatortype_append(OBJECT_OT_multires_base_apply); WM_operatortype_append(OBJECT_OT_meshdeform_bind); WM_operatortype_append(OBJECT_OT_explode_refresh); diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 3211763b619..96bfab97c5f 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -27,6 +27,7 @@ SET(INC ../../imbuf ../../gpu ../../blenlib + ../../../../extern/ptex ../../makesdna ../../makesrna ../../windowmanager diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index 2902b21fff1..4c49b19d307 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -9,6 +9,7 @@ incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../render/extern/include' incs += ' ../../gpu ../../makesrna' +incs += ' #/extern/ptex' if env['OURPLATFORM'] == 'linux2': cflags='-pthread' diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 6232a8f2de1..68599dfcda2 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -29,10 +29,17 @@ #ifndef ED_PAINT_INTERN_H #define ED_PAINT_INTERN_H +#include "BLI_pbvh.h" + struct bContext; +struct Brush; struct Scene; struct Object; struct Mesh; +struct Multires; +struct Object; +struct Paint; +struct PaintOverlay; struct PaintStroke; struct PointerRNA; struct ViewContext; @@ -44,24 +51,83 @@ struct VPaint; struct ListBase; /* paint_stroke.c */ -typedef int (*StrokeGetLocation)(struct bContext *C, struct PaintStroke *stroke, float location[3], float mouse[2]); +typedef struct PaintStroke PaintStroke; +typedef int (*StrokeGetLocation)(struct bContext *C, PaintStroke *stroke, float location[3], float mouse[2]); typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, struct wmEvent *event); -typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); -typedef void (*StrokeDone)(struct bContext *C, struct PaintStroke *stroke); +typedef void (*StrokeUpdateStep)(struct bContext *C, PaintStroke *stroke, struct PointerRNA *itemptr); +typedef void (*StrokeUpdateSymmetry)(struct bContext *C, PaintStroke *stroke, + char symmetry, char axis, float angle, + int mirror_symmetry_pass, int radial_symmetry_pass, + float (*symmetry_rot_mat)[4]); +typedef void (*StrokeBrushAction)(struct bContext *C, PaintStroke *stroke); +typedef void (*StrokeDone)(struct bContext *C, PaintStroke *stroke); -struct PaintStroke *paint_stroke_new(struct bContext *C, - StrokeGetLocation get_location, StrokeTestStart test_start, - StrokeUpdateStep update_step, StrokeDone done); -void paint_stroke_free(struct PaintStroke *stroke); +PaintStroke *paint_stroke_new(struct bContext *C, + StrokeGetLocation get_location, + StrokeTestStart test_start, + StrokeUpdateStep update_step, + StrokeUpdateSymmetry update_symmetry, + StrokeBrushAction brush_action, + StrokeDone done); +void paint_stroke_free(PaintStroke *stroke); int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event); +void paint_stroke_apply_brush(struct bContext *C, PaintStroke *stroke, struct Paint *paint); +float paint_stroke_combined_strength(PaintStroke *stroke, + float dist, float co[3], float mask); int paint_stroke_exec(struct bContext *C, struct wmOperator *op); -struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); -void *paint_stroke_mode_data(struct PaintStroke *stroke); -void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); +void *paint_stroke_mode_data(PaintStroke *stroke); +void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data); + +/* paint stroke cache access */ +struct ViewContext *paint_stroke_view_context(PaintStroke *stroke); +float paint_stroke_feather(PaintStroke *stroke); +void paint_stroke_mouse_location(PaintStroke *stroke, float mouse[2]); +void paint_stroke_initial_mouse_location(PaintStroke *stroke, float initial_mouse[2]); +void paint_stroke_location(PaintStroke *stroke, float location[3]); +float paint_stroke_pressure(PaintStroke *stroke); +float paint_stroke_radius(PaintStroke *stroke); +float paint_stroke_radius_squared(PaintStroke *stroke); +void paint_stroke_symmetry_location(PaintStroke *stroke, float loc[3]); +int paint_stroke_first_dab(PaintStroke *stroke); +void paint_stroke_project(PaintStroke *stroke, float loc[3], float out[2]); +void paint_stroke_symmetry_unflip(PaintStroke *stroke, float out[3], float vec[3]); + +/* paint stroke modifiers */ +void paint_stroke_set_modifier_use_original_location(PaintStroke *stroke); +void paint_stroke_set_modifier_initial_radius_factor(PaintStroke *stroke, float initial_radius_factor); +void paint_stroke_set_modifier_use_original_texture_coords(PaintStroke *stroke); + +typedef struct { + void *mode_data; + struct Object *ob; + float *ray_start, *ray_normal; + int hit; + float dist; + int original; +} PaintStrokeRaycastData; + +int paint_stroke_over_mesh(struct bContext *C, PaintStroke *stroke, int x, int y); +int paint_stroke_get_location(struct bContext *C, PaintStroke *stroke, + BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data, + float out[3], float mouse[2], int original); + int paint_poll(struct bContext *C); void paint_cursor_start(struct bContext *C, int (*poll)(struct bContext *C)); +typedef struct PaintStrokeTest { + float radius_squared; + float location[3]; + float dist; +} PaintStrokeTest; +void paint_stroke_test_init(PaintStrokeTest *test, PaintStroke *stroke); +int paint_stroke_test(PaintStrokeTest *test, float co[3]); +int paint_stroke_test_sq(PaintStrokeTest *test, float co[3]); +int paint_stroke_test_fast(PaintStrokeTest *test, float co[3]); +int paint_stroke_test_cube(PaintStrokeTest *test, float co[3], + float local[4][4]); +float paint_stroke_test_dist(PaintStrokeTest *test); + /* paint_vertex.c */ int weight_paint_poll(struct bContext *C); int weight_paint_mode_poll(struct bContext *C); @@ -80,7 +146,6 @@ void PAINT_OT_weight_from_bones(struct wmOperatorType *ot); void PAINT_OT_vertex_paint_radial_control(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); /* paint_image.c */ @@ -110,6 +175,24 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot); int facemask_paint_poll(struct bContext *C); +float paint_calc_object_space_radius(struct ViewContext *vc, + float center[3], + float pixel_radius); + +void paint_tag_partial_redraw(struct bContext *C, struct Object *ob); + +void paint_flip_coord(float out[3], float in[3], const char symm); + +float brush_tex_strength(struct ViewContext *vc, + float pmat[4][4], struct Brush *br, + float co[3], float mask, const float len, + float pixel_radius, float radius3d, + float special_rotation, float tex_mouse[2]); + +int paint_util_raycast(struct ViewContext *vc, + BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data, + float out[3], float mouse[2], int original); + /* stroke operator */ typedef enum wmBrushStrokeMode { WM_BRUSHSTROKE_NORMAL, @@ -126,5 +209,69 @@ struct ListBase *undo_paint_push_get_list(int type); void undo_paint_push_count_alloc(int type, int size); void undo_paint_push_end(int type); +/* paint_mask.c */ +void paintmask_brush_apply(struct Paint *paint, PaintStroke *stroke, + struct PBVHNode **nodes, + int totnode, float bstrength); + +void PAINT_OT_mask_layer_add(struct wmOperatorType *ot); +void PAINT_OT_mask_layer_remove(struct wmOperatorType *ot); + +typedef enum { + MASKING_CLEAR, + MASKING_FILL, + MASKING_INVERT, + MASKING_RANDOM, +} MaskSetMode; +void PAINT_OT_mask_set(struct wmOperatorType *ot); +void PAINT_OT_mask_from_texture(struct wmOperatorType *ot); + +/* pbvh_undo.c */ +typedef enum { + PBVH_UNDO_CO_NO = (1<<0), + PBVH_UNDO_PMASK = (1<<1), + PBVH_UNDO_PTEX = (1<<2) +} PBVHUndoFlag; + +typedef struct PBVHUndoNode PBVHUndoNode; + +PBVHUndoNode *pbvh_undo_push_node(PBVHNode *node, PBVHUndoFlag flag, + struct Object *ob, struct Scene *scene); +void pbvh_undo_push_begin(char *name); +void pbvh_undo_push_end(void); +/* undo node access */ +PBVHUndoNode *pbvh_undo_get_node(struct PBVHNode *node); +typedef float (*pbvh_undo_f3)[3]; +typedef short (*pbvh_undo_s3)[3]; +int pbvh_undo_node_totvert(PBVHUndoNode *unode); +pbvh_undo_f3 pbvh_undo_node_co(PBVHUndoNode *unode); +pbvh_undo_s3 pbvh_undo_node_no(PBVHUndoNode *unode); +float *pbvh_undo_node_layer_disp(PBVHUndoNode *unode); +void pbvh_undo_node_set_layer_disp(PBVHUndoNode *unode, float *layer_disp); +const char *pbvh_undo_node_mptex_name(PBVHUndoNode *unode); +void *pbvh_undo_node_mptex_data(PBVHUndoNode *unode, int ndx); + +/* paint_ptex.c */ +void PTEX_OT_layer_add(struct wmOperatorType *ot); +void PTEX_OT_layer_remove(struct wmOperatorType *ot); +void PTEX_OT_layer_save(struct wmOperatorType *ot); +void PTEX_OT_layer_convert(struct wmOperatorType *ot); +void PTEX_OT_open(struct wmOperatorType *ot); +void PTEX_OT_face_resolution_set(struct wmOperatorType *ot); +void PTEX_OT_subface_select(struct wmOperatorType *ot); +void PTEX_OT_select_all(struct wmOperatorType *ot); +void PTEX_OT_subface_flag_set(struct wmOperatorType *ot); + +/* paint_overlay.c */ +int paint_sample_overlay(PaintStroke *stroke, float col[3], float co[2]); +typedef enum { + PAINT_MANIP_GRAB, + PAINT_MANIP_SCALE, + PAINT_MANIP_ROTATE +} PaintManipAction; +void paint_overlay_transform(struct PaintOverlay *overlay, struct ARegion *ar, struct ImBuf *ibuf, + int out[2], float vec[2], int scale, int rotate); +void PAINT_OT_overlay_manipulate(struct wmOperatorType *ot); + #endif /* ED_PAINT_INTERN_H */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c new file mode 100644 index 00000000000..fb9624b8f60 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -0,0 +1,555 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_dmgrid.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_paint.h" +#include "BKE_texture.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_pbvh.h" +#include "BLI_string.h" + +#include "ED_mesh.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RE_render_ext.h" +#include "RE_shader_ext.h" + +/* for redraw, just need to update the pbvh's vbo buffers */ +static void paintmask_redraw(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + paint_refresh_mask_display(ob); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); +} + +static int mask_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + return ob && get_mesh(ob) && ob->paint && + (ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)); +} + +static int mask_active_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if(mask_poll(C)) { + Mesh *me = get_mesh(ob); + return CustomData_get_active_layer_index(&me->vdata, CD_PAINTMASK) != -1; + } + + return 0; +} + +void paintmask_brush_apply(Paint *paint, PaintStroke *stroke, PBVHNode **nodes, int totnode, + float bstrength) +{ + Object *ob = paint_stroke_view_context(stroke)->obact; + int n; + + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + PaintStrokeTest test; + + pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, NULL); + paint_stroke_test_init(&test, stroke); + + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + /* TODO: should add a mask layer if needed */ + if(vd.mask_active) { + if(paint_stroke_test(&test, vd.co)) { + float fade; + + fade = paint_stroke_combined_strength(stroke, + test.dist, + vd.co, + 0) * + bstrength; + + *vd.mask_active += fade; + CLAMP(*vd.mask_active, 0, 1); + + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_set_flags(nodes[n], SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|PBVH_UpdateRedraw)); + } +} + +static float get_tex_mask_strength(MTex *tex_slot, float *uv, float vco[3]) +{ + float texvec[3] = {0, 0, 0}; + TexResult texres; + int mapping = tex_slot->texco; + + if(mapping == TEXCO_UV && !uv) + mapping = TEXCO_ORCO; + + switch(tex_slot->texco) { + case TEXCO_UV: + texvec[0] = uv[0] * 2 - 1; + texvec[1] = uv[1] * 2 - 1; + break; + default: + copy_v3_v3(texvec, vco); + } + + memset(&texres, 0, sizeof(TexResult)); + get_texture_value(tex_slot->tex, texvec, &texres); + + return texres.tin; +} + +/* Set the value of a single mask element from either a UV or a coord */ +#define SET_MASK(elem, uv) \ + GRIDELEM_MASK(elem, gridkey)[active] = \ + get_tex_mask_strength(tex_slot, uv, \ + GRIDELEM_CO(elem, gridkey)) \ + +/* Fill active mask layer of entire grid from either MTFaces or coords */ +static void mask_grid_from_tex(DMGridData *grid, int gridsize, + GridKey *gridkey, MTFace *mtface, + MTex *tex_slot, int active) +{ + int x, y, boundary; + + boundary = gridsize - 2; + + for(y = 0; y <= boundary; ++y) { + for(x = 0; x <= boundary; ++x) { + SET_MASK(GRIDELEM_AT(grid, y*gridsize+x, gridkey), + mtface ? mtface->uv[0] : NULL); + + /* Do the edge of the grid separately because the UV + grid is one element smaller on each side compared + to the vert-data grid */ + if(x == boundary && y == boundary) { + SET_MASK(GRIDELEM_AT(grid, (y+1)*gridsize+x+1, gridkey), + mtface ? mtface->uv[2] : NULL); + } + if(x == boundary) { + SET_MASK(GRIDELEM_AT(grid, y*gridsize+x+1, gridkey), + mtface ? mtface->uv[3] : NULL); + } + if(y == boundary) { + SET_MASK(GRIDELEM_AT(grid, (y+1)*gridsize+x, gridkey), + mtface ? mtface->uv[1] : NULL); + } + + if(mtface) ++mtface; + } + } +} + +static void mask_face_from_tex(MFace *f, MVert *mvert, MTFace *mtface, + float *pmask, MTex *tex_slot, int active) +{ + int S = f->v4 ? 4 : 3; + int i; + + /* Masks are per-vertex, not per-face-corner, so the mask + value at each vertex comes from one arbitrary face + corner; not averaged or otherwise combined yet */ + for(i = 0; i < S; ++i) { + int vndx = (&f->v1)[i]; + float *vco = mvert[vndx].co; + + pmask[vndx] = get_tex_mask_strength(tex_slot, + mtface ? mtface->uv[i] : NULL, vco); + } +} + +static int paint_mask_from_texture_exec(bContext *C, wmOperator *op) +{ + struct Scene *scene; + Object *ob; + Mesh *me; + struct MultiresModifierData *mmd; + PaintSession *ps; + MTex *tex_slot; + DerivedMesh *dm = NULL; + PBVH *pbvh; + MTFace *mtfaces = NULL; + PBVHNode **nodes; + int totnode, n, i, active; + + tex_slot = CTX_data_pointer_get_type(C, "texture_slot", + &RNA_TextureSlot).data; + + scene = CTX_data_scene(C); + ob = CTX_data_active_object(C); + ps = ob->paint; + me = get_mesh(ob); + mmd = ED_paint_multires_active(scene, ob); + + pbvh_undo_push_begin("Paint mask from texture"); + + active = CustomData_get_active_layer(&me->vdata, CD_PAINTMASK); + + /* if using UV mapping, check for a matching MTFace layer */ + if(tex_slot->texco == TEXCO_UV) { + mtfaces = CustomData_get_layer_named(&me->fdata, CD_MTFACE, + tex_slot->uvname); + } + + /* the MTFace mask is needed only for multires+UV */ + dm = mesh_get_derived_final(scene, ob, + (mtfaces && mmd) ? CD_MASK_MTFACE : 0); + + /* use the subdivided UVs for multires */ + if(mtfaces && mmd) { + mtfaces = CustomData_get_layer_named(&dm->faceData, + CD_MTFACE, + tex_slot->uvname); + } + + /* update the pbvh */ + ps->pbvh = pbvh = dm->getPBVH(ob, dm); + + /* get all nodes in the pbvh */ + BLI_pbvh_search_gather(pbvh, + NULL, NULL, + &nodes, &totnode); + + if(mmd) { + /* For all grids, find offset into mtfaces and apply + the texture to the grid */ + for(n = 0; n < totnode; ++n) { + DMGridData **grids; + GridKey *gridkey; + int *grid_indices, totgrid, gridsize; + + pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene); + + BLI_pbvh_node_get_grids(pbvh, nodes[n], &grid_indices, + &totgrid, NULL, &gridsize, + &grids, NULL, &gridkey); + + for(i = 0; i < totgrid; ++i) { + int grid_index = grid_indices[i]; + MTFace *mtface = NULL; + + if(mtfaces) { + mtface = &mtfaces[grid_index * + ((gridsize-1) * + (gridsize-1))]; + } + + mask_grid_from_tex(grids[grid_index], + gridsize, gridkey, + mtface, tex_slot, active); + } + + BLI_pbvh_node_set_flags(nodes[n], + SET_INT_IN_POINTER(PBVH_UpdateColorBuffers)); + } + + multires_mark_as_modified(ob); + } + else { + float *pmask = CustomData_get_layer(&me->vdata, CD_PAINTMASK); + + for(n = 0; n < totnode; ++n) { + MFace *mface; + int *face_indices, totface; + + pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene); + + BLI_pbvh_node_get_faces(pbvh, nodes[n], &mface, + &face_indices, NULL, &totface); + + for(i = 0; i < totface; ++i) { + int face_index = face_indices[i]; + MTFace *mtface = NULL; + + if(mtfaces) + mtface = mtfaces + face_index; + + mask_face_from_tex(me->mface + face_index, + me->mvert, mtface, + pmask, tex_slot, active); + } + + BLI_pbvh_node_set_flags(nodes[n], + SET_INT_IN_POINTER(PBVH_UpdateColorBuffers)); + } + } + + MEM_freeN(nodes); + + pbvh_undo_push_end(); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +/* fills a mask with intensity values from a texture, using an + mtex to provide mapping */ +void PAINT_OT_mask_from_texture(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mask From Texture"; + ot->idname= "PAINT_OT_mask_from_texture"; + + /* api callbacks */ + ot->exec= paint_mask_from_texture_exec; + ot->poll= mask_active_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; +} + +static void set_mask_value(MaskSetMode mode, float *m) +{ + *m = (mode == MASKING_CLEAR ? 0 : + mode == MASKING_FILL ? 1 : + mode == MASKING_INVERT ? (1 - *m) : + mode == MASKING_RANDOM ? (float)rand() / RAND_MAX : 0); +} + +static int paint_mask_set_exec(bContext *C, wmOperator *op) +{ + MaskSetMode mode = RNA_enum_get(op->ptr, "mode"); + struct Scene *scene; + Object *ob; + DerivedMesh *dm; + struct MultiresModifierData *mmd; + PaintSession *ps; + Mesh *me; + PBVH *pbvh; + + scene = CTX_data_scene(C); + ob = CTX_data_active_object(C); + ps = ob->paint; + me = get_mesh(ob); + mmd = ED_paint_multires_active(scene, ob); + + dm = mesh_get_derived_final(scene, ob, 0); + ps->pbvh = pbvh = dm->getPBVH(ob, dm); + + if(pbvh) { + PBVHNode **nodes; + int n, totnode; + + BLI_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + pbvh_undo_push_begin("Paint mask fill"); + + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + + pbvh_undo_push_node(nodes[n], PBVH_UNDO_PMASK, ob, scene); + + BLI_pbvh_vertex_iter_begin(pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(vd.mask_active) + set_mask_value(mode, vd.mask_active); + } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_set_flags(nodes[n], SET_INT_IN_POINTER(PBVH_UpdateColorBuffers|PBVH_UpdateRedraw)); + } + + if(nodes) + MEM_freeN(nodes); + + if(mmd) + multires_mark_as_modified(ob); + + pbvh_undo_push_end(); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + } + + return OPERATOR_FINISHED; +} + +/* fills up a mask for the entire object, setting each vertex to + either 0, 1, or a random value */ +void PAINT_OT_mask_set(wmOperatorType *ot) +{ + static EnumPropertyItem mask_items[] = { + {MASKING_CLEAR, "CLEAR", 0, "Clear", ""}, + {MASKING_FILL, "FILL", 0, "Fill", ""}, + {MASKING_INVERT, "INVERT", 0, "Invert", ""}, + {MASKING_RANDOM, "RANDOM", 0, "Random", ""}, + {0, NULL, 0, NULL, NULL}}; + + /* identifiers */ + ot->name= "Set Mask"; + ot->idname= "PAINT_OT_mask_set"; + + /* api callbacks */ + ot->exec= paint_mask_set_exec; + ot->poll= mask_active_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_enum(ot->srna, "mode", mask_items, MASKING_CLEAR, "Mode", ""); +} + +/* if this is a multires mesh, update it and free the DM. + returns 1 if this is a multires mesh, 0 otherwise */ +static int paintmask_check_multires(bContext *C) +{ + struct Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + + if(ED_paint_multires_active(scene, ob)) { + multires_force_update(ob); + return 1; + } + + return 0; +} + +static int mask_layer_poll(bContext *C) +{ + return mask_poll(C) && ED_mesh_layers_poll(C); +} + +static int mask_layer_add_exec(bContext *C, wmOperator *op) +{ + Object *ob= CTX_data_active_object(C); + Mesh *me= ob->data; + int multires, top, first; + char *layer_name; + PaintLayerUndoNode *unode; + + unode = paint_layer_undo_push(CD_PAINTMASK, "Add mask layer"); + + multires = paintmask_check_multires(C); + + top= CustomData_number_of_layers(&me->vdata, CD_PAINTMASK); + CustomData_add_layer(&me->vdata, CD_PAINTMASK, CD_DEFAULT, + NULL, me->totvert); + + first = CustomData_get_layer_index(&me->vdata, CD_PAINTMASK); + assert(first >= 0); + + me->vdata.layers[first + top].flag |= CD_FLAG_MULTIRES; + CustomData_set_layer_active(&me->vdata, CD_PAINTMASK, top); + layer_name = me->vdata.layers[first + top].name; + + /* now that we have correct name, update multires and finish undo push */ + + if(multires) { + CustomDataMultires *cdm; + + cdm = CustomData_get_layer(&me->fdata, CD_GRIDS); + + CustomData_multires_add_layers(cdm, me->totface, + CD_PAINTMASK, layer_name); + } + + paint_layer_undo_set_add(unode, layer_name); + + paintmask_redraw(C); + + return OPERATOR_FINISHED; +} + +static int mask_layer_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob= CTX_data_active_object(C); + Mesh *me= ob->data; + int active_offset, first; + + active_offset = CustomData_get_active_layer(&me->vdata, CD_PAINTMASK); + + if(active_offset >= 0) { + PaintLayerUndoNode *unode; + int multires = paintmask_check_multires(C); + char *layer_name; + + unode = paint_layer_undo_push(CD_PAINTMASK, + "Remove mask layer"); + + first = CustomData_get_layer_index(&me->vdata, CD_PAINTMASK); + layer_name = me->vdata.layers[first + active_offset].name; + + paint_layer_undo_set_remove(unode, layer_name, + &me->vdata, &me->fdata, + me->totvert, me->totface); + + if(multires) { + CustomDataMultires *cdm; + + cdm = CustomData_get_layer(&me->fdata, CD_GRIDS); + + CustomData_multires_remove_layers(cdm, me->totface, + CD_PAINTMASK, + layer_name); + } + + CustomData_free_layer_active(&me->vdata, CD_PAINTMASK, + me->totvert); + + paintmask_redraw(C); + } + else + return OPERATOR_CANCELLED; + + return OPERATOR_FINISHED; +} + +void PAINT_OT_mask_layer_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Mask Layer"; + ot->description= "Add a paint mask layer"; + ot->idname= "PAINT_OT_mask_layer_add"; + + /* api callbacks */ + ot->poll= mask_layer_poll; + ot->exec= mask_layer_add_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +void PAINT_OT_mask_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Mask Layer"; + ot->description= "Remove the active paint mask layer"; + ot->idname= "PAINT_OT_mask_layer_remove"; + + /* api callbacks */ + ot->poll= mask_layer_poll; + ot->exec= mask_layer_remove_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 2e0338b370e..b190398037d 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -19,6 +19,7 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -216,11 +217,29 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_vertex_paint_toggle); WM_operatortype_append(PAINT_OT_vertex_paint); WM_operatortype_append(PAINT_OT_vertex_color_set); + WM_operatortype_append(PAINT_OT_overlay_manipulate); + + /* ptex */ + WM_operatortype_append(PTEX_OT_layer_add); + WM_operatortype_append(PTEX_OT_layer_remove); + WM_operatortype_append(PTEX_OT_layer_save); + WM_operatortype_append(PTEX_OT_layer_convert); + WM_operatortype_append(PTEX_OT_open); + WM_operatortype_append(PTEX_OT_face_resolution_set); + WM_operatortype_append(PTEX_OT_subface_select); + WM_operatortype_append(PTEX_OT_select_all); + WM_operatortype_append(PTEX_OT_subface_flag_set); /* face-select */ WM_operatortype_append(PAINT_OT_face_select_linked); WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); + + /* mask */ + WM_operatortype_append(PAINT_OT_mask_layer_add); + WM_operatortype_append(PAINT_OT_mask_layer_remove); + WM_operatortype_append(PAINT_OT_mask_set); + WM_operatortype_append(PAINT_OT_mask_from_texture); } @@ -301,6 +320,19 @@ static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *UNUSED(path RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111.... } +static void ed_keymap_paint_overlay(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", GKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_GRAB); + kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", SKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_SCALE); + kmi = WM_keymap_add_item(keymap, "PAINT_OT_overlay_manipulate", RKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", PAINT_MANIP_ROTATE); + kmi= WM_keymap_add_item(keymap, "WM_OT_context_toggle", IKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.paint_overlay.enabled"); +} + void ED_keymap_paint(wmKeyConfig *keyconf) { wmKeyMap *keymap; @@ -318,6 +350,12 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL); RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_BRUSHSTROKE_INVERT); RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH); + + WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", EVT_TWEAK_L, KM_ANY, KM_CTRL|KM_SHIFT, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", EVT_TWEAK_L, KM_ANY, KM_CTRL|KM_ALT, 0)->ptr, "hide_inside", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", LEFTMOUSE, KM_RELEASE, KM_CTRL|KM_SHIFT, 0)->ptr, "show_all", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", LEFTMOUSE, KM_RELEASE, KM_CTRL|KM_ALT, 0)->ptr, "show_all", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_area_hide", HKEY, KM_PRESS, KM_ALT, 0)->ptr, "show_all", 1); //stroke_mode_modal_keymap(keyconf); @@ -394,16 +432,41 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_vertex_paint_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH); WM_keymap_verify_item(keymap, "PAINT_OT_vertex_paint", LEFTMOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "PAINT_OT_sample_color", RIGHTMOUSE, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "PAINT_OT_vertex_color_set",KKEY, KM_PRESS, KM_SHIFT, 0); + /* ptex edit mode */ + WM_keymap_add_item(keymap, "PTEX_OT_subface_select", RIGHTMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_select", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", 1); + WM_keymap_add_item(keymap, "PTEX_OT_select_all", AKEY, KM_PRESS, 0, 0); + + /* ptex subface hiding */ + kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", HKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_HIDDEN); + kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", HKEY, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_HIDDEN); + RNA_boolean_set(kmi->ptr, "ignore_unselected", 0); + RNA_boolean_set(kmi->ptr, "ignore_hidden", 0); + RNA_boolean_set(kmi->ptr, "set", 0); + + /* ptex subface mask */ + kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", MKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_MASKED); + kmi = WM_keymap_add_item(keymap, "PTEX_OT_subface_flag_set", MKEY, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "flag", MPTEX_SUBFACE_MASKED); + RNA_boolean_set(kmi->ptr, "ignore_unselected", 0); + RNA_boolean_set(kmi->ptr, "ignore_hidden", 0); + RNA_boolean_set(kmi->ptr, "set", 0); + ed_keymap_paint_brush_switch(keymap, "tool_settings.vertex_paint.active_brush_index"); ed_keymap_paint_brush_size(keymap, "tool_settings.vertex_paint.brush.size"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */ RNA_string_set(kmi->ptr, "data_path", "vertex_paint_object.data.use_paint_mask"); + ed_keymap_paint_overlay(keymap); + /* Weight Paint mode */ keymap= WM_keymap_find(keyconf, "Weight Paint", 0, 0); keymap->poll= weight_paint_mode_poll; @@ -442,6 +505,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* mask toggle */ RNA_string_set(kmi->ptr, "data_path", "texture_paint_object.data.use_paint_mask"); + ed_keymap_paint_overlay(keymap); + /* face-mask mode */ keymap= WM_keymap_find(keyconf, "Face Mask", 0, 0); keymap->poll= facemask_paint_poll; diff --git a/source/blender/editors/sculpt_paint/paint_overlay.c b/source/blender/editors/sculpt_paint/paint_overlay.c new file mode 100644 index 00000000000..c02e2c1fcd6 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_overlay.c @@ -0,0 +1,427 @@ +/* + * $Id$ + * + * ***** 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. + * + * The Original Code is Copyright (C) 2010 by Nicholas Bishop + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): None + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_paint.h" + +#include "BLI_math.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "UI_resources.h" + +#include "paint_intern.h" + +#include <math.h> + +int paint_sample_overlay(PaintStroke *stroke, float col[3], float co[2]) +{ + ViewContext *vc = paint_stroke_view_context(stroke); + PaintOverlay *overlay = &vc->scene->toolsettings->paint_overlay; + + col[0] = col[1] = col[2] = col[3] = 0; + + if(overlay->use && overlay->img) { + ImBuf *ibuf = BKE_image_get_ibuf(overlay->img, NULL); + + if(ibuf) { + int x, y; + int offset, trans[2]; + float uco[3], proj[2]; + + paint_stroke_symmetry_unflip(stroke, uco, co); + paint_stroke_project(stroke, uco, proj); + + + paint_overlay_transform(overlay, vc->ar, ibuf, + trans, proj, 1, 1); + x = trans[0]; + y = trans[1]; + + if(x >= 0 && x < ibuf->x && y >= 0 && y < ibuf->y) { + offset = y*ibuf->x + x; + + if(ibuf->rect) { + char *ccol = ((char*)ibuf->rect) + offset*4; + + col[0] = ccol[0] / 255.0; + col[1] = ccol[1] / 255.0; + col[2] = ccol[2] / 255.0; + col[3] = ccol[3] / 255.0; + } + } + } + + return 1; + } + + return 0; +} + +static int paint_overlay_poll(bContext *C) +{ + PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay; + + if(vertex_paint_poll(C) || image_texture_paint_poll(C)) + return overlay->img && overlay->use; + + return 0; +} + +void ED_paint_update_overlay(PaintOverlay *overlay) +{ + if(overlay->gltex) { + glDeleteTextures(1, &overlay->gltex); + overlay->gltex = 0; + } +} + +void ED_paint_overlay_draw(const bContext *C, ARegion *ar) +{ + PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay; + ImBuf *ibuf; + int center[2]; + int sx, sy; + + if(!paint_overlay_poll((bContext*)C)) + return; + + ibuf = BKE_image_get_ibuf(overlay->img, NULL); + + if(!ibuf) + return; + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + if(!overlay->gltex) { + /* update gl texture */ + + /* doing this manually because it looks like the GPU + Image stuff is customized on mesh tex? */ + + glGenTextures(1, (GLuint*)&overlay->gltex); + glBindTexture(GL_TEXTURE_2D, overlay->gltex); + + if ((ibuf->rect==NULL) && ibuf->rect_float) + IMB_rect_from_float(ibuf); + + { + char *bc = (char*)ibuf->rect; + char transp[3] = {overlay->transp_col[0] * 255, + overlay->transp_col[1] * 255, + overlay->transp_col[2] * 255}; + int i; + + for(i = 0; i < ibuf->y*ibuf->x; ++i, bc+=4) { + float d[3] = {fabs(bc[2]-transp[0]), + fabs(bc[1]-transp[1]), + fabs(bc[0]-transp[2])}; + + if(d[0] < overlay->transp_tol && + d[1] < overlay->transp_tol && + d[2] < overlay->transp_tol) + bc[3] = 0; + else + bc[3] = 255; + } + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ibuf->x, ibuf->y, + 0, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + } + + glBindTexture(GL_TEXTURE_2D, overlay->gltex); + glColor4f(1, 1, 1, 0.5); + + glPushMatrix(); + center[0] = ar->winx/2 + overlay->offset[0]; + center[1] = ar->winy/2 + overlay->offset[1]; + sx = overlay->size[0] / 2; + sy = overlay->size[1] / 2; + + glTranslatef(center[0], center[1], 0); + glRotatef(overlay->angle * 180/M_PI, 0, 0, 1); + + /* draw textured quad */ + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(-sx, -sy); + glTexCoord2f(1, 0); + glVertex2f(+sx, -sy); + glTexCoord2f(1, 1); + glVertex2f(+sx, +sy); + glTexCoord2f(0, 1); + glVertex2f(-sx, +sy); + glEnd(); + glPopMatrix(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +} + + +/* convert screen-space coords to ibuf coords */ +void paint_overlay_transform(PaintOverlay *overlay, ARegion *ar, ImBuf *ibuf, + int out[2], float vec[2], int scale, int rotate) +{ + float center[2], size[2], org[2], t1[2], t2[2]; + float sina, cosa; + + center[0] = ar->winx/2 + overlay->offset[0]; + center[1] = ar->winy/2 + overlay->offset[1]; + size[0] = overlay->size[0]; + size[1] = overlay->size[1]; + org[0] = center[0] - size[0]/2; + org[1] = center[1] - size[1]/2; + sina = sin(overlay->angle); + cosa = cos(overlay->angle); + + /* make overlay center origin */ + sub_v2_v2v2(t1, vec, center); + + /* apply rotation */ + if(rotate) { + t2[0] = cosa*t1[0] + sina*t1[1]; + t2[1] = -sina*t1[0] + cosa*t1[1]; + } + else { + out[0] = t1[0]; + out[1] = t1[1]; + t2[0] = t1[0]; + t2[1] = t1[1]; + } + + /* translation */ + if(scale) { + out[0] = t2[0] + size[0]/2; + out[1] = t2[1] + size[1]/2; + + /* scale */ + out[0] *= (ibuf->x / size[0]); + out[1] *= (ibuf->y / size[1]); + } + else { + out[0] = t2[0]; + out[1] = t2[1]; + } +} + +typedef struct { + int x, y; + int offset[2]; + int size[2]; + float orig_angle, start_angle; + + ImBuf *ibuf; + int ibuf_mouse[2]; + + void *draw_cb_handle; + struct ARegionType *draw_cb_type; + /* data needed for manip draw */ + PaintManipAction action; + int cur_mouse[2]; +} VPaintManipData; + +/* when rotating or scaling, draw hashed line to center */ +static void paint_overlay_manip_draw(const bContext *C, ARegion *ar, void *data_v) +{ + PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay; + VPaintManipData *data = data_v; + + if(data->action != PAINT_MANIP_GRAB) { + UI_ThemeColor(TH_WIRE); + setlinestyle(3); + + glBegin(GL_LINES); + glVertex2i(data->cur_mouse[0] - ar->winrct.xmin, + data->cur_mouse[1] - ar->winrct.ymin); + glVertex2i(ar->winx/2 + overlay->offset[0], + ar->winy/2 + overlay->offset[1]); + glEnd(); + + setlinestyle(0); + } +} + +static int paint_overlay_manip_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay; + ARegion *ar = CTX_wm_region(C); + VPaintManipData *data; + float mouse[2] = {event->x - ar->winrct.xmin, + mouse[1] = event->y - ar->winrct.ymin}; + int angle_mouse[2]; + + op->customdata = data = MEM_callocN(sizeof(VPaintManipData), "VPaintManipData"); + + data->x = event->x; + data->y = event->y; + data->offset[0] = overlay->offset[0]; + data->offset[1] = overlay->offset[1]; + data->size[0] = overlay->size[0]; + data->size[1] = overlay->size[1]; + data->orig_angle = overlay->angle; + data->ibuf = BKE_image_get_ibuf(overlay->img, NULL); + data->action = RNA_enum_get(op->ptr, "action"); + data->cur_mouse[0] = event->x; + data->cur_mouse[1] = event->y; + + paint_overlay_transform(overlay, ar, data->ibuf, data->ibuf_mouse, mouse, 0, 1); + + paint_overlay_transform(overlay, ar, data->ibuf, angle_mouse, mouse, 0, 0); + data->start_angle = atan2(angle_mouse[0], angle_mouse[1]); + + data->draw_cb_type = ar->type; + data->draw_cb_handle = ED_region_draw_cb_activate(ar->type, + paint_overlay_manip_draw, + data, + REGION_DRAW_POST_PIXEL); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int paint_overlay_manip_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + PaintOverlay *overlay = &CTX_data_tool_settings(C)->paint_overlay; + ARegion *ar = CTX_wm_region(C); + float mouse[2] = {event->x - ar->winrct.xmin, + event->y - ar->winrct.ymin}; + int ibuf_mouse[2]; + int *co = overlay->offset; + int *size = overlay->size; + float *angle = &overlay->angle; + VPaintManipData *data = op->customdata; + int dx, dy; + + dx = event->x - data->x; + dy = event->y - data->y; + data->cur_mouse[0] = event->x; + data->cur_mouse[1] = event->y; + + switch(data->action) { + case PAINT_MANIP_GRAB: + co[0] = data->offset[0] + dx; + co[1] = data->offset[1] + dy; + break; + case PAINT_MANIP_SCALE: + { + float d[2]; + + paint_overlay_transform(overlay, ar, data->ibuf, ibuf_mouse, mouse, 0, 1); + + d[0] = fabs(ibuf_mouse[0]) - fabs(data->ibuf_mouse[0]); + d[1] = fabs(ibuf_mouse[1]) - fabs(data->ibuf_mouse[1]); + + size[0] = data->size[0] + d[0]; + size[1] = data->size[1] + d[1]; + } + break; + case PAINT_MANIP_ROTATE: + paint_overlay_transform(overlay, ar, data->ibuf, ibuf_mouse, mouse, 0, 0); + *angle = data->orig_angle + data->start_angle - atan2(ibuf_mouse[0], ibuf_mouse[1]); + + break; + } + + ED_region_tag_redraw(CTX_wm_region(C)); + + if(event->type == LEFTMOUSE) { + ED_region_draw_cb_exit(data->draw_cb_type, data->draw_cb_handle); + MEM_freeN(data); + return OPERATOR_FINISHED; + } + else if(event->type == RIGHTMOUSE) { + co[0] = data->offset[0]; + co[1] = data->offset[1]; + size[0] = data->size[0]; + size[1] = data->size[1]; + *angle = data->orig_angle; + ED_region_draw_cb_exit(data->draw_cb_type, data->draw_cb_handle); + MEM_freeN(data); + return OPERATOR_CANCELLED; + } + else + return OPERATOR_RUNNING_MODAL; +} + +void PAINT_OT_overlay_manipulate(wmOperatorType *ot) +{ + static EnumPropertyItem action_items[]= { + {PAINT_MANIP_GRAB, "GRAB", 0, "Grab", ""}, + {PAINT_MANIP_SCALE, "SCALE", 0, "Scale", ""}, + {PAINT_MANIP_ROTATE, "Rotate", 0, "Rotate", ""}, + + {0, NULL, 0, NULL, NULL}}; + + /* identifiers */ + ot->name= "Paint Overlay Manipulate"; + ot->idname= "PAINT_OT_overlay_manipulate"; + + /* api callbacks */ + ot->invoke= paint_overlay_manip_invoke; + ot->modal= paint_overlay_manip_modal; + ot->poll= paint_overlay_poll; + + /* flags */ + ot->flag= OPTYPE_BLOCKING; + + /* properties */ + ot->prop= RNA_def_enum(ot->srna, "action", action_items, 0, "Action", ""); +} diff --git a/source/blender/editors/sculpt_paint/paint_ptex.c b/source/blender/editors/sculpt_paint/paint_ptex.c new file mode 100644 index 00000000000..be669f4b911 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_ptex.c @@ -0,0 +1,1414 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" +#include "BKE_dmgrid.h" +#include "BKE_mesh.h" +#include "BKE_paint.h" +#include "BKE_ptex.h" +#include "BKE_report.h" +#include "BKE_subsurf.h" + +#include "BLI_math.h" +#include "BLI_string.h" + +#include "IMB_imbuf.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "paint_intern.h" +#include "ptex.h" + +#include <assert.h> +#include <stdlib.h> + +static void paint_raycast_cb(PBVHNode *node, void *data_v, float *tmin) +{ + if(BLI_pbvh_node_get_tmin(node) < *tmin) { + PaintStrokeRaycastData *data = data_v; + + if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, NULL, + data->ray_start, data->ray_normal, + &data->dist, NULL, NULL)) { + data->hit |= 1; + *tmin = data->dist; + } + } +} + +static int ptex_paint_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]) +{ + // XXX: sculpt_stroke_modifiers_check(C, ss); + return paint_stroke_get_location(C, stroke, paint_raycast_cb, NULL, out, mouse, 0); +} + +static int ptex_paint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event) +{ + if(paint_stroke_over_mesh(C, op->customdata, event->x, event->y)) { + Object *ob= CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + DerivedMesh *dm; + Mesh *me; + + /* context checks could be a poll() */ + me= get_mesh(ob); + + dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH|CD_MASK_MCOL); + ob->paint->pbvh = dm->getPBVH(ob, dm); + + pbvh_undo_push_begin("Vertex paint"); + + return 1; + } + return 0; +} + +static void ptex_paint_blend(Brush *brush, PaintStroke *stroke, float col[4], float alpha, float co[2]) +{ + float src_img[4], *src; + int tool = brush->vertexpaint_tool; + + if(tool == IMB_BLEND_ADD_ALPHA && + (brush->flag & BRUSH_DIR_IN)) + tool = IMB_BLEND_ERASE_ALPHA; + + if(paint_sample_overlay(stroke, src_img, co)) { + src = src_img; + alpha *= src_img[3]; + } + else + src = brush->rgb; + + IMB_blend_color_float(col, col, src, alpha, tool); +} + +static void ptex_elem_to_float4(PtexDataType type, int channels, void *data, float fcol[4]) +{ + int i; + + /* default alpha */ + fcol[3] = 1; + + switch(type) { + case PTEX_DT_UINT8: + for(i = 0; i < channels; ++i) + fcol[i] = ((unsigned char*)data)[i] / 255.0; + break; + case PTEX_DT_UINT16: + for(i = 0; i < channels; ++i) + fcol[i] = ((unsigned char*)data)[i] / 65535.0; + break; + case PTEX_DT_FLOAT: + for(i = 0; i < channels; ++i) + fcol[i] = ((float*)data)[i]; + break; + default: + break; + } + + if(channels == 1) { + for(i = 1; i < 4; ++i) + fcol[i] = fcol[0]; + } +} + +static void ptex_elem_from_float4(PtexDataType type, int channels, void *data, float fcol[4]) +{ + int i; + + if(channels == 1) { + float avg = (fcol[0]+fcol[1]+fcol[2]) / 3.0f; + switch(type) { + case PTEX_DT_UINT8: + ((unsigned char*)data)[0] = avg * 255; + break; + case PTEX_DT_UINT16: + ((unsigned short*)data)[0] = avg * 65535; + break; + case PTEX_DT_FLOAT: + ((float*)data)[0] = avg; + break; + default: + break; + } + } + else { + switch(type) { + case PTEX_DT_UINT8: + for(i = 0; i < channels; ++i) + ((unsigned char*)data)[i] = fcol[i] * 255; + break; + case PTEX_DT_UINT16: + for(i = 0; i < channels; ++i) + ((unsigned short*)data)[i] = fcol[i] * 65535; + break; + case PTEX_DT_FLOAT: + for(i = 0; i < channels; ++i) + ((float*)data)[i] = fcol[i]; + break; + default: + break; + } + } +} + +static void ptex_get_edge_iter(MPtex *mptex, GridToFace *gtf, + char *data, + int layersize, int edge, int border, + char **start, int *len, int *step) +{ + MPtexSubface *subface = &mptex[gtf->face].subfaces[gtf->offset]; + int *res = subface->res; + int start_offset; + int x, y; + + if(!data) + data = subface->data; + + switch(edge) { + case 0: + x = border; + y = 0; + *len = res[0]; + *step = layersize; + break; + case 2: + x = border; + y = res[1] + border + border - 1; + *len = res[0]; + *step = layersize; + break; + case 1: + x = -1; + y = border + 1; + *len = res[1]; + *step = layersize * (res[0] + border + border); + break; + case 3: + x = 0; + y = border; + *len = res[1]; + *step = layersize * (res[0] + border + border); + break; + } + + start_offset = y * (res[0] + border + border) + x; + *start = data + layersize * start_offset; +} + + + +/* build a ptex grid that includes borders filled with neighbor data, + or repeated data if at a mesh boundary. this could be done more efficient + by not allocating a full grid here, but simpler to get this working first */ +static void *ptex_paint_build_blur_input(DMGridAdjacency *grid_adj, MPtex *mptex, + int grid_index, GridToFace *grid_face_map, + int layersize) +{ + GridToFace *gtf; + MPtexSubface *subface; + char *out; + int i, v; + + gtf = &grid_face_map[grid_index]; + subface = &mptex[gtf->face].subfaces[gtf->offset]; + + out = MEM_mallocN(layersize * (subface->res[0]+2) * (subface->res[1]+2), "blurred_data_input"); + + /* copy center */ + for(v = 0; v < subface->res[1]; ++v) { + memcpy(out + layersize * ((v+1) * (subface->res[0]+2) + 1), + (char*)subface->data + layersize * v * subface->res[0], + layersize * subface->res[0]); + } + + /* fill in borders with adjacent data */ + for(i = 0; i < 4; ++i) { + DMGridAdjacency *adj = &grid_adj[grid_index]; + char *t1, *t2; + float step2_fac; + int j, len1, len2, step1, step2; + + ptex_get_edge_iter(mptex, &grid_face_map[grid_index], out, + layersize, i, 1, &t1, &len1, &step1); + + if(adj->index[i] == -1) { + /* mesh boundary, just repeat existing border */ + ptex_get_edge_iter(mptex, &grid_face_map[grid_index], + NULL, layersize, i, 0, + &t2, &len2, &step2); + } + else { + ptex_get_edge_iter(mptex, &grid_face_map[adj->index[i]], + NULL, layersize, adj->rotation[i], 0, + &t2, &len2, &step2); + } + + step2_fac = (float)len2 / (float)len1; + + for(j = 0; j < len1; ++j) { + memcpy(t1 + step1 * j, + t2 + step2*(int)(step2_fac*j), + layersize); + } + } + + return out; +} + +static void ptex_blur_texel(MPtex *pt, MPtexSubface *subface, + char *blur_input, int layersize, + int u_offset, int v_offset, + float avg[4]) +{ + int i; + + zero_v4(avg); + + for(i = 0; i < 4; ++i) { + float col[4]; + int u = u_offset; + int v = v_offset; + + if(i == 0) u--; + else if(i == 1) v--; + else if(i == 2) u++; + else if(i == 3) v++; + + ptex_elem_to_float4(pt->type, + pt->channels, + blur_input + layersize*((v+1)*(subface->res[0]+2) + u+1), + col); + avg[0] += col[0]; + avg[1] += col[1]; + avg[2] += col[2]; + avg[3] += col[3]; + } + + mul_v4_fl(avg, 1.0f / i); +} + +static void ptex_paint_ptex_from_quad(Brush *brush, PaintStroke *stroke, PaintStrokeTest *test, + MPtex *pt, MPtexSubface *subface, int res[2], + int u_offset, int v_offset, int layersize, + char *blur_input, + float v1[3], float v2[3], float v3[3], float v4[3]) + +{ + char *data = (char*)subface->data + layersize * (v_offset * subface->res[0] + u_offset); + float dtop[3], dbot[3], xoffset, yinterp, ustep, vstep; + float co_bot[3], co_top[3], start_top[3], start_bot[3]; + int u, v; + + /* start of top and bottom "rails" */ + copy_v3_v3(start_top, v4); + copy_v3_v3(start_bot, v1); + + /* direction of "rails" */ + sub_v3_v3v3(dtop, v3, v4); + sub_v3_v3v3(dbot, v2, v1); + + /* offset to use center of texel rather than corner */ + xoffset = 1.0f / (2 * res[0]); + yinterp = 1.0f / (2 * res[1]); + madd_v3_v3fl(start_top, dtop, xoffset); + madd_v3_v3fl(start_bot, dbot, xoffset); + + ustep = 1.0f / res[0]; + vstep = 1.0f / res[1]; + + /* precalculate interpolation along "rails" */ + mul_v3_fl(dtop, ustep); + mul_v3_fl(dbot, ustep); + + for(v = 0; v < res[1]; ++v) { + copy_v3_v3(co_top, start_top); + copy_v3_v3(co_bot, start_bot); + + for(u = 0; u < res[0]; ++u) { + float co[3]; + + interp_v3_v3v3(co, co_bot, co_top, yinterp); + + if(paint_stroke_test(test, co)) { + float strength; + float fcol[4]; + char *elem = data + layersize*(v*subface->res[0] + u); + + strength = brush->alpha * + paint_stroke_combined_strength(stroke, test->dist, co, 0); + + ptex_elem_to_float4(pt->type, pt->channels, elem, fcol); + + if(blur_input) { + float blurcol[4]; + + ptex_blur_texel(pt, subface, + blur_input, layersize, + u_offset + u, v_offset + v, + blurcol); + + interp_v4_v4v4(fcol, fcol, blurcol, strength); + } + else + ptex_paint_blend(brush, stroke, fcol, strength, co); + + ptex_elem_from_float4(pt->type, pt->channels, elem, fcol); + } + + add_v3_v3(co_bot, dbot); + add_v3_v3(co_top, dtop); + } + + yinterp += vstep; + } +} + +static void ptex_paint_node_grids(Brush *brush, PaintStroke *stroke, + DMGridData **grids, GridKey *gridkey, + GridToFace *grid_face_map, + DMGridAdjacency *grid_adj, + CustomData *fdata, + int *grid_indices, + int totgrid, int gridsize) +{ + PaintStrokeTest test; + MPtex *mptex; + int i; + + mptex = CustomData_get_layer(fdata, CD_MPTEX); + + paint_stroke_test_init(&test, stroke); + + for(i = 0; i < totgrid; ++i) { + int g = grid_indices[i]; + DMGridData *grid = grids[g]; + GridToFace *gtf = &grid_face_map[g]; + MPtex *pt = &mptex[gtf->face]; + MPtexSubface *subface = &pt->subfaces[gtf->offset]; + char *blur_input = NULL; + int u, v, x, y, layersize, res[2]; + + /* ignore hidden and masked subfaces */ + if(subface->flag & (MPTEX_SUBFACE_HIDDEN|MPTEX_SUBFACE_MASKED)) + continue; + + layersize = pt->channels * ptex_data_size(pt->type); + + if(brush->vertexpaint_tool == VERTEX_PAINT_BLUR) { + blur_input = ptex_paint_build_blur_input(grid_adj, mptex, + g, grid_face_map, + layersize); + } + + res[0] = MAX2(subface->res[0] / (gridsize - 1), 1); + res[1] = MAX2(subface->res[1] / (gridsize - 1), 1); + + for(v = 0, y = 0; v < subface->res[1]; v += res[1], ++y) { + for(u = 0, x = 0; u < subface->res[0]; u += res[0], ++x) { + float *co[4] = { + GRIDELEM_CO_AT(grid, y*gridsize+x, gridkey), + GRIDELEM_CO_AT(grid, y*gridsize+(x+1), gridkey), + + GRIDELEM_CO_AT(grid, (y+1)*gridsize+(x+1), gridkey), + GRIDELEM_CO_AT(grid, (y+1)*gridsize+x, gridkey), + }; + + ptex_paint_ptex_from_quad(brush, stroke, &test, + pt, subface, res, u, v, + layersize, blur_input, + co[0], co[1], co[2], co[3]); + } + } + + if(blur_input) + MEM_freeN(blur_input); + } +} + +static void ptex_paint_nodes(VPaint *vp, PaintStroke *stroke, + Scene *scene, Object *ob, + PBVHNode **nodes, int totnode) +{ + PBVH *pbvh = ob->paint->pbvh; + Brush *brush = paint_brush(&vp->paint); + CustomData *vdata = NULL; + CustomData *fdata = NULL; + GridToFace *grid_face_map; + int n; + + assert(BLI_pbvh_uses_grids(pbvh)); + + BLI_pbvh_get_customdata(pbvh, &vdata, &fdata); + grid_face_map = BLI_pbvh_get_grid_face_map(pbvh); + + for(n = 0; n < totnode; ++n) { + DMGridData **grids; + DMGridAdjacency *grid_adj; + GridKey *gridkey; + int *grid_indices; + int totgrid, gridsize; + + pbvh_undo_push_node(nodes[n], PBVH_UNDO_PTEX, ob, scene); + + BLI_pbvh_node_get_grids(pbvh, nodes[n], + &grid_indices, &totgrid, NULL, + &gridsize, &grids, &grid_adj, &gridkey); + + ptex_paint_node_grids(brush, stroke, + grids, gridkey, + grid_face_map, + grid_adj, fdata, + grid_indices, + totgrid, gridsize); + + BLI_pbvh_node_set_flags(nodes[n], + SET_INT_IN_POINTER(PBVH_UpdateColorBuffers| + PBVH_UpdateRedraw)); + } +} + +static void ptex_paint_restore_node(PBVH *pbvh, PBVHNode *node, PBVHUndoNode *unode, + CustomData *fdata, GridToFace *grid_face_map) +{ + MPtex *mptex; + int *grid_indices, totgrid, i; + + mptex = CustomData_get_layer_named(fdata, CD_MPTEX, + (char*)pbvh_undo_node_mptex_name(unode)); + + grid_face_map = BLI_pbvh_get_grid_face_map(pbvh); + + BLI_pbvh_node_get_grids(pbvh, node, + &grid_indices, &totgrid, + NULL, NULL, NULL, NULL, NULL); + + for(i = 0; i < totgrid; i++) { + GridToFace *gtf = &grid_face_map[grid_indices[i]]; + MPtex *pt = &mptex[gtf->face]; + MPtexSubface *subface = &pt->subfaces[gtf->offset]; + int layersize; + + layersize = pt->channels * ptex_data_size(pt->type); + + memcpy(subface->data, pbvh_undo_node_mptex_data(unode, i), + layersize * subface->res[0] * subface->res[1]); + } + + BLI_pbvh_node_set_flags(node, SET_INT_IN_POINTER(PBVH_UpdateColorBuffers| + PBVH_UpdateRedraw)); +} + +static void ptex_paint_restore(VPaint *vp, Object *ob) +{ + Brush *brush = paint_brush(&vp->paint); + PBVH *pbvh = ob->paint->pbvh; + + /* Restore the mesh before continuing with anchored stroke */ + if((brush->flag & BRUSH_ANCHORED) || + (brush->flag & BRUSH_RESTORE_MESH)) + { + PBVHNode **nodes; + CustomData *fdata; + GridToFace *grid_face_map; + int n, totnode; + + BLI_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + grid_face_map = BLI_pbvh_get_grid_face_map(pbvh); + BLI_pbvh_get_customdata(pbvh, NULL, &fdata); + + for(n = 0; n < totnode; n++) { + PBVHUndoNode *unode; + + unode= pbvh_undo_get_node(nodes[n]); + if(unode) { + ptex_paint_restore_node(pbvh, nodes[n], unode, fdata, grid_face_map); + } + } + + if(nodes) + MEM_freeN(nodes); + } +} + +static void ptex_stitch_subfaces() +{ + +} + +static void ptex_paint_stroke_update_step(bContext *C, PaintStroke *stroke, + PointerRNA *itemptr) +{ + VPaint *vp= CTX_data_tool_settings(C)->vpaint; + Object *ob = CTX_data_active_object(C); + + ptex_paint_restore(vp, ob); + + paint_stroke_apply_brush(C, stroke, &vp->paint); + + if(paint_brush(&vp->paint)->vertexpaint_tool == VERTEX_PAINT_BLUR) + ptex_stitch_subfaces(); + + /* partial redraw */ + paint_tag_partial_redraw(C, ob); +} + +static void ptex_paint_stroke_brush_action(bContext *C, PaintStroke *stroke) +{ + + VPaint *vp= CTX_data_tool_settings(C)->vpaint; + ViewContext *vc = paint_stroke_view_context(stroke); + Scene *scene = CTX_data_scene(C); + Object *ob = vc->obact; + PBVHSearchSphereData search_data; + PBVHNode **nodes; + int totnode; + float center[3], radius; + + paint_stroke_symmetry_location(stroke, center); + + search_data.center = center; + + radius = paint_stroke_radius(stroke); + search_data.radius_squared = radius*radius; + search_data.original = 0; + + BLI_pbvh_search_gather(ob->paint->pbvh, BLI_pbvh_search_sphere_cb, + &search_data, &nodes, &totnode); + + ptex_paint_nodes(vp, stroke, scene, ob, nodes, totnode); + + if(nodes) + MEM_freeN(nodes); +} + +static void ptex_paint_stroke_done(bContext *C, struct PaintStroke *stroke) +{ + pbvh_undo_push_end(); +} + +static int ptex_paint_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + op->customdata = paint_stroke_new(C, + ptex_paint_stroke_get_location, + ptex_paint_stroke_test_start, + ptex_paint_stroke_update_step, + NULL, + ptex_paint_stroke_brush_action, + ptex_paint_stroke_done); + + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + op->type->modal(C, op, event); + + return OPERATOR_RUNNING_MODAL; +} + +void PAINT_OT_vertex_paint(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Vertex Paint"; + ot->idname= "PAINT_OT_vertex_paint"; + + /* api callbacks */ + ot->invoke= ptex_paint_invoke; + ot->modal= paint_stroke_modal; + ot->exec= paint_stroke_exec; + ot->poll= vertex_paint_poll; + + /* flags */ + ot->flag= OPTYPE_BLOCKING; + + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); +} + +static EnumPropertyItem ptex_layer_type_items[]= { + {PTEX_DT_UINT8, "PTEX_DT_UINT8", 0, "8-bit channels", ""}, + {PTEX_DT_UINT16, "PTEX_DT_UINT16", 0, "16-bit channels", ""}, + {PTEX_DT_FLOAT, "PTEX_DT_FLOAT", 0, "32-bit floating-point channels", ""}, + + {0, NULL, 0, NULL, NULL}}; + +static int ptex_active_layer_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if(ob) { + Mesh *me = get_mesh(ob); + if(me) + return !!CustomData_get_layer(&me->fdata, CD_MPTEX); + } + return 0; +} + +static int ptex_layer_convert_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + MPtex *mptex; + PtexDataType new_type; + int i, j, x, y; + + new_type = RNA_enum_get(op->ptr, "type"); + mptex = CustomData_get_layer(&me->fdata, CD_MPTEX); + + for(i = 0; i < me->totface; ++i) { + MPtex *pt = &mptex[i]; + + for(j = 0; j < mptex[i].totsubface; ++j) { + MPtexSubface *subface = &pt->subfaces[j]; + int orig_layersize, new_layersize; + float *f; + char *orig_data, *new_data, *new_data_start; + + orig_layersize = pt->channels * ptex_data_size(pt->type); + new_layersize = pt->channels * ptex_data_size(new_type); + + f = MEM_callocN(sizeof(float) * pt->channels, "tmp ptex elem"); + new_data_start = new_data = + MEM_callocN(new_layersize * subface->res[0] * subface->res[1], + "mptex converted data"); + orig_data = subface->data; + + for(y = 0; y < subface->res[1]; ++y) { + for(x = 0; x < subface->res[0]; ++x, + orig_data += orig_layersize, + new_data += new_layersize) { + ptex_elem_to_floats(pt->type, pt->channels, orig_data, f); + ptex_elem_from_floats(new_type, pt->channels, new_data, f); + } + } + + MEM_freeN(subface->data); + subface->data = new_data_start; + MEM_freeN(f); + } + + pt->type = new_type; + } + + return OPERATOR_FINISHED; +} + +void PTEX_OT_layer_convert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Convert Layer"; + ot->description= "Convert Ptex data to another type"; + ot->idname= "PTEX_OT_layer_convert"; + + /* api callbacks */ + ot->exec= ptex_layer_convert_exec; + ot->poll= ptex_active_layer_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, "type", ptex_layer_type_items, PTEX_DT_FLOAT, "Type", "Layer channels and data type"); +} + +static int next_power_of_two(int n) +{ + n--; + n = (n >> 1) | n; + n = (n >> 2) | n; + n = (n >> 4) | n; + n = (n >> 8) | n; + n = (n >> 16) | n; + n++; + + return n; +} + +static const void *ptex_default_data(PtexDataType type) { + static const unsigned char ptex_def_val_uc[] = {255, 255, 255, 255}; + static const unsigned short ptex_def_val_us[] = {65535, 65535, 65535, 65535}; + static const float ptex_def_val_f[] = {1, 1, 1, 1}; + + switch(type) { + case PTEX_DT_UINT8: + return ptex_def_val_uc; + case PTEX_DT_UINT16: + return ptex_def_val_us; + case PTEX_DT_FLOAT: + return ptex_def_val_f; + default: + return NULL; + }; +} + +/* add a new ptex layer + automatically sets resolution based on face area */ +static int ptex_layer_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + MPtex *mptex; + float (*limit_pos)[3], *face_area, totarea; + float density; + float largest_face_area = 0; + const void *def_val; + PtexDataType type; + int totchannel; + int layer_size; + int tottexel = 0; + int active_offset; + int i, j; + + type = RNA_enum_get(op->ptr, "type"); + totchannel = RNA_int_get(op->ptr, "channels"); + layer_size = ptex_data_size(type) * totchannel; + def_val = ptex_default_data(type); + + active_offset = CustomData_number_of_layers(&me->fdata, CD_MPTEX); + mptex = CustomData_add_layer(&me->fdata, CD_MPTEX, CD_CALLOC, + NULL, me->totface); + CustomData_set_layer_active(&me->fdata, CD_MPTEX, active_offset); + + /* TODO: for now i'm allocating texels based on limit surface area; + according to ptex paper it's better to use surface derivatives */ + + limit_pos = MEM_callocN(sizeof(float)*3*me->totvert, "limit_pos"); + face_area = MEM_callocN(sizeof(float)*me->totface, "face_area"); + subsurf_calculate_limit_positions(me, limit_pos); + for(i = 0, totarea = 0; i < me->totface; ++i) { + MFace *f = &me->mface[i]; + if(f->v4) { + face_area[i] = area_quad_v3(limit_pos[f->v1], limit_pos[f->v2], + limit_pos[f->v3], limit_pos[f->v4]); + } + else { + face_area[i] = area_tri_v3(limit_pos[f->v1], limit_pos[f->v2], + limit_pos[f->v3]); + } + largest_face_area = MAX2(largest_face_area, face_area[i]); + totarea += face_area[i]; + } + + /* try to make the density factor less dependent on mesh size */ + density = RNA_float_get(op->ptr, "density") * 1000 / largest_face_area; + + for(i = 0; i < me->totface; ++i) { + int S = me->mface[i].v4 ? 4 : 3; + int ures; + int vres; + int gridsize; + char *data; + + if(S == 4) { + /* adjust u and v resolution by the ration + between the average edge size in u and v + directions */ + float len1 = (len_v3v3(limit_pos[me->mface[i].v1], + limit_pos[me->mface[i].v2]) + + len_v3v3(limit_pos[me->mface[i].v3], + limit_pos[me->mface[i].v4])) * 0.5f; + float len2 = (len_v3v3(limit_pos[me->mface[i].v2], + limit_pos[me->mface[i].v3]) + + len_v3v3(limit_pos[me->mface[i].v4], + limit_pos[me->mface[i].v1])) * 0.5f; + float r = len2/len1; + + ures = next_power_of_two(sqrtf((face_area[i] * density) * r)) / 2; + vres = next_power_of_two(sqrtf((face_area[i] * density) / r)) / 2; + } + else { + /* do triangles uniform (subfaces) */ + ures = sqrtf(face_area[i] * (density / 3.0f)); + vres = ures = next_power_of_two(ures); + } + + ures = MAX2(ures, 1); + vres = MAX2(vres, 1); + gridsize = ures * vres; + + mptex[i].totsubface = S; + mptex[i].type = type; + mptex[i].channels = totchannel; + + for(j = 0; j < S; ++j) { + int texels, k; + + mptex[i].subfaces[j].res[0] = ures; + mptex[i].subfaces[j].res[1] = vres; + + texels = ures*vres; + data = mptex[i].subfaces[j].data = + MEM_callocN(layer_size * texels, "MptexSubface.data"); + tottexel += texels; + + for(k = 0; k < texels; ++k) { + memcpy(data, def_val, layer_size); + data += layer_size; + } + } + } + + printf("total texels = %d, sqrt(texels)=%.1f\n", tottexel, sqrtf(tottexel)); + + MEM_freeN(face_area); + MEM_freeN(limit_pos); + + return OPERATOR_FINISHED; +} + +void PTEX_OT_layer_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Add Layer"; + ot->description= "Add a new ptex layer"; + ot->idname= "PTEX_OT_layer_add"; + + /* api callbacks */ + ot->exec= ptex_layer_add_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float(ot->srna, "density", 10, 0, 6000, "Density", "Density of texels to generate", 0, 6000); + RNA_def_int(ot->srna, "channels", 3, 1, 4, "Channels", "", 1, 4); + RNA_def_enum(ot->srna, "type", ptex_layer_type_items, PTEX_DT_FLOAT, "Type", "Layer channels and data type"); +} + +static int ptex_layer_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + + CustomData_free_layer_active(&me->fdata, CD_MPTEX, + me->totface); + + if((ob->mode & OB_MODE_VERTEX_PAINT) && + !CustomData_number_of_layers(&me->fdata, CD_MPTEX)) + ED_object_toggle_modes(C, OB_MODE_VERTEX_PAINT); + + return OPERATOR_FINISHED; +} + +void PTEX_OT_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Remove Layer"; + ot->description= "Remove active ptex layer"; + ot->idname= "PTEX_OT_layer_remove"; + + /* api callbacks */ + ot->exec= ptex_layer_remove_exec; + ot->poll= ptex_active_layer_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +static int ptex_layer_save_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + char str[FILE_MAX]; + + if(!me->totface) + return OPERATOR_CANCELLED; + + RNA_string_get(op->ptr, "filepath", str); + if(!ptex_layer_save_file(me, str)) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; +} + +static int ptex_layer_save_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + /*Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + const char *name; + char buf[FILE_MAX];*/ + + if(RNA_property_is_set(op->ptr, "filepath")) + return ptex_layer_save_exec(C, op); + + /*name = me->fdata.layers[CustomData_get_active_layer_index(&me->fdata, CD_MPTEX)].name; + BLI_snprintf(buf, FILE_MAX, "%s.ptx", name); + + RNA_string_set(op->ptr, "filepath", buf);*/ + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void PTEX_OT_layer_save(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Save Layer"; + ot->description= "Save active ptex layer"; + ot->idname= "PTEX_OT_layer_save"; + + /* api callbacks */ + ot->invoke= ptex_layer_save_invoke; + ot->exec= ptex_layer_save_exec; + ot->poll= ptex_active_layer_poll; + + WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH); +} + +/* loads a .ptx file + makes some assumptions that could be relaxed + later as our ptex implementation is refined + + on the other hand, some unsupported ptex features + are not checked for yet +*/ +int ptex_open_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + + PtexTextureHandle *ptex_texture; + PtexDataType ptex_data_type; + int totchannel; + + char *path; + int i, j; + + path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0); + + ptex_texture = ptex_open(path, 1, 0); + MEM_freeN(path); + + /* check if loader worked */ + if(!ptex_texture) { + BKE_report(op->reports, RPT_ERROR, "Error loading ptex file (see stdout for now, TODO)"); + return OPERATOR_CANCELLED; + } + + /* data type */ + ptex_data_type = ptex_texture_data_type(ptex_texture); + if(ptex_data_type == PTEX_DT_UNSUPPORTED) { + BKE_report(op->reports, RPT_ERROR, "Ptex format unsupported"); + ptex_texture_release(ptex_texture); + return OPERATOR_CANCELLED; + } + + /* data channels */ + totchannel = ptex_texture_num_channels(ptex_texture); + if(totchannel == 2 || totchannel > 4) { + BKE_report(op->reports, RPT_ERROR, "Ptex channel count unsupported"); + ptex_texture_release(ptex_texture); + return OPERATOR_CANCELLED; + } + + /* check that ptex file matches mesh topology */ + for(i = 0, j = 0; i < me->totface; ++i) { + MFace *f = &me->mface[i]; + PtexFaceInfoHandle *ptex_face = ptex_texture_get_face_info(ptex_texture, j); + int subface; + + if(!ptex_face) { + BKE_report(op->reports, RPT_ERROR, "Ptex/mesh topology mismatch"); + ptex_texture_release(ptex_texture); + return OPERATOR_CANCELLED; + } + + subface = ptex_face_info_is_subface(ptex_face); + + if(subface != (f->v4 == 0)) { + BKE_report(op->reports, RPT_ERROR, "Ptex/mesh topology mismatch"); + ptex_texture_release(ptex_texture); + return OPERATOR_CANCELLED; + } + + j += (f->v4 ? 1 : 3); + } + + ptex_layer_from_file(me, ptex_texture); + + return OPERATOR_FINISHED; +} + +static int ptex_open_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; +} + +void PTEX_OT_open(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Open"; + ot->idname= "PTEX_OT_open"; + + /* api callbacks */ + ot->exec= ptex_open_exec; + ot->invoke= ptex_open_invoke; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_RELPATH); +} + +typedef enum { + RES_OP_NUMERIC, + RES_OP_DOUBLE, + RES_OP_HALF +} PtexResOp; + +static void ptex_face_resolution_set(MPtex *pt, int offset, ToolSettings *ts, PtexResOp op) +{ + int i; + + for(i = 0; i < pt->totsubface; ++i) { + int ures, vres; + + if(i == offset || pt->totsubface == 4) { + MPtexSubface *subface = &pt->subfaces[i]; + + switch(op) { + case RES_OP_NUMERIC: + ures = ts->ptex_ures; + vres = ts->ptex_vres; + if(pt->totsubface == 4) { + ures >>= 1; + vres >>= 1; + } + break; + case RES_OP_DOUBLE: + ures = subface->res[0] << 1; + vres = subface->res[1] << 1; + break; + case RES_OP_HALF: + ures = subface->res[0] >> 1; + vres = subface->res[1] >> 1; + break; + } + + if(ures < 1) ures = 1; + if(vres < 1) vres = 1; + + ptex_subface_scale(pt, subface, ures, vres); + } + } +} + +static void ptex_redraw_selected(PBVHNode *node, void *data) +{ + PBVH *pbvh = data; + MPtex *mptex; + GridToFace *grid_face_map; + CustomData *fdata; + int totgrid, *grid_indices, i; + + BLI_pbvh_get_customdata(pbvh, NULL, &fdata); + mptex = CustomData_get_layer(fdata, CD_MPTEX); + grid_face_map = BLI_pbvh_get_grid_face_map(pbvh); + BLI_pbvh_node_get_grids(pbvh, node, + &grid_indices, &totgrid, NULL, NULL, + NULL, NULL, NULL); + + for(i = 0; i < totgrid; ++i) { + GridToFace *gtf = &grid_face_map[grid_indices[i]]; + if(mptex[gtf->face].subfaces[gtf->offset].flag & MPTEX_SUBFACE_SELECTED) { + BLI_pbvh_node_set_flags(node, + SET_INT_IN_POINTER(PBVH_UpdateColorBuffers| + PBVH_UpdateRedraw)); + } + } +} + +static int ptex_face_resolution_set_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + MPtex *mptex = CustomData_get_layer(&me->fdata, CD_MPTEX); + PtexResOp operation = RNA_enum_get(op->ptr, "operation"); + int i, j; + + for(i = 0; i < me->totface; ++i) { + for(j = 0; j < mptex[i].totsubface; ++j) { + if(mptex[i].subfaces[j].flag & MPTEX_SUBFACE_SELECTED) { + ptex_face_resolution_set(mptex + i, j, ts, operation); + if(mptex[i].totsubface == 4) + break; + } + } + } + + BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL, ptex_redraw_selected, ob->paint->pbvh); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static int ptex_face_resolution_set_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if(ob) { + Mesh *me = get_mesh(ob); + if(me && CustomData_get_layer(&me->fdata, CD_MPTEX)) + return 1; + } + return 0; +} + +void PTEX_OT_face_resolution_set(wmOperatorType *ot) +{ + static EnumPropertyItem op_items[] = { + {RES_OP_NUMERIC, "NUMERIC", 0, "Numeric", ""}, + {RES_OP_DOUBLE, "DOUBLE", 0, "Double", ""}, + {RES_OP_HALF, "HALF", 0, "Half", ""}, + {0, NULL, 0, NULL, NULL}}; + + /* identifiers */ + ot->name= "Set Face Resolution"; + ot->idname= "PTEX_OT_face_resolution_set"; + + /* api callbacks */ + ot->exec= ptex_face_resolution_set_exec; + ot->poll= ptex_face_resolution_set_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "operation", op_items, RES_OP_NUMERIC, "Operation", "How to modify the resolution"); +} + +typedef struct { + int grid_index; + PBVHNode *node; +} PtexSelectData; + +static void select_raycast_cb(PBVHNode *node, void *data_v, float *tmin) +{ + if(BLI_pbvh_node_get_tmin(node) < *tmin) { + PaintStrokeRaycastData *data = data_v; + PtexSelectData *mode_data = data->mode_data; + + if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, NULL, + data->ray_start, data->ray_normal, + &data->dist, &mode_data->grid_index, NULL)) { + data->hit |= 1; + *tmin = data->dist; + mode_data->node = node; + } + } +} + +static int ptex_subface_select_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + ViewContext vc; + float out[3], mouse[2] = {event->x, event->y}; + PtexSelectData mode_data; + + view3d_set_viewcontext(C, &vc); + if(paint_util_raycast(&vc, select_raycast_cb, &mode_data, out, mouse, 0)) { + PBVH *pbvh = ob->paint->pbvh; + GridToFace *grid_face_map, *gtf; + CustomData *fdata; + MPtex *mptex, *pt; + int *grid_indices; + int i, j; + + grid_face_map = BLI_pbvh_get_grid_face_map(pbvh); + BLI_pbvh_get_customdata(pbvh, NULL, &fdata); + BLI_pbvh_node_get_grids(pbvh, mode_data.node, + &grid_indices, NULL, NULL, NULL, + NULL, NULL, NULL); + + mptex = CustomData_get_layer(fdata, CD_MPTEX); + + /* deselect everything */ + if(!RNA_boolean_get(op->ptr, "extend")) { + for(i = 0; i < me->totface; ++i) { + for(j = 0; j < mptex[i].totsubface; ++j) + mptex[i].subfaces[j].flag &= ~MPTEX_SUBFACE_SELECTED; + } + } + + gtf = &grid_face_map[grid_indices[mode_data.grid_index]]; + pt = &mptex[gtf->face]; + + if(pt->totsubface == 4) { + for(i = 0; i < 4; ++i) + pt->subfaces[i].flag ^= MPTEX_SUBFACE_SELECTED; + } + else + pt->subfaces[gtf->offset].flag ^= MPTEX_SUBFACE_SELECTED; + + me->act_face = gtf->face; + me->act_subface = gtf->offset; + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + } + + return OPERATOR_FINISHED; +} + +static int ptex_edit_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if(ob && (ob->mode & OB_MODE_VERTEX_PAINT)) { + Mesh *me = get_mesh(ob); + return me && (me->editflag & ME_EDIT_PTEX); + } + + return 0; +} + +void PTEX_OT_subface_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Subface"; + ot->idname= "PTEX_OT_subface_select"; + + /* api callbacks */ + ot->invoke= ptex_subface_select_invoke; + ot->poll= ptex_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "extend", 0, "Extend", ""); +} + +static int ptex_select_all_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + MPtex *mptex; + int i, j, action = RNA_enum_get(op->ptr, "action"); + + mptex = CustomData_get_layer(&me->fdata, CD_MPTEX); + + if(action == SEL_TOGGLE) { + for(i = 0; i < me->totface; ++i) { + for(j = 0; j < mptex[i].totsubface; ++j) { + if(mptex[i].subfaces[j].flag & MPTEX_SUBFACE_SELECTED) { + action = SEL_DESELECT; + break; + } + } + } + } + + if(action == SEL_TOGGLE) + action = SEL_SELECT; + + for(i = 0; i < me->totface; ++i) { + for(j = 0; j < mptex[i].totsubface; ++j) { + MPtexSubface *subface = &mptex[i].subfaces[j]; + switch(action) { + case SEL_SELECT: + subface->flag |= MPTEX_SUBFACE_SELECTED; + break; + case SEL_DESELECT: + subface->flag &= ~MPTEX_SUBFACE_SELECTED; + break; + case SEL_INVERT: + subface->flag ^= MPTEX_SUBFACE_SELECTED; + break; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void PTEX_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select or Deselect All"; + ot->description= "Change selection of all ptex faces"; + ot->idname= "PTEX_OT_select_all"; + + /* api callbacks */ + ot->exec= ptex_select_all_exec; + ot->poll= ptex_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + WM_operator_properties_select_all(ot); +} + +int ptex_subface_flag_set_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + MPtex *mptex = CustomData_get_layer(&me->fdata, CD_MPTEX); + int flag = RNA_enum_get(op->ptr, "flag"); + int set = RNA_boolean_get(op->ptr, "set"); + int ignore_unselected = RNA_boolean_get(op->ptr, "ignore_unselected"); + int ignore_hidden = RNA_boolean_get(op->ptr, "ignore_hidden"); + int i, j; + + for(i = 0; i < me->totface; ++i) { + for(j = 0; j < mptex[i].totsubface; ++j) { + MPtexSubface *subface = &mptex[i].subfaces[j]; + + if((!ignore_unselected || (subface->flag & MPTEX_SUBFACE_SELECTED)) && + (!ignore_hidden || !(subface->flag & MPTEX_SUBFACE_HIDDEN))) { + if(set) + subface->flag |= flag; + else + subface->flag &= ~flag; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +void PTEX_OT_subface_flag_set(wmOperatorType *ot) +{ + static EnumPropertyItem flag_items[] = { + {MPTEX_SUBFACE_HIDDEN, "HIDDEN", 0, "Hidden", ""}, + {MPTEX_SUBFACE_MASKED, "MASKED", 0, "Masked", ""}, + {0, NULL, 0, NULL, NULL}}; + + /* identifiers */ + ot->name= "Set Subface Flags"; + ot->description= "Set or clear a flag from ptex subfaces"; + ot->idname= "PTEX_OT_subface_flag_set"; + + /* api callbacks */ + ot->exec= ptex_subface_flag_set_exec; + ot->poll= ptex_active_layer_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "flag", flag_items, 0, "Flag", ""); + RNA_def_boolean(ot->srna, "set", 1, "Set", "Set the flag if true, otherwise clear the flag"); + RNA_def_boolean(ot->srna, "ignore_unselected", 1, "Ignore Unselected", "Don't change the flags of unselected faces"); + RNA_def_boolean(ot->srna, "ignore_hidden", 1, "Ignore Hidden", "Don't change the flags of hidden faces"); +} diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index ea99844bfac..50d1286042b 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -34,15 +34,19 @@ #include "RNA_access.h" +#include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_paint.h" -#include "BKE_brush.h" #include "WM_api.h" #include "WM_types.h" #include "BLI_math.h" +#include "BLI_rand.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -57,28 +61,62 @@ #include <float.h> #include <math.h> -typedef struct PaintStroke { +struct PaintStroke { void *mode_data; void *smooth_stroke_cursor; wmTimer *timer; /* Cached values */ ViewContext vc; + float project_mat[4][4]; bglMats mats; Brush *brush; - - float last_mouse_position[2]; + /* brush location (object space) */ + float location[3], symmetry_location[3]; + /* screen-space brush location */ + float mouse[2], initial_mouse[2], last_mouse_position[2]; + /* not always the same as brush_size() */ + int pixel_radius; + /* tablet pressure, or 1 if using mouse */ + float pressure; + /* 3d brush radius */ + float radius, radius_squared, initial_radius; + /* previous location of updating rake rotation */ + float last_rake[2]; + /* this value is added to the brush's rotation in calculations */ + float rotation; + /* mouse location used for texturing */ + float tex_mouse[2]; + /* symmetry */ + /* current symmetry pass (0-7) */ + int mirror_symmetry_pass; + int radial_symmetry_pass; + float symm_rot_mat[4][4]; + float symm_rot_mat_inv[4][4]; + /* decrease brush strength if symmetry overlaps */ + float feather; + + /* anything special that sculpt or other paint modes need + to do should go through these modifiers */ + float modifier_initial_radius_factor; + int modifier_use_original_texture_coords; + int modifier_use_original_location; /* Set whether any stroke step has yet occurred e.g. in sculpt mode, stroke doesn't start until cursor passes over the mesh */ int stroke_started; + /* 1 if this is the first paint dab, 0 otherwise */ + int first_dab; + /* callbacks */ StrokeGetLocation get_location; StrokeTestStart test_start; StrokeUpdateStep update_step; + StrokeUpdateSymmetry update_symmetry; + StrokeBrushAction brush_action; StrokeDone done; -} PaintStroke; +}; /*** Cursor ***/ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) @@ -323,7 +361,7 @@ static int load_tex(Sculpt *sd, Brush* br, ViewContext* vc) buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex"); - #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(static) if (sd->paint.flags & PAINT_USE_OPENMP) for (j= 0; j < size; j++) { int i; float y; @@ -436,7 +474,7 @@ static void projectf(bglMats *mats, const float v[3], float p[2]) p[1]= uy; } -static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) +int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats) { float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; @@ -476,8 +514,10 @@ static int project_brush_radius(RegionView3D* rv3d, float radius, float location return len_v2v2(p1, p2); } +#if 0 int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4]) { + Object *ob = CTX_data_active_object(C); struct PaintStroke *stroke; float window[2]; int hit; @@ -491,13 +531,13 @@ int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, floa memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16])); memcpy(viewport, stroke->mats.viewport, sizeof(int[4])); - if (stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && sculpt_stroke_get_location(C, stroke, location, window)) { + if (ob && ob->paint && ob->paint->sculpt && ob->paint->pbvh && sculpt_stroke_get_location(C, stroke, location, window)) { *pixel_radius = project_brush_radius(stroke->vc.rv3d, brush_unprojected_radius(stroke->brush), location, &stroke->mats); if (*pixel_radius == 0) *pixel_radius = brush_size(stroke->brush); - mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location); + mul_m4_v3(ob->obmat, location); hit = 1; } @@ -513,6 +553,7 @@ int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, floa return hit; } +#endif // XXX duplicated from sculpt.c float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset) @@ -543,14 +584,16 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused) (void)unused; view3d_set_viewcontext(C, &vc); - - if (vc.obact->sculpt) { + +// XXX +#if 0 + if (vc.obact->paint && vc.obact->paint->sculpt) { Paint *paint = paint_get_active(CTX_data_scene(C)); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = paint_brush(paint); - int pixel_radius, viewport[4]; - float location[3], modelview[16], projection[16]; + int pixel_radius = 0, viewport[4]; + float location[3]; // XXX: modelview[16], projection[16]; int hit; @@ -585,7 +628,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused) if(!brush_use_locked_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) return; - hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport); + hit = 0; if (brush_use_locked_size(brush)) brush_set_size(brush, pixel_radius); @@ -625,7 +668,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused) glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); - + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) { glTranslatef(0.5f, 0.5f, 0); @@ -767,7 +810,11 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused) glPopAttrib(); } - else { + //else +#endif + + + { Paint *paint = paint_get_active(CTX_data_scene(C)); Brush *brush = paint_brush(paint); @@ -787,73 +834,271 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *unused) } } -/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2]) +/**** Symmetry ****/ + +float calc_overlap(PaintStroke *stroke, float location[3], char symm, char axis, float angle) { - Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX - Brush *brush = paint_brush(paint); // XXX + float mirror[3]; + float distsq; + float mat[4][4]; + + //paint_flip_coord(mirror, cache->traced_location, symm); + paint_flip_coord(mirror, location, symm); - float mouse[3]; + unit_m4(mat); + rotate_m4(mat, axis, angle); - PointerRNA itemptr; + mul_m4_v3(mat, mirror); - float location[3]; + //distsq = len_squared_v3v3(mirror, cache->traced_location); + distsq = len_squared_v3v3(mirror, location); - float pressure; - int pen_flip; + if (distsq <= 4*stroke->radius_squared) + return (2*stroke->radius - sqrt(distsq)) / (2*stroke->radius); + else + return 0; +} + +static float calc_radial_symmetry_feather(PaintStroke *stroke, Paint *paint, + float location[3], char symm, char axis) +{ + int i; + float overlap; + + overlap = 0; + for(i = 1; i < paint->radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i / paint->radial_symm[axis-'X']; + overlap += calc_overlap(stroke, location, symm, axis, angle); + } + + return overlap; +} + +static float calc_symmetry_feather(PaintStroke *stroke, Paint *paint, float location[3], char symm) +{ + if(paint->flags & PAINT_SYMMETRY_FEATHER) { + float overlap; + int i; + + overlap = 0; + for (i = 0; i <= symm; i++) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + + overlap += calc_overlap(stroke, location, i, 0, 0); + + overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'X'); + overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'Y'); + overlap += calc_radial_symmetry_feather(stroke, paint, location, i, 'Z'); + } + } + + return 1/overlap; + } + else { + return 1; + } +} + +/* flip data across the axes specified by symm */ +static void calc_symm(bContext *C, PaintStroke *stroke, float location[3], char symm, + char axis, float angle) +{ + /* radial symmetry */ + unit_m4(stroke->symm_rot_mat); + rotate_m4(stroke->symm_rot_mat, axis, angle); + + unit_m4(stroke->symm_rot_mat_inv); + rotate_m4(stroke->symm_rot_mat_inv, axis, -angle); + + /* symmetry_location */ + paint_flip_coord(location, location, symm); + mul_m4_v3(stroke->symm_rot_mat, location); + copy_v3_v3(stroke->symmetry_location, location); + + /* callback */ + if(stroke->update_symmetry) { + stroke->update_symmetry(C, stroke, symm, axis, + angle, + stroke->mirror_symmetry_pass, + stroke->radial_symmetry_pass, + stroke->symm_rot_mat); + } +} + +static void do_radial_symmetry(bContext *C, PaintStroke *stroke, Paint *paint, char symm, int axis, float feather) +{ + int i; + + for(i = 1; i < paint->radial_symm[axis-'X']; ++i) { + const float angle = 2*M_PI*i / paint->radial_symm[axis-'X']; + float location[3]; + + stroke->radial_symmetry_pass= i; + copy_v3_v3(location, stroke->location); + calc_symm(C, stroke, location, symm, axis, angle); + + stroke->brush_action(C, stroke); + } +} + +static void paint_stroke_update_cache(bContext *C, PaintStroke *stroke, Paint *paint) +{ + ViewContext *vc = &stroke->vc; + + if(stroke->first_dab) { + copy_v2_v2(stroke->initial_mouse, stroke->mouse); + copy_v2_v2(stroke->tex_mouse, stroke->initial_mouse); + + if(!brush_use_locked_size(stroke->brush)) { + stroke->initial_radius = + paint_calc_object_space_radius(vc, + stroke->location, + brush_size(stroke->brush)); + brush_set_unprojected_radius(stroke->brush, stroke->initial_radius); + } + else + stroke->initial_radius= brush_unprojected_radius(stroke->brush); + + stroke->initial_radius *= stroke->modifier_initial_radius_factor; + } + + stroke->pixel_radius = brush_size(stroke->brush); + stroke->radius = stroke->initial_radius; + stroke->feather = calc_symmetry_feather(stroke, paint, stroke->location, paint->flags & 7); + + if(brush_use_size_pressure(stroke->brush)) { + stroke->pixel_radius *= stroke->pressure; + stroke->radius *= stroke->pressure; + } + + if(!((stroke->brush->flag & BRUSH_ANCHORED) || + stroke->modifier_use_original_texture_coords)) { + copy_v2_v2(stroke->tex_mouse, stroke->mouse); + + if((stroke->brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) && + (stroke->brush->flag & BRUSH_RANDOM_ROTATION) && + !(stroke->brush->flag & BRUSH_RAKE)) { + stroke->rotation = 2*M_PI*BLI_frand(); + } + } - ViewContext vc; // XXX + if(stroke->brush->flag & BRUSH_ANCHORED) { + int dx, dy; + + dx = stroke->mouse[0] - stroke->initial_mouse[0]; + dy = stroke->mouse[1] - stroke->initial_mouse[1]; + + stroke->pixel_radius = sqrt(dx*dx + dy*dy); + + stroke->rotation = atan2(dx, dy) + M_PI; + + if(stroke->brush->flag & BRUSH_EDGE_TO_EDGE) { + float d[3]; + float halfway[3]; + float out[3]; + + d[0] = dx; + d[1] = dy; + d[2] = 0; + + mul_v3_v3fl(halfway, d, 0.5f); + add_v3_v3(halfway, stroke->initial_mouse); + + if(stroke->get_location(C, stroke, out, halfway)) { + copy_v2_v2(stroke->tex_mouse, halfway); + copy_v3_v3(stroke->location, out); + stroke->pixel_radius /= 2.0f; + } + } + + stroke->radius= paint_calc_object_space_radius(&stroke->vc, + stroke->location, + stroke->pixel_radius); + } + else if(stroke->brush->flag & BRUSH_RAKE) { + const float u = 0.5f; + const float r = 20; + + const float dx = stroke->last_rake[0] - stroke->mouse[0]; + const float dy = stroke->last_rake[1] - stroke->mouse[1]; + + if(stroke->first_dab) { + copy_v2_v2(stroke->last_rake, stroke->mouse); + } + else if (dx*dx + dy*dy >= r*r) { + stroke->rotation = atan2(dx, dy); + interp_v2_v2v2(stroke->last_rake, stroke->mouse, stroke->last_rake, u); + } + } + + stroke->radius_squared = stroke->radius*stroke->radius; +} + +/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ +static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2]) +{ + Paint *paint = paint_get_active(CTX_data_scene(C)); PaintStroke *stroke = op->customdata; + PointerRNA itemptr; + int pen_flip; - view3d_set_viewcontext(C, &vc); // XXX + view3d_get_object_project_mat(stroke->vc.rv3d, stroke->vc.obact, stroke->project_mat); /* Tablet */ if(event->custom == EVT_DATA_TABLET) { wmTabletData *wmtab= event->customdata; - pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; + stroke->pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; pen_flip = (wmtab->Active == EVT_TABLET_ERASER); } else { - pressure = 1; + stroke->pressure = 1; pen_flip = 0; } // XXX: temporary check for sculpt mode until things are more unified - if (vc.obact->sculpt) { + if(stroke->vc.obact->paint && stroke->vc.obact->paint->sculpt) { float delta[3]; - brush_jitter_pos(brush, mouse_in, mouse); + brush_jitter_pos(stroke->brush, mouse_in, stroke->mouse); // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here - if (brush->flag & BRUSH_JITTER_PRESSURE) { - sub_v3_v3v3(delta, mouse, mouse_in); - mul_v3_fl(delta, pressure); - add_v3_v3v3(mouse, mouse_in, delta); + if(stroke->brush->flag & BRUSH_JITTER_PRESSURE) { + sub_v3_v3v3(delta, stroke->mouse, mouse_in); + mul_v3_fl(delta, stroke->pressure); + add_v3_v3v3(stroke->mouse, mouse_in, delta); } } else - copy_v3_v3(mouse, mouse_in); + copy_v2_v2(stroke->mouse, mouse_in); - /* XXX: can remove the if statement once all modes have this */ - if(stroke->get_location) - stroke->get_location(C, stroke, location, mouse); - else - zero_v3(location); + if(stroke->first_dab || + !((stroke->brush->flag & BRUSH_ANCHORED) || + stroke->modifier_use_original_location)) { + + /* XXX: can remove the following if statement once all modes have this */ + if(stroke->get_location) + stroke->get_location(C, stroke, stroke->location, stroke->mouse); + else + zero_v3(stroke->location); + } /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - RNA_float_set_array(&itemptr, "location", location); - RNA_float_set_array(&itemptr, "mouse", mouse); + RNA_float_set_array(&itemptr, "location", stroke->location); + RNA_float_set_array(&itemptr, "mouse", stroke->mouse); RNA_boolean_set (&itemptr, "pen_flip", pen_flip); - RNA_float_set (&itemptr, "pressure", pressure); + RNA_float_set(&itemptr, "pressure", stroke->pressure); + + copy_v2_v2(stroke->last_mouse_position, stroke->mouse); - stroke->last_mouse_position[0] = mouse[0]; - stroke->last_mouse_position[1] = mouse[1]; + paint_stroke_update_cache(C, stroke, paint); stroke->update_step(C, stroke, &itemptr); + + stroke->first_dab = 0; } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ @@ -862,7 +1107,7 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev output[0] = event->x; output[1] = event->y; - if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && + if((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && !ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) && !(stroke->brush->flag & BRUSH_ANCHORED) && !(stroke->brush->flag & BRUSH_RESTORE_MESH)) @@ -919,7 +1164,7 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const pressure = brush_use_size_pressure(stroke->brush) ? wmtab->Pressure : 1; } - scale = (brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length; + scale = (brush_size(stroke->brush) * pressure * stroke->brush->spacing/50.0f) / length; mul_v2_fl(vec, scale); steps = (int)(1.0f / scale); @@ -937,20 +1182,25 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const /**** Public API ****/ PaintStroke *paint_stroke_new(bContext *C, - StrokeGetLocation get_location, - StrokeTestStart test_start, - StrokeUpdateStep update_step, - StrokeDone done) + StrokeGetLocation get_location, + StrokeTestStart test_start, + StrokeUpdateStep update_step, + StrokeUpdateSymmetry update_symmetry, + StrokeBrushAction brush_action, + StrokeDone done) { PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C))); view3d_set_viewcontext(C, &stroke->vc); view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats); + stroke->modifier_initial_radius_factor = 1; stroke->get_location = get_location; stroke->test_start = test_start; stroke->update_step = update_step; + stroke->update_symmetry = update_symmetry; + stroke->brush_action = brush_action; stroke->done = done; return stroke; @@ -965,7 +1215,6 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) { PaintStroke *stroke = op->customdata; float mouse[2]; - int first= 0; if(!stroke->stroke_started) { stroke->last_mouse_position[0] = event->x; @@ -978,9 +1227,10 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) if(stroke->brush->flag & BRUSH_AIRBRUSH) stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); + + stroke->first_dab = 1; } - first= 1; //ED_region_tag_redraw(ar); } @@ -993,11 +1243,12 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) if(stroke->timer) WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); - stroke->done(C, stroke); + if(stroke->stroke_started) + stroke->done(C, stroke); MEM_freeN(stroke); return OPERATOR_FINISHED; } - else if(first || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) { + else if(stroke->first_dab || ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (event->type == TIMER && (event->customdata == stroke->timer))) { if(stroke->stroke_started) { if(paint_smooth_stroke(stroke, mouse, event)) { if(paint_space_stroke_enabled(stroke->brush)) { @@ -1016,7 +1267,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, 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 && + if(stroke->first_dab && stroke->stroke_started && paint_space_stroke_enabled(stroke->brush) && !(stroke->brush->flag & BRUSH_ANCHORED) && @@ -1028,12 +1279,66 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) return OPERATOR_RUNNING_MODAL; } +void paint_stroke_apply_brush(bContext *C, PaintStroke *stroke, Paint *paint) +{ + char symm = paint->flags & 7; + float location[3]; + int i; + + /* symm is a bitwise 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 */ + for(i = 0; i <= symm; ++i) { + if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + stroke->mirror_symmetry_pass= i; + stroke->radial_symmetry_pass= 0; + + copy_v3_v3(location, stroke->location); + calc_symm(C, stroke, location, i, 0, 0); + + stroke->brush_action(C, stroke); + + do_radial_symmetry(C, stroke, paint, i, 'X', stroke->feather); + do_radial_symmetry(C, stroke, paint, i, 'Y', stroke->feather); + do_radial_symmetry(C, stroke, paint, i, 'Z', stroke->feather); + } + } +} + +/* combines mask, curve, and texture strengths */ +float paint_stroke_combined_strength(PaintStroke *stroke, float dist, float co[3], float mask) +{ + float mco[3]; + + /* if the active area is being applied for symmetry, flip it + across the symmetry axis and rotate it back to the orignal + position in order to project it. This insures that the + brush texture will be oriented correctly. */ + if(stroke->brush->mtex.tex) { + paint_stroke_symmetry_unflip(stroke, mco, co); + co = mco; + } + + return brush_tex_strength(&stroke->vc, + stroke->project_mat, stroke->brush, co, mask, dist, + stroke->pixel_radius, stroke->radius, + stroke->rotation, + stroke->tex_mouse); +} + int paint_stroke_exec(bContext *C, wmOperator *op) { + Paint *paint = paint_get_active(CTX_data_scene(C)); PaintStroke *stroke = op->customdata; RNA_BEGIN(op->ptr, itemptr, "stroke") { + RNA_float_get_array(&itemptr, "location", stroke->location); + RNA_float_get_array(&itemptr, "mouse", stroke->mouse); + stroke->pressure = RNA_float_get(&itemptr, "pressure"); + paint_stroke_update_cache(C, stroke, paint); + stroke->update_step(C, stroke, &itemptr); + + stroke->first_dab = 0; } RNA_END; @@ -1043,10 +1348,7 @@ int paint_stroke_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -ViewContext *paint_stroke_view_context(PaintStroke *stroke) -{ - return &stroke->vc; -} +/**** mode data ****/ void *paint_stroke_mode_data(struct PaintStroke *stroke) { @@ -1058,6 +1360,106 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) stroke->mode_data = mode_data; } +/**** cache access ***/ + +ViewContext *paint_stroke_view_context(PaintStroke *stroke) +{ + return &stroke->vc; +} + +float paint_stroke_feather(struct PaintStroke *stroke) +{ + return stroke->feather; +} + +void paint_stroke_mouse_location(PaintStroke *stroke, float mouse[2]) +{ + copy_v2_v2(mouse, stroke->mouse); +} +void paint_stroke_initial_mouse_location(PaintStroke *stroke, float initial_mouse[2]) +{ + copy_v2_v2(initial_mouse, stroke->initial_mouse); +} + +void paint_stroke_location(PaintStroke *stroke, float location[3]) +{ + copy_v3_v3(location, stroke->location); +} + +float paint_stroke_pressure(struct PaintStroke *stroke) +{ + return stroke->pressure; +} + +void paint_stroke_symmetry_location(struct PaintStroke *stroke, float loc[3]) +{ + copy_v3_v3(loc, stroke->symmetry_location); +} + +float paint_stroke_radius(struct PaintStroke *stroke) +{ + return stroke->radius; +} + +float paint_stroke_radius_squared(struct PaintStroke *stroke) +{ + return stroke->radius_squared; +} + +int paint_stroke_first_dab(PaintStroke *stroke) +{ + return stroke->first_dab; +} + +void paint_stroke_project(PaintStroke *stroke, float loc[3], float out[2]) +{ + view3d_project_float(stroke->vc.ar, loc, out, stroke->project_mat); +} + +void paint_stroke_symmetry_unflip(PaintStroke *stroke, float out[3], float vec[3]) +{ + paint_flip_coord(out, vec, stroke->mirror_symmetry_pass); + + if(stroke->radial_symmetry_pass) + mul_m4_v3(stroke->symm_rot_mat_inv, out); +} + +/**** stroke modifiers ****/ +void paint_stroke_set_modifier_use_original_location(PaintStroke *stroke) +{ + stroke->modifier_use_original_location = 1; +} + +void paint_stroke_set_modifier_initial_radius_factor(PaintStroke *stroke, + float initial_radius_factor) +{ + stroke->modifier_initial_radius_factor = initial_radius_factor; +} + +void paint_stroke_set_modifier_use_original_texture_coords(PaintStroke *stroke) +{ + stroke->modifier_use_original_texture_coords = 1; +} + +/* returns 1 if the mouse is over the mesh, 0 otherwise */ +int paint_stroke_over_mesh(bContext *C, PaintStroke *stroke, int x, int y) +{ + float mouse[2] = {x, y}, co[3]; + return stroke->get_location(C, stroke, co, mouse); +} + +/* Do a raycast in the tree to find the 3d brush location + (This allows us to ignore the GL depth buffer) + Returns 0 if the ray doesn't hit the mesh, non-zero otherwise + */ +int paint_stroke_get_location(bContext *C, PaintStroke *stroke, + BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data, + float out[3], float mouse[2], int original) +{ + ViewContext *vc = paint_stroke_view_context(stroke); + return paint_util_raycast(vc, hit_cb, mode_data, out, mouse, original); +} + int paint_poll(bContext *C) { Paint *p = paint_get_active(CTX_data_scene(C)); @@ -1076,3 +1478,88 @@ void paint_cursor_start(bContext *C, int (*poll)(bContext *C)) p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL); } +/* Optimization for testing if a coord is within the brush area */ + +void paint_stroke_test_init(PaintStrokeTest *test, PaintStroke *stroke) +{ + test->radius_squared= stroke->radius_squared; + copy_v3_v3(test->location, stroke->symmetry_location); +} + +int paint_stroke_test(PaintStrokeTest *test, float co[3]) +{ + float distsq = len_squared_v3v3(co, test->location); + + if(distsq <= test->radius_squared) { + test->dist = sqrt(distsq); + return 1; + } + else { + return 0; + } +} + +int paint_stroke_test_sq(PaintStrokeTest *test, float co[3]) +{ + float distsq = len_squared_v3v3(co, test->location); + + if(distsq <= test->radius_squared) { + test->dist = distsq; + return 1; + } + else { + return 0; + } +} + +int paint_stroke_test_fast(PaintStrokeTest *test, float co[3]) +{ + return len_squared_v3v3(co, test->location) <= test->radius_squared; +} + +int paint_stroke_test_cube(PaintStrokeTest *test, float co[3], float local[4][4]) +{ + const static float side = 0.70710678118654752440084436210485; // sqrt(.5); + + float local_co[3]; + + mul_v3_m4v3(local_co, local, co); + + local_co[0] = fabs(local_co[0]); + local_co[1] = fabs(local_co[1]); + local_co[2] = fabs(local_co[2]); + + if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { + test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side; + + return 1; + } + else { + return 0; + } +} + + +#if 0 + +static int paint_stroke_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) +{ + if (paint_stroke_test_fast(test, co)) { + float t1[3], t2[3], t3[3], dist; + + sub_v3_v3v3(t1, location, co); + sub_v3_v3v3(t2, x2, location); + + cross_v3_v3v3(t3, an, t1); + + dist = len_v3(t3)/len_v3(t2); + + test->dist = dist; + + return 1; + } + + return 0; +} + +#endif diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 643e2cd6915..72f42aa1d3c 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -27,13 +27,20 @@ #include "MEM_guardedalloc.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "BLI_listbase.h" +#include "BKE_mesh.h" +#include "BLI_string.h" #include "BKE_utildefines.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_global.h" +#include "BKE_multires.h" #include "ED_sculpt.h" @@ -188,6 +195,242 @@ static void undo_stack_free(UndoStack *stack) stack->current= NULL; } +/**** paint undo for layers (mcol, paintmask) ****/ + +typedef enum { + LAYER_ADDED, + LAYER_REMOVED +} PaintLayerUndoOp; + +struct PaintLayerUndoNode { + struct PaintLayerUndoNode *next, *prev; + + /* customdata type */ + int type; + /* add/remove */ + PaintLayerUndoOp op; + /* only for restoring into its original location */ + int layer_offset; + /* for identifying layer, don't use layer_offset for that */ + char layer_name[32]; + /* copy of a removed layer's data */ + void *layer_data; + void **multires_layer_data; + float strength; + /* length of multires_layer_data array */ + int totface; + /* whether layer has multires data */ + int flag_multires; +}; + +void paint_layer_undo_set_add(PaintLayerUndoNode *unode, char *name) +{ + unode->op = LAYER_ADDED; + + /* check for restore */ + if(unode->layer_name != name) { + BLI_strncpy(unode->layer_name, name, + sizeof(unode->layer_name)); + } + + unode->totface = 0; +} + +void paint_layer_undo_set_remove(PaintLayerUndoNode *unode, char *name, + CustomData *data, CustomData *fdata, + int totvert, int totface) +{ + CustomDataMultires *cdm; + int ndx; + + unode->op = LAYER_REMOVED; + /* check for restore */ + if(unode->layer_name != name) { + BLI_strncpy(unode->layer_name, name, + sizeof(unode->layer_name)); + } + + unode->totface = totface; + + ndx = CustomData_get_named_layer_index(data, unode->type, name); + assert(ndx >= 0); + + /* store the layer offset so we can re-insert layer at the + same location on undo */ + unode->layer_offset = + ndx - CustomData_get_layer_index(data, unode->type); + + /* backup layer data */ + unode->layer_data = MEM_dupallocN(data->layers[ndx].data); + + unode->strength = data->layers[ndx].strength; + + unode->flag_multires = data->layers[ndx].flag & CD_FLAG_MULTIRES; + if(!unode->flag_multires) + return; + + /* back multires data */ + cdm = CustomData_get_layer(fdata, CD_GRIDS); + if(cdm && totface) { + int i; + + /* check first cdm to see if this layer has multires data */ + if(!CustomData_multires_get_data(cdm, unode->type, name)) + return; + + unode->multires_layer_data = + MEM_callocN(sizeof(void*) * totface, + "PaintLayerUndoNode.multires_layer_data"); + + for(i = 0; i < totface; ++i, ++cdm) { + float *f; + + f = CustomData_multires_get_data(cdm, unode->type, + name); + assert(f); + + unode->multires_layer_data[i] = MEM_dupallocN(f); + } + } +} + +void paint_layer_undo_restore(bContext *C, ListBase *lb) +{ + PaintLayerUndoNode *unode = lb->first; /* only one undo node */ + Object *ob; + Mesh *me; + CustomData *data, *fdata; + CustomDataMultires *cdm; + int i, ndx, offset, active, totelem; + + ob = CTX_data_active_object(C); + me = get_mesh(ob); + fdata = &me->fdata; + + switch(unode->type) { + case CD_MCOL: + data = &me->fdata; + totelem = me->totface; + break; + case CD_PAINTMASK: + data = &me->vdata; + totelem = me->totface; + break; + default: + assert(0); + } + + /* update multires before making changes */ + if(ED_paint_multires_active(CTX_data_scene(C), ob)) + multires_force_update(ob); + + switch(unode->op) { + case LAYER_ADDED: + /* backup current layer data for redo */ + paint_layer_undo_set_remove(unode, unode->layer_name, data, + fdata, me->totvert, me->totface); + + active = CustomData_get_active_layer(data, unode->type); + + /* remove layer */ + ndx = CustomData_get_named_layer_index(data, unode->type, + unode->layer_name); + CustomData_free_layer(data, unode->type, totelem, ndx); + + /* set active layer */ + offset = CustomData_number_of_layers(data, unode->type) - 1; + if(active > offset) + active = offset; + CustomData_set_layer_active(data, unode->type, active); + + /* remove layer's multires data */ + cdm = CustomData_get_layer(fdata, CD_GRIDS); + if(!cdm) + break; + + CustomData_multires_remove_layers(cdm, me->totface, + unode->type, + unode->layer_name); + + break; + case LAYER_REMOVED: + paint_layer_undo_set_add(unode, unode->layer_name); + + /* add layer */ + CustomData_add_layer_at_offset(data, unode->type, CD_ASSIGN, + unode->layer_data, totelem, + unode->layer_offset); + + ndx = CustomData_get_named_layer_index(data, unode->type, + unode->layer_name); + offset = ndx - CustomData_get_layer_index(data, unode->type); + + CustomData_set_layer_active(data, unode->type, offset); + BLI_strncpy(data->layers[ndx].name, unode->layer_name, + sizeof(data->layers[ndx].name)); + data->layers[ndx].strength = unode->strength; + + if(!unode->flag_multires) + break; + + /* add multires layer */ + CustomData_set_layer_offset_flag(data, unode->type, + offset, CD_FLAG_MULTIRES); + + cdm = CustomData_get_layer(fdata, CD_GRIDS); + if(!cdm) + break; + + for(i = 0; i < me->totface; ++i, ++cdm) { + void *griddata = unode->multires_layer_data[i]; + + CustomData_multires_add_layer_data(cdm, unode->type, + unode->layer_name, + griddata); + } + + unode->layer_data = NULL; + if(unode->multires_layer_data) + MEM_freeN(unode->multires_layer_data); + unode->multires_layer_data = NULL; + + break; + } +} + +static void paint_layer_undo_node_free(ListBase *lb) +{ + PaintLayerUndoNode *unode = lb->first; + + if(unode->layer_data) + MEM_freeN(unode->layer_data); + + if(unode->multires_layer_data) { + int i; + + for(i = 0; i < unode->totface; ++i) + MEM_freeN(unode->multires_layer_data[i]); + MEM_freeN(unode->multires_layer_data); + } +} + +PaintLayerUndoNode *paint_layer_undo_push(int type, char *description) +{ + PaintLayerUndoNode *unode; + + undo_paint_push_begin(UNDO_PAINT_MESH, description, + paint_layer_undo_restore, + paint_layer_undo_node_free); + + unode = MEM_callocN(sizeof(PaintLayerUndoNode), "PaintLayerUndoNode"); + unode->type = type; + + BLI_addtail(undo_paint_push_get_list(UNDO_PAINT_MESH), unode); + undo_paint_push_end(UNDO_PAINT_MESH); + + return unode; +} + /* Exported Functions */ void undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free) @@ -243,4 +486,3 @@ void ED_undo_paint_free(void) undo_stack_free(&ImageUndoStack); undo_stack_free(&MeshUndoStack); } - diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index e3a486a0fee..42c56424df4 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -4,6 +4,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -17,10 +18,13 @@ #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" #include "BKE_paint.h" #include "BIF_gl.h" +#include "BIF_glutil.h" #include "ED_view3d.h" #include "ED_screen.h" @@ -31,10 +35,21 @@ #include "WM_api.h" #include "WM_types.h" +#include "RE_render_ext.h" +#include "RE_shader_ext.h" + #include "paint_intern.h" /* 3D Paint */ +void ED_paint_force_update(bContext *C) +{ + Object *ob= CTX_data_active_object(C); + + if(ob && (ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT))) + multires_force_update(ob); +} + static void imapaint_project(Object *ob, float *model, float *proj, float *co, float *pco) { VECCOPY(pco, co); @@ -296,3 +311,319 @@ void PAINT_OT_face_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } + +float paint_calc_object_space_radius(ViewContext *vc, float center[3], + float pixel_radius) +{ + Object *ob = vc->obact; + float delta[3], scale, loc[3]; + + mul_v3_m4v3(loc, ob->obmat, center); + + initgrabz(vc->rv3d, loc[0], loc[1], loc[2]); + window_to_3d_delta(vc->ar, delta, pixel_radius, 0); + + scale= fabsf(mat4_to_scale(ob->obmat)); + scale= (scale == 0.0f)? 1.0f: scale; + + return len_v3(delta)/scale; +} + +/* Paint modes can handle multires differently from regular meshes, but only + if it's the last modifier on the stack and it is not on level zero */ +struct MultiresModifierData *ED_paint_multires_active(Scene *scene, Object *ob) +{ + Mesh *me= (Mesh*)ob->data; + ModifierData *md, *nmd; + + if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) { + /* multires can't work without displacement layer */ + return NULL; + } + + for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { + if(md->type == eModifierType_Multires) { + MultiresModifierData *mmd= (MultiresModifierData*)md; + + /* Check if any of the modifiers after multires are active + * if not it can use the multires struct */ + for(nmd= md->next; nmd; nmd= nmd->next) + if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) + break; + + if(!nmd && mmd->sculptlvl > 0) + return mmd; + } + } + + return NULL; +} + +/*** BVH Tree ***/ + +/* Get a screen-space rectangle of the modified area */ +static int paint_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, + Object *ob, rcti *rect) +{ + PBVH *pbvh= ob->paint->pbvh; + float bb_min[3], bb_max[3], pmat[4][4]; + int i, j, k; + + view3d_get_object_project_mat(rv3d, ob, pmat); + + if(!pbvh) + return 0; + + BLI_pbvh_redraw_BB(pbvh, bb_min, bb_max); + + rect->xmin = rect->ymin = INT_MAX; + rect->xmax = rect->ymax = INT_MIN; + + if(bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2]) + return 0; + + for(i = 0; i < 2; ++i) { + for(j = 0; j < 2; ++j) { + for(k = 0; k < 2; ++k) { + float vec[3], proj[2]; + vec[0] = i ? bb_min[0] : bb_max[0]; + vec[1] = j ? bb_min[1] : bb_max[1]; + vec[2] = k ? bb_min[2] : bb_max[2]; + view3d_project_float(ar, vec, proj, pmat); + rect->xmin = MIN2(rect->xmin, proj[0]); + rect->xmax = MAX2(rect->xmax, proj[0]); + rect->ymin = MIN2(rect->ymin, proj[1]); + rect->ymax = MAX2(rect->ymax, proj[1]); + } + } + } + + return rect->xmin < rect->xmax && rect->ymin < rect->ymax; +} + +void paint_tag_partial_redraw(bContext *C, Object *ob) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + ARegion *ar = CTX_wm_region(C); + rcti r; + + if(paint_get_redraw_rect(ar, rv3d, ob, &r)) { + //rcti tmp; + + r.xmin += ar->winrct.xmin + 1; + r.xmax += ar->winrct.xmin - 1; + r.ymin += ar->winrct.ymin + 1; + r.ymax += ar->winrct.ymin - 1; + + //tmp = r; + + //if (!BLI_rcti_is_empty(&ss->previous_r)) + // BLI_union_rcti(&r, &ss->previous_r); + + //ss->previous_r= tmp; + + ob->paint->partial_redraw = 1; + ED_region_tag_redraw_partial(ar, &r); + } +} + +void paint_get_redraw_planes(float planes[4][4], ARegion *ar, + RegionView3D *rv3d, Object *ob) +{ + PBVH *pbvh= ob->paint->pbvh; + BoundBox bb; + bglMats mats; + rcti rect; + + memset(&bb, 0, sizeof(BoundBox)); + + view3d_get_transformation(ar, rv3d, ob, &mats); + paint_get_redraw_rect(ar, rv3d,ob, &rect); + +#if 1 + /* use some extra space just in case */ + rect.xmin -= 2; + rect.xmax += 2; + rect.ymin -= 2; + rect.ymax += 2; +#else + /* it was doing this before, allows to redraw a smaller + part of the screen but also gives artifaces .. */ + rect.xmin += 2; + rect.xmax -= 2; + rect.ymin += 2; + rect.ymax -= 2; +#endif + + view3d_calculate_clipping(&bb, planes, &mats, &rect); + mul_m4_fl(planes, -1.0f); + + /* clear redraw flag from nodes */ + if(pbvh) + BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); +} + +float get_tex_pixel(Brush* br, float u, float v) +{ + TexResult texres; + float co[3]; + int hasrgb; + + co[0] = u; + co[1] = v; + co[2] = 0; + + memset(&texres, 0, sizeof(TexResult)); + hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres); + + if (hasrgb & TEX_RGB) + texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta; + + return texres.tin; +} + +/* selectively flip any axis of a coordinate */ +void paint_flip_coord(float out[3], float in[3], const char symm) +{ + if(symm & PAINT_SYMM_X) + out[0]= -in[0]; + else + out[0]= in[0]; + if(symm & PAINT_SYMM_Y) + out[1]= -in[1]; + else + out[1]= in[1]; + if(symm & PAINT_SYMM_Z) + out[2]= -in[2]; + else + out[2]= in[2]; +} + +/* return a multiplier for brush strength at a coordinate, + incorporating texture, curve control, and masking + + TODO: pulled almost directly from sculpt, still needs + to be prettied up +*/ +float brush_tex_strength(ViewContext *vc, + float pmat[4][4], Brush *br, + float co[3], float mask, const float len, + float pixel_radius, float radius3d, + float special_rotation, float tex_mouse[2]) +{ + MTex *mtex = &br->mtex; + float avg= 1; + + if(!mtex->tex) { + avg= 1; + } + else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + float jnk; + + /* Get strength by feeding the vertex + location directly into a texture */ + externtex(mtex, co, &avg, + &jnk, &jnk, &jnk, &jnk, 0); + } + else { + float rotation = -mtex->rot; + float x, y, point_2d[3]; + float radius; + + view3d_project_float(vc->ar, co, point_2d, pmat); + + /* if fixed mode, keep coordinates relative to mouse */ + if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) { + rotation += special_rotation; + + point_2d[0] -= tex_mouse[0]; + point_2d[1] -= tex_mouse[1]; + + radius = pixel_radius; // use pressure adjusted size for fixed mode + + x = point_2d[0]; + y = point_2d[1]; + } + else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED), + leave the coordinates relative to the screen */ + { + radius = brush_size(br); // use unadjusted size for tiled mode + + x = point_2d[0] - vc->ar->winrct.xmin; + y = point_2d[1] - vc->ar->winrct.ymin; + } + + x /= vc->ar->winx; + y /= vc->ar->winy; + + if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + x -= 0.5f; + y -= 0.5f; + } + + x *= vc->ar->winx / radius; + y *= vc->ar->winy / radius; + + /* it is probably worth optimizing for those cases where + the texture is not rotated by skipping the calls to + atan2, sqrtf, sin, and cos. */ + if (rotation > 0.001 || rotation < -0.001) { + const float angle = atan2(y, x) + rotation; + const float flen = sqrtf(x*x + y*y); + + x = flen * cos(angle); + y = flen * sin(angle); + } + + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; + + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; + + avg = get_tex_pixel(br, x, y); + } + + avg += br->texture_sample_bias; + + avg *= brush_curve_strength(br, len, radius3d); /* Falloff curve */ + avg*= 1 - mask; + + return avg; +} + +int paint_util_raycast(ViewContext *vc, + BLI_pbvh_HitOccludedCallback hit_cb, void *mode_data, + float out[3], float mouse[2], int original) +{ + float ray_start[3], ray_end[3], ray_normal[3], dist; + float obimat[4][4]; + float mval[2] = {mouse[0] - vc->ar->winrct.xmin, + mouse[1] - vc->ar->winrct.ymin}; + PaintStrokeRaycastData hit_data; + + viewline(vc->ar, vc->v3d, mval, ray_start, ray_end); + + invert_m4_m4(obimat, vc->obact->obmat); + mul_m4_v3(obimat, ray_start); + mul_m4_v3(obimat, ray_end); + + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist= normalize_v3(ray_normal); + + hit_data.mode_data = mode_data; + hit_data.ob = vc->obact; + hit_data.ray_start = ray_start; + hit_data.ray_normal = ray_normal; + hit_data.dist = dist; + hit_data.hit = 0; + hit_data.original = original; + BLI_pbvh_raycast(vc->obact->paint->pbvh, hit_cb, &hit_data, + ray_start, ray_normal, original); + + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, hit_data.dist); + add_v3_v3(out, ray_start); + + return hit_data.hit; +} diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 3c25d861d2f..b12767d46ad 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -22,7 +22,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Nicholas Bishop * * ***** END GPL LICENSE BLOCK ***** */ @@ -62,30 +62,29 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_deform.h" +#include "BKE_displist.h" +#include "BKE_dmgrid.h" +#include "BKE_global.h" +#include "BKE_image.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_multires.h" #include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_subsurf.h" +#include "BKE_utildefines.h" #include "WM_api.h" #include "WM_types.h" - #include "ED_armature.h" #include "ED_mesh.h" #include "ED_screen.h" #include "ED_view3d.h" -#include "paint_intern.h" +#include "ptex.h" -/* brush->vertexpaint_tool */ -#define VP_MIX 0 -#define VP_ADD 1 -#define VP_SUB 2 -#define VP_MUL 3 -#define VP_BLUR 4 -#define VP_LIGHTEN 5 -#define VP_DARKEN 6 +#include "paint_intern.h" /* polling - retrieve whether cursor should be set or operator should be done */ @@ -102,8 +101,10 @@ int vertex_paint_poll(bContext *C) { if(vertex_paint_mode_poll(C) && paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) { + Object *ob = CTX_data_active_object(C); ScrArea *sa= CTX_wm_area(C); - if(sa->spacetype==SPACE_VIEW3D) { + if(!(get_mesh(ob)->editflag & ME_EDIT_PTEX) && + sa->spacetype==SPACE_VIEW3D) { ARegion *ar= CTX_wm_region(C); if(ar->regiontype==RGN_TYPE_WINDOW) return 1; @@ -187,72 +188,6 @@ unsigned int vpaint_get_current_col(VPaint *vp) return rgba_to_mcol(brush->rgb[0], brush->rgb[1], brush->rgb[2], 1.0f); } -static void do_shared_vertexcol(Mesh *me) -{ - /* if no mcol: do not do */ - /* if tface: only the involved faces, otherwise all */ - MFace *mface; - MTFace *tface; - int a; - short *scolmain, *scol; - char *mcol; - - if(me->mcol==0 || me->totvert==0 || me->totface==0) return; - - scolmain= MEM_callocN(4*sizeof(short)*me->totvert, "colmain"); - - tface= me->mtface; - mface= me->mface; - mcol= (char *)me->mcol; - for(a=me->totface; a>0; a--, mface++, mcol+=16) { - if((tface && tface->mode & TF_SHAREDCOL) || (me->editflag & ME_EDIT_PAINT_MASK)==0) { - scol= scolmain+4*mface->v1; - scol[0]++; scol[1]+= mcol[1]; scol[2]+= mcol[2]; scol[3]+= mcol[3]; - scol= scolmain+4*mface->v2; - scol[0]++; scol[1]+= mcol[5]; scol[2]+= mcol[6]; scol[3]+= mcol[7]; - scol= scolmain+4*mface->v3; - scol[0]++; scol[1]+= mcol[9]; scol[2]+= mcol[10]; scol[3]+= mcol[11]; - if(mface->v4) { - scol= scolmain+4*mface->v4; - scol[0]++; scol[1]+= mcol[13]; scol[2]+= mcol[14]; scol[3]+= mcol[15]; - } - } - if(tface) tface++; - } - - a= me->totvert; - scol= scolmain; - while(a--) { - if(scol[0]>1) { - scol[1]/= scol[0]; - scol[2]/= scol[0]; - scol[3]/= scol[0]; - } - scol+= 4; - } - - tface= me->mtface; - mface= me->mface; - mcol= (char *)me->mcol; - for(a=me->totface; a>0; a--, mface++, mcol+=16) { - if((tface && tface->mode & TF_SHAREDCOL) || (me->editflag & ME_EDIT_PAINT_MASK)==0) { - scol= scolmain+4*mface->v1; - mcol[1]= scol[1]; mcol[2]= scol[2]; mcol[3]= scol[3]; - scol= scolmain+4*mface->v2; - mcol[5]= scol[1]; mcol[6]= scol[2]; mcol[7]= scol[3]; - scol= scolmain+4*mface->v3; - mcol[9]= scol[1]; mcol[10]= scol[2]; mcol[11]= scol[3]; - if(mface->v4) { - scol= scolmain+4*mface->v4; - mcol[13]= scol[1]; mcol[14]= scol[2]; mcol[15]= scol[3]; - } - } - if(tface) tface++; - } - - MEM_freeN(scolmain); -} - static void make_vertexcol(Object *ob) /* single ob */ { Mesh *me; @@ -272,24 +207,6 @@ static void make_vertexcol(Object *ob) /* single ob */ //else memset(me->mcol, 255, 4*sizeof(MCol)*me->totface); - - DAG_id_flush_update(&me->id, OB_RECALC_DATA); - -} - -static void copy_vpaint_prev(VPaint *vp, unsigned int *mcol, int tot) -{ - if(vp->vpaint_prev) { - MEM_freeN(vp->vpaint_prev); - vp->vpaint_prev= NULL; - } - vp->tot= tot; - - if(mcol==NULL || tot==0) return; - - vp->vpaint_prev= MEM_mallocN(4*sizeof(int)*tot, "vpaint_prev"); - memcpy(vp->vpaint_prev, mcol, 4*sizeof(int)*tot); - } static void copy_wpaint_prev (VPaint *wp, MDeformVert *dverts, int dcount) @@ -494,197 +411,6 @@ void vpaint_dogamma(Scene *scene) } */ -static unsigned int mcol_blend(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int mfac; - unsigned int col=0; - - if(fac==0) return col1; - if(fac>=255) return col2; - - mfac= 255-fac; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - cp[0]= 255; - cp[1]= (mfac*cp1[1]+fac*cp2[1])/255; - cp[2]= (mfac*cp1[2]+fac*cp2[2])/255; - cp[3]= (mfac*cp1[3]+fac*cp2[3])/255; - - return col; -} - -static unsigned int mcol_add(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int temp; - unsigned int col=0; - - if(fac==0) return col1; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - cp[0]= 255; - temp= cp1[1] + ((fac*cp2[1])/255); - if(temp>254) cp[1]= 255; else cp[1]= temp; - temp= cp1[2] + ((fac*cp2[2])/255); - if(temp>254) cp[2]= 255; else cp[2]= temp; - temp= cp1[3] + ((fac*cp2[3])/255); - if(temp>254) cp[3]= 255; else cp[3]= temp; - - return col; -} - -static unsigned int mcol_sub(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int temp; - unsigned int col=0; - - if(fac==0) return col1; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - cp[0]= 255; - temp= cp1[1] - ((fac*cp2[1])/255); - if(temp<0) cp[1]= 0; else cp[1]= temp; - temp= cp1[2] - ((fac*cp2[2])/255); - if(temp<0) cp[2]= 0; else cp[2]= temp; - temp= cp1[3] - ((fac*cp2[3])/255); - if(temp<0) cp[3]= 0; else cp[3]= temp; - - return col; -} - -static unsigned int mcol_mul(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int mfac; - unsigned int col=0; - - if(fac==0) return col1; - - mfac= 255-fac; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - /* first mul, then blend the fac */ - cp[0]= 255; - cp[1]= (mfac*cp1[1] + fac*((cp2[1]*cp1[1])/255) )/255; - cp[2]= (mfac*cp1[2] + fac*((cp2[2]*cp1[2])/255) )/255; - cp[3]= (mfac*cp1[3] + fac*((cp2[3]*cp1[3])/255) )/255; - - - return col; -} - -static unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int mfac; - unsigned int col=0; - - if(fac==0) return col1; - if(fac>=255) return col2; - - mfac= 255-fac; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - /* See if are lighter, if so mix, else dont do anything. - if the paint col is darker then the original, then ignore */ - if (cp1[1]+cp1[2]+cp1[3] > cp2[1]+cp2[2]+cp2[3]) - return col1; - - cp[0]= 255; - cp[1]= (mfac*cp1[1]+fac*cp2[1])/255; - cp[2]= (mfac*cp1[2]+fac*cp2[2])/255; - cp[3]= (mfac*cp1[3]+fac*cp2[3])/255; - - return col; -} - -static unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fac) -{ - char *cp1, *cp2, *cp; - int mfac; - unsigned int col=0; - - if(fac==0) return col1; - if(fac>=255) return col2; - - mfac= 255-fac; - - cp1= (char *)&col1; - cp2= (char *)&col2; - cp= (char *)&col; - - /* See if were darker, if so mix, else dont do anything. - if the paint col is brighter then the original, then ignore */ - if (cp1[1]+cp1[2]+cp1[3] < cp2[1]+cp2[2]+cp2[3]) - return col1; - - cp[0]= 255; - cp[1]= (mfac*cp1[1]+fac*cp2[1])/255; - cp[2]= (mfac*cp1[2]+fac*cp2[2])/255; - cp[3]= (mfac*cp1[3]+fac*cp2[3])/255; - return col; -} - -static void vpaint_blend(VPaint *vp, unsigned int *col, unsigned int *colorig, unsigned int paintcol, int alpha) -{ - Brush *brush = paint_brush(&vp->paint); - - if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) *col= mcol_blend( *col, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_ADD) *col= mcol_add( *col, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_SUB) *col= mcol_sub( *col, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_MUL) *col= mcol_mul( *col, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_LIGHTEN) *col= mcol_lighten( *col, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_DARKEN) *col= mcol_darken( *col, paintcol, alpha); - - /* if no spray, clip color adding with colorig & orig alpha */ - if((vp->flag & VP_SPRAY)==0) { - unsigned int testcol=0, a; - char *cp, *ct, *co; - - alpha= (int)(255.0*brush_alpha(brush)); - - if(brush->vertexpaint_tool==VP_MIX || brush->vertexpaint_tool==VP_BLUR) testcol= mcol_blend( *colorig, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_ADD) testcol= mcol_add( *colorig, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_SUB) testcol= mcol_sub( *colorig, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_MUL) testcol= mcol_mul( *colorig, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_LIGHTEN) testcol= mcol_lighten( *colorig, paintcol, alpha); - else if(brush->vertexpaint_tool==VP_DARKEN) testcol= mcol_darken( *colorig, paintcol, alpha); - - cp= (char *)col; - ct= (char *)&testcol; - co= (char *)colorig; - - for(a=0; a<4; a++) { - if( ct[a]<co[a] ) { - if( cp[a]<ct[a] ) cp[a]= ct[a]; - else if( cp[a]>co[a] ) cp[a]= co[a]; - } - else { - if( cp[a]<co[a] ) cp[a]= co[a]; - else if( cp[a]>ct[a] ) cp[a]= ct[a]; - } - } - } -} - - static int sample_backbuf_area(ViewContext *vc, int *indexar, int totface, int x, int y, float size) { struct ImBuf *ibuf; @@ -774,32 +500,32 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float if (flip) { switch(tool) { - case VP_MIX: + case IMB_BLEND_MIX: paintval = 1.f - paintval; break; - case VP_ADD: - tool= VP_SUB; break; - case VP_SUB: - tool= VP_ADD; break; - case VP_LIGHTEN: - tool= VP_DARKEN; break; - case VP_DARKEN: - tool= VP_LIGHTEN; break; + case IMB_BLEND_ADD: + tool= IMB_BLEND_SUB; break; + case IMB_BLEND_SUB: + tool= IMB_BLEND_ADD; break; + case IMB_BLEND_LIGHTEN: + tool= IMB_BLEND_DARKEN; break; + case IMB_BLEND_DARKEN: + tool= IMB_BLEND_LIGHTEN; break; } } - if(tool==VP_MIX || tool==VP_BLUR) + if(tool==IMB_BLEND_MIX || tool==VERTEX_PAINT_BLUR) dw->weight = paintval*alpha + dw->weight*(1.0-alpha); - else if(tool==VP_ADD) + else if(tool==IMB_BLEND_ADD) dw->weight += paintval*alpha; - else if(tool==VP_SUB) + else if(tool==IMB_BLEND_SUB) dw->weight -= paintval*alpha; - else if(tool==VP_MUL) + else if(tool==IMB_BLEND_MUL) /* first mul, then blend the fac */ dw->weight = ((1.0-alpha) + alpha*paintval)*dw->weight; - else if(tool==VP_LIGHTEN) { + else if(tool==IMB_BLEND_LIGHTEN) { if (dw->weight < paintval) dw->weight = paintval*alpha + dw->weight*(1.0-alpha); - } else if(tool==VP_DARKEN) { + } else if(tool==IMB_BLEND_DARKEN) { if (dw->weight > paintval) dw->weight = paintval*alpha + dw->weight*(1.0-alpha); } @@ -810,21 +536,21 @@ static void wpaint_blend(VPaint *wp, MDeformWeight *dw, MDeformWeight *uw, float float testw=0.0f; alpha= brush_alpha(brush); - if(tool==VP_MIX || tool==VP_BLUR) + if(tool==IMB_BLEND_MIX || tool==VERTEX_PAINT_BLUR) testw = paintval*alpha + uw->weight*(1.0-alpha); - else if(tool==VP_ADD) + else if(tool==IMB_BLEND_ADD) testw = uw->weight + paintval*alpha; - else if(tool==VP_SUB) + else if(tool==IMB_BLEND_SUB) testw = uw->weight - paintval*alpha; - else if(tool==VP_MUL) + else if(tool==IMB_BLEND_MUL) /* first mul, then blend the fac */ testw = ((1.0-alpha) + alpha*paintval)*uw->weight; - else if(tool==VP_LIGHTEN) { + else if(tool==IMB_BLEND_LIGHTEN) { if (uw->weight < paintval) testw = paintval*alpha + uw->weight*(1.0-alpha); else testw = uw->weight; - } else if(tool==VP_DARKEN) { + } else if(tool==IMB_BLEND_DARKEN) { if (uw->weight > paintval) testw = paintval*alpha + uw->weight*(1.0-alpha); else @@ -1413,7 +1139,7 @@ static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *UNUSED wpd->vgroup_mirror= actdef; } } - + return 1; } @@ -1496,7 +1222,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* make sure each vertex gets treated only once */ /* and calculate filter weight */ totw= 0; - if(brush->vertexpaint_tool==VP_BLUR) + if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR) paintweight= 0.0f; else paintweight= ts->vgroup_weight; @@ -1510,7 +1236,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P (me->dvert+mface->v3)->flag= 1; if(mface->v4) (me->dvert+mface->v4)->flag= 1; - if(brush->vertexpaint_tool==VP_BLUR) { + if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR) { MDeformWeight *dw, *(*dw_func)(MDeformVert *, int); if(wp->flag & VP_ONLYVGROUP) @@ -1532,7 +1258,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P } } - if(brush->vertexpaint_tool==VP_BLUR) + if(brush->vertexpaint_tool==VERTEX_PAINT_BLUR) paintweight/= (float)totw; for(index=0; index<totindex; index++) { @@ -1633,7 +1359,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, wmEvent *event) { op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, - wpaint_stroke_update_step, + wpaint_stroke_update_step, NULL, NULL, wpaint_stroke_done); /* add modal handler */ @@ -1689,6 +1415,10 @@ void PAINT_OT_weight_set(wmOperatorType *ot) /* ************ set / clear vertex paint mode ********** */ +struct VPaintSession { + unsigned int src_image_gltex; +}; + static int set_vpaint(bContext *C, wmOperator *op) /* toggle */ { @@ -1704,11 +1434,10 @@ static int set_vpaint(bContext *C, wmOperator *op) /* toggle */ return OPERATOR_PASS_THROUGH; } - if(me && me->mcol==NULL) make_vertexcol(ob); - /* toggle: end vpaint */ if(ob->mode & OB_MODE_VERTEX_PAINT) { - + free_paintsession(ob); + ob->mode &= ~OB_MODE_VERTEX_PAINT; } else { @@ -1716,18 +1445,25 @@ static int set_vpaint(bContext *C, wmOperator *op) /* toggle */ /* Turn off weight painting */ if (ob->mode & OB_MODE_WEIGHT_PAINT) set_wpaint(C, op); - - if(vp==NULL) - vp= scene->toolsettings->vpaint= new_vpaint(0); - + + if(me->mcol==NULL) + make_vertexcol(ob); + + if(!CustomData_get_layer(&me->fdata, CD_MPTEX)) + WM_operator_name_call(C, "PTEX_OT_layer_add", WM_OP_INVOKE_REGION_WIN, NULL); + + create_paintsession(ob); + paint_cursor_start(C, vertex_paint_poll); paint_init(&vp->paint, PAINT_CURSOR_VERTEX_PAINT); } - if (me) - /* update modifier stack for mapping requirements */ - DAG_id_flush_update(&me->id, OB_RECALC_DATA); + /* create pbvh */ + if(ob->mode & OB_MODE_VERTEX_PAINT) { + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH|CD_MASK_MCOL); + ob->paint->pbvh = dm->getPBVH(ob, dm); + } WM_event_add_notifier(C, NC_SCENE|ND_MODE, scene); @@ -1749,211 +1485,6 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } - - -/* ********************** vertex paint operator ******************* */ - -/* Implementation notes: - -Operator->invoke() - - validate context (add mcol) - - create customdata storage - - call paint once (mouse click) - - add modal handler - -Operator->modal() - - for every mousemove, apply vertex paint - - exit on mouse release, free customdata - (return OPERATOR_FINISHED also removes handler and operator) - -For future: - - implement a stroke event (or mousemove with past positons) - - revise whether op->customdata should be added in object, in set_vpaint - -*/ - -typedef struct VPaintData { - ViewContext vc; - unsigned int paintcol; - int *indexar; - float *vertexcosnos; - float vpimat[3][3]; -} VPaintData; - -static int vpaint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *UNUSED(event)) -{ - ToolSettings *ts= CTX_data_tool_settings(C); - struct PaintStroke *stroke = op->customdata; - VPaint *vp= ts->vpaint; - struct VPaintData *vpd; - Object *ob= CTX_data_active_object(C); - Mesh *me; - float mat[4][4], imat[4][4]; - - /* context checks could be a poll() */ - me= get_mesh(ob); - if(me==NULL || me->totface==0) return OPERATOR_PASS_THROUGH; - - if(me->mcol==NULL) make_vertexcol(ob); - if(me->mcol==NULL) return OPERATOR_CANCELLED; - - /* make mode data storage */ - vpd= MEM_callocN(sizeof(struct VPaintData), "VPaintData"); - paint_stroke_set_mode_data(stroke, vpd); - view3d_set_viewcontext(C, &vpd->vc); - - vpd->vertexcosnos= mesh_get_mapped_verts_nors(vpd->vc.scene, ob); - vpd->indexar= get_indexarray(me); - vpd->paintcol= vpaint_get_current_col(vp); - - /* for filtering */ - copy_vpaint_prev(vp, (unsigned int *)me->mcol, me->totface); - - /* some old cruft to sort out later */ - mul_m4_m4m4(mat, ob->obmat, vpd->vc.rv3d->viewmat); - invert_m4_m4(imat, mat); - copy_m3_m4(vpd->vpimat, imat); - - return 1; -} - -static void vpaint_paint_face(VPaint *vp, VPaintData *vpd, Object *ob, int index, float mval[2], float pressure, int UNUSED(flip)) -{ - ViewContext *vc = &vpd->vc; - Brush *brush = paint_brush(&vp->paint); - Mesh *me = get_mesh(ob); - MFace *mface= ((MFace*)me->mface) + index; - unsigned int *mcol= ((unsigned int*)me->mcol) + 4*index; - unsigned int *mcolorig= ((unsigned int*)vp->vpaint_prev) + 4*index; - float alpha; - int i; - - if((vp->flag & VP_COLINDEX && mface->mat_nr!=ob->actcol-1) || - ((me->editflag & ME_EDIT_PAINT_MASK) && !(mface->flag & ME_FACE_SEL))) - return; - - if(brush->vertexpaint_tool==VP_BLUR) { - unsigned int fcol1= mcol_blend( mcol[0], mcol[1], 128); - if(mface->v4) { - unsigned int fcol2= mcol_blend( mcol[2], mcol[3], 128); - vpd->paintcol= mcol_blend( fcol1, fcol2, 128); - } - else { - vpd->paintcol= mcol_blend( mcol[2], fcol1, 170); - } - - } - - for(i = 0; i < (mface->v4 ? 4 : 3); ++i) { - alpha= calc_vp_alpha_dl(vp, vc, vpd->vpimat, vpd->vertexcosnos+6*(&mface->v1)[i], mval, pressure); - if(alpha) - vpaint_blend(vp, mcol+i, mcolorig+i, vpd->paintcol, (int)(alpha*255.0)); - } -} - -static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) -{ - ToolSettings *ts= CTX_data_tool_settings(C); - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - VPaint *vp= ts->vpaint; - Brush *brush = paint_brush(&vp->paint); - ViewContext *vc= &vpd->vc; - Object *ob= vc->obact; - Mesh *me= ob->data; - float mat[4][4]; - int *indexar= vpd->indexar; - int totindex, index, flip; - float pressure, mval[2]; - - RNA_float_get_array(itemptr, "mouse", mval); - flip = RNA_boolean_get(itemptr, "pen_flip"); - pressure = RNA_float_get(itemptr, "pressure"); - - view3d_operator_needs_opengl(C); - - /* load projection matrix */ - mul_m4_m4m4(mat, ob->obmat, vc->rv3d->persmat); - - mval[0]-= vc->ar->winrct.xmin; - mval[1]-= vc->ar->winrct.ymin; - - - /* which faces are involved */ - if(vp->flag & VP_AREA) { - totindex= sample_backbuf_area(vc, indexar, me->totface, mval[0], mval[1], brush_size(brush)); - } - else { - indexar[0]= view3d_sample_backbuf(vc, mval[0], mval[1]); - if(indexar[0]) totindex= 1; - else totindex= 0; - } - - swap_m4m4(vc->rv3d->persmat, mat); - - for(index=0; index<totindex; index++) { - if(indexar[index] && indexar[index]<=me->totface) - vpaint_paint_face(vp, vpd, ob, indexar[index]-1, mval, pressure, flip); - } - - swap_m4m4(vc->rv3d->persmat, mat); - - /* was disabled because it is slow, but necessary for blur */ - if(brush->vertexpaint_tool == VP_BLUR) - do_shared_vertexcol(me); - - ED_region_tag_redraw(vc->ar); - - DAG_id_flush_update(ob->data, OB_RECALC_DATA); -} - -static void vpaint_stroke_done(bContext *C, struct PaintStroke *stroke) -{ - ToolSettings *ts= CTX_data_tool_settings(C); - struct VPaintData *vpd= paint_stroke_mode_data(stroke); - - if(vpd->vertexcosnos) - MEM_freeN(vpd->vertexcosnos); - MEM_freeN(vpd->indexar); - - /* frees prev buffer */ - copy_vpaint_prev(ts->vpaint, NULL, 0); - - MEM_freeN(vpd); -} - -static int vpaint_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - - op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, - vpaint_stroke_update_step, - vpaint_stroke_done); - - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - op->type->modal(C, op, event); - - return OPERATOR_RUNNING_MODAL; -} - -void PAINT_OT_vertex_paint(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Vertex Paint"; - ot->idname= "PAINT_OT_vertex_paint"; - - /* api callbacks */ - ot->invoke= vpaint_invoke; - ot->modal= paint_stroke_modal; - /* ot->exec= vpaint_exec; <-- needs stroke property */ - ot->poll= vertex_paint_poll; - - /* flags */ - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; - - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); -} - /* ********************** weight from bones operator ******************* */ static int weight_from_bones_poll(bContext *C) @@ -2001,4 +1532,3 @@ void PAINT_OT_weight_from_bones(wmOperatorType *ot) /* properties */ ot->prop= RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights."); } - diff --git a/source/blender/editors/sculpt_paint/pbvh_undo.c b/source/blender/editors/sculpt_paint/pbvh_undo.c new file mode 100644 index 00000000000..793f5e79676 --- /dev/null +++ b/source/blender/editors/sculpt_paint/pbvh_undo.c @@ -0,0 +1,570 @@ +/* + * $Id$ + * + * ***** 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. + * + * The Original Code is Copyright (C) 2006 by Nicholas Bishop + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "BLI_math.h" +#include "BLI_ghash.h" +#include "BLI_threads.h" + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_mesh_types.h" +#include "DNA_key_types.h" + +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_dmgrid.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_paint.h" +#include "BKE_mesh.h" +#include "BKE_key.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_sculpt.h" +#include "paint_intern.h" +#include "sculpt_intern.h" +#include "ptex.h" + +/************************** Undo *************************/ + +struct PBVHUndoNode { + struct PBVHUndoNode *next, *prev; + + /* object id name */ + char idname[MAX_ID_NAME]; + /* only during push, not valid afterwards! */ + struct PBVHNode *node; + + /* total unique verts in node */ + int totvert; + + /* actual undo data */ + float (*co)[3]; + /* paint mask */ + float *pmask; + char pmask_name[32]; + /* ptex */ + void **mptex_data; + char mptex_name[32]; + + /* non-multires */ + /* to verify if me->totvert it still the same */ + int maxvert; + /* to restore into the right location */ + int *vert_indices; + int *face_indices; + /* multires */ + /* to verify total number of grids is still the same */ + int maxgrid; + int gridsize; + int totgrid; + /* to restore into the right location */ + int *grid_indices; + + /* shape keys */ + char *shapeName[32]; /* keep size in sync with keyblock dna */ + + /* only during push, not stored */ + short (*no)[3]; + /* layer brush */ + float *layer_disp; +}; + +static void pbvh_undo_restore_mesh_co(PBVHUndoNode *unode, bContext *C, Scene *scene, Object *ob) +{ + SculptSession *ss = ob->paint->sculpt; + Mesh *me = ob->data; + MVert *mvert; + char *shapeName= (char*)unode->shapeName; + int *index, i; + + if(ss && ss->kb && strcmp(ss->kb->name, shapeName)) { + /* shape key has been changed before calling undo operator */ + + Key *key= ob_get_key(ob); + KeyBlock *kb= key_get_named_keyblock(key, shapeName); + + if (kb) { + ob->shapenr= BLI_findindex(&key->block, kb) + 1; + ob->shapeflag|= OB_SHAPE_LOCK; + + sculpt_update_mesh_elements(scene, ob, 0); + WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob); + } else { + /* key has been removed -- skip this undo node */ + return; + } + } + + mvert= me->mvert; + index= unode->vert_indices; + + if(ss && ss->kb) { + float (*vertCos)[3]; + vertCos= key_to_vertcos(ob, ss->kb); + + for(i=0; i<unode->totvert; i++) + swap_v3_v3(vertCos[index[i]], unode->co[i]); + + /* propagate new coords to keyblock */ + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + + /* pbvh uses it's own mvert array, so coords should be */ + /* propagated to pbvh here */ + BLI_pbvh_apply_vertCos(ob->paint->pbvh, vertCos); + + MEM_freeN(vertCos); + } + else { + for(i=0; i<unode->totvert; i++) { + swap_v3_v3(mvert[index[i]].co, unode->co[i]); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + + } +} + +static void pbvh_undo_restore(bContext *C, ListBase *lb) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + PBVH *pbvh; + PBVHUndoNode *unode; + MultiresModifierData *mmd; + GridToFace *grid_face_map; + int i, j, update_co= 0, update_ptex= 0, update_mask= 0; + + // XXX: sculpt_update_mesh_elements(scene, ob, 0); + + pbvh = dm->getPBVH(ob, dm); + grid_face_map = dm->getGridFaceMap(dm); + + for(unode=lb->first; unode; unode=unode->next) { + CustomData *vdata, *fdata; + + if(!(strcmp(unode->idname, ob->id.name)==0)) + continue; + + BLI_pbvh_get_customdata(pbvh, &vdata, &fdata); + + if(unode->maxvert) { + /* regular mesh restore */ + if(dm->getNumVerts(dm) != unode->maxvert) + continue; + + update_co |= !!unode->co; + update_ptex |= !!unode->mptex_data; + update_mask |= !!unode->pmask; + + if(unode->co) { + pbvh_undo_restore_mesh_co(unode, C, scene, ob); + } + if(unode->pmask) { + float *pmask; + + pmask = CustomData_get_layer_named(vdata, + CD_PAINTMASK, + unode->pmask_name); + + for(i=0; i<unode->totvert; i++) + SWAP(float, pmask[unode->vert_indices[i]], + unode->pmask[i]); + } + } + else if(unode->maxgrid && dm->getGridData) { + /* multires restore */ + DMGridData **grids, *grid; + GridKey *gridkey; + float (*co)[3] = NULL, *pmask = NULL; + int gridsize, active_pmask; + + if(dm->getNumGrids(dm) != unode->maxgrid) + continue; + + /* do ptex restore before checking gridsize */ + for(j=0; j<unode->totgrid; j++) { + if(unode->mptex_data) { + GridToFace *gtf = &grid_face_map[unode->grid_indices[j]]; + MPtex *mptex; + + mptex = CustomData_get_layer_named(fdata, + CD_MPTEX, + unode->mptex_name); + SWAP(void*, unode->mptex_data[j], + mptex[gtf->face].subfaces[gtf->offset].data); + } + } + + update_ptex |= !!unode->mptex_data; + + if(dm->getGridSize(dm) != unode->gridsize) + continue; + + update_co |= !!unode->co; + update_mask |= !!unode->pmask; + + grids= dm->getGridData(dm); + gridsize= dm->getGridSize(dm); + gridkey= dm->getGridKey(dm); + + if(unode->co) { + co = unode->co; + } + if(unode->pmask) { + pmask = unode->pmask; + active_pmask = gridelem_active_offset(vdata, gridkey, CD_PAINTMASK); + } + + for(j=0; j<unode->totgrid; j++) { + grid= grids[unode->grid_indices[j]]; + + for(i=0; i<gridsize*gridsize; i++) { + DMGridData *elem = GRIDELEM_AT(grid, i, gridkey); + + if(co) { + swap_v3_v3(GRIDELEM_CO(elem, gridkey), co[0]); + ++co; + } + if(pmask) { + SWAP(float, GRIDELEM_MASK(elem, gridkey)[active_pmask], + *pmask); + ++pmask; + } + } + } + } + } + + if(update_co || update_ptex || update_mask) { + SculptSession *ss = ob->paint->sculpt; + int update_flags = PBVH_UpdateRedraw; + + /* we update all nodes still, should be more clever, but also + needs to work correct when exiting/entering sculpt mode and + the nodes get recreated, though in that case it could do all */ + BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL, BLI_pbvh_node_set_flags, + SET_INT_IN_POINTER(PBVH_UpdateAll)); + + if(update_co) + update_flags |= PBVH_UpdateBB|PBVH_UpdateOriginalBB; + if(update_ptex || update_mask) + update_flags |= PBVH_UpdateColorBuffers|PBVH_UpdateRedraw; + + BLI_pbvh_update(ob->paint->pbvh, update_flags, NULL); + + if((mmd=ED_paint_multires_active(scene, ob))) + multires_mark_as_modified(ob); + + /* TODO: should work with other paint modes too */ + if(ss && (ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1)) + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + } +} + +static void pbvh_undo_free(ListBase *lb) +{ + PBVHUndoNode *unode; + + for(unode=lb->first; unode; unode=unode->next) { + if(unode->co) + MEM_freeN(unode->co); + if(unode->no) + MEM_freeN(unode->no); + if(unode->pmask) + MEM_freeN(unode->pmask); + if(unode->mptex_data) { + int i; + for(i = 0; i < unode->totgrid; ++i) + MEM_freeN(unode->mptex_data[i]); + MEM_freeN(unode->mptex_data); + } + if(unode->vert_indices) + MEM_freeN(unode->vert_indices); + if(unode->face_indices) + MEM_freeN(unode->face_indices); + if(unode->grid_indices) + MEM_freeN(unode->grid_indices); + if(unode->layer_disp) + MEM_freeN(unode->layer_disp); + } +} + +PBVHUndoNode *pbvh_undo_get_node(PBVHNode *node) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + PBVHUndoNode *unode; + + if(!lb) + return NULL; + + for(unode=lb->first; unode; unode=unode->next) + if(unode->node == node) + return unode; + + return NULL; +} + +PBVHUndoNode *pbvh_undo_push_node(PBVHNode *node, PBVHUndoFlag flag, + Object *ob, Scene *scene) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + PBVHUndoNode *unode; + PBVH *pbvh = ob->paint->pbvh; + PBVHVertexIter vd; + int totbytes= 0; + + CustomData *vdata, *fdata; + SculptSession *ss = NULL; /* for shapekey */ + + GridKey *gridkey; + int uses_grids, totgrid, *grid_indices; + GridToFace *grid_face_map; + + /* list is manipulated by multiple threads, so we lock */ + BLI_lock_thread(LOCK_CUSTOM1); + + if((unode= pbvh_undo_get_node(node))) { + BLI_unlock_thread(LOCK_CUSTOM1); + return unode; + } + + unode= MEM_callocN(sizeof(PBVHUndoNode), "PBVHUndoNode"); + strcpy(unode->idname, ob->id.name); + unode->node= node; + + /* XXX: changed this to just use unique verts rather than all, + seems like only unique are restored from anyway? -nicholas */ + BLI_pbvh_node_num_verts(pbvh, node, &unode->totvert, NULL); + + BLI_pbvh_get_customdata(pbvh, &vdata, &fdata); + uses_grids = BLI_pbvh_uses_grids(pbvh); + + if(uses_grids) { + /* multires */ + BLI_pbvh_node_get_grids(pbvh, node, &grid_indices, + &totgrid, &unode->maxgrid, &unode->gridsize, + NULL, NULL, &gridkey); + + unode->totgrid= totgrid; + unode->grid_indices= MEM_mapallocN(sizeof(int)*totgrid, "PBVHUndoNode.grid_indices"); + totbytes += sizeof(int) * totgrid; + } + else { + /* regular mesh */ + + if(scene) { + DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + unode->maxvert= dm->getNumVerts(dm); + } + else + unode->maxvert= get_mesh(ob)->totvert; + + if(flag & (PBVH_UNDO_CO_NO|PBVH_UNDO_PMASK)) { + unode->vert_indices= MEM_mapallocN(sizeof(int)*unode->totvert, + "PBVHUndoNode.vert_indices"); + totbytes += sizeof(int) * unode->totvert; + } + } + + /* allocate only the necessary undo data + XXX: we will use this while sculpting, is mapalloc slow to access then? */ + + if(flag & PBVH_UNDO_CO_NO) { + unode->co= MEM_mapallocN(sizeof(float)*3*unode->totvert, "PBVHUndoNode.co"); + unode->no= MEM_mapallocN(sizeof(short)*3*unode->totvert, "PBVHUndoNode.no"); + totbytes += (sizeof(float)*3 + sizeof(short)*3) * unode->totvert; + } + if(flag & PBVH_UNDO_PMASK) { + int active; + active= CustomData_get_active_layer_index(vdata, CD_PAINTMASK); + + if(active == -1) + flag &= ~PBVH_UNDO_PMASK; + else { + BLI_strncpy(unode->pmask_name, + vdata->layers[active].name, + sizeof(unode->pmask_name)); + unode->pmask= MEM_mapallocN(sizeof(float)*unode->totvert, "PBVHUndoNode.pmask"); + totbytes += sizeof(float) * unode->totvert; + } + } + if(flag & PBVH_UNDO_PTEX) { + int i, active; + + active= CustomData_get_active_layer_index(fdata, CD_MPTEX); + + if(active == -1) + flag &= ~PBVH_UNDO_PTEX; + + assert(uses_grids); + + BLI_strncpy(unode->mptex_name, + fdata->layers[active].name, + sizeof(unode->mptex_name)); + + grid_face_map= BLI_pbvh_get_grid_face_map(pbvh); + + for(i = 0; i < unode->totgrid; ++i) { + GridToFace *gtf= &grid_face_map[grid_indices[i]]; + MPtex *pt= ((MPtex*)fdata->layers[active].data) + gtf->face; + MPtexSubface *subface= &pt->subfaces[gtf->offset]; + + totbytes+= (pt->channels * ptex_data_size(pt->type) * + subface->res[0] * subface->res[1]); + } + + unode->mptex_data= MEM_mapallocN(sizeof(void*)*unode->totgrid, "PBVHUndoNode.mptex_data"); + totbytes+= sizeof(void*)*unode->totgrid; + } + + /* push undo node onto paint undo list */ + undo_paint_push_count_alloc(UNDO_PAINT_MESH, totbytes); + BLI_addtail(lb, unode); + + BLI_unlock_thread(LOCK_CUSTOM1); + + /* the rest is threaded, hopefully this is the performance critical part */ + + if(uses_grids || (flag & (PBVH_UNDO_CO_NO|PBVH_UNDO_PMASK))) { + BLI_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_UNIQUE) { + if(flag & PBVH_UNDO_CO_NO) { + copy_v3_v3(unode->co[vd.i], vd.co); + if(vd.no) VECCOPY(unode->no[vd.i], vd.no) + else normal_float_to_short_v3(unode->no[vd.i], vd.fno); + } + if(flag & PBVH_UNDO_PMASK) { + if(vd.mask_active) + unode->pmask[vd.i]= *vd.mask_active; + } + + if(vd.vert_indices) + unode->vert_indices[vd.i]= vd.vert_indices[vd.i]; + } + BLI_pbvh_vertex_iter_end; + } + + if(unode->grid_indices) + memcpy(unode->grid_indices, grid_indices, sizeof(int)*totgrid); + + /* copy ptex data (doesn't handle res changes yet) */ + if(flag & PBVH_UNDO_PTEX) { + MPtex *mptex; + int i; + + mptex= CustomData_get_layer(fdata, CD_MPTEX); + + for(i = 0; i < unode->totgrid; ++i) { + GridToFace *gtf= &grid_face_map[grid_indices[i]]; + MPtex *pt= &mptex[gtf->face]; + MPtexSubface *subface= &pt->subfaces[gtf->offset]; + + unode->mptex_data[i]= MEM_dupallocN(subface->data); + } + } + + /* store active shape key */ + ss= ob->paint->sculpt; + if(ss && ss->kb) + BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); + else + unode->shapeName[0]= '\0'; + + return unode; +} + +void pbvh_undo_push_begin(char *name) +{ + undo_paint_push_begin(UNDO_PAINT_MESH, name, + pbvh_undo_restore, pbvh_undo_free); +} + +void pbvh_undo_push_end(void) +{ + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); + PBVHUndoNode *unode; + + if(!lb || !lb->first) + return; + + /* remove data that's only used during stroke */ + for(unode=lb->first; unode; unode=unode->next) { + if(unode->no) { + MEM_freeN(unode->no); + unode->no= NULL; + } + + if(unode->layer_disp) { + MEM_freeN(unode->layer_disp); + unode->layer_disp= NULL; + } + } + + undo_paint_push_end(UNDO_PAINT_MESH); +} + +int pbvh_undo_node_totvert(PBVHUndoNode *unode) +{ + return unode->totvert; +} + +pbvh_undo_f3 pbvh_undo_node_co(PBVHUndoNode *unode) +{ + return unode->co; +} + +pbvh_undo_s3 pbvh_undo_node_no(PBVHUndoNode *unode) +{ + return unode->no; +} + +float *pbvh_undo_node_layer_disp(PBVHUndoNode *unode) +{ + return unode->layer_disp; +} + +void pbvh_undo_node_set_layer_disp(PBVHUndoNode *unode, float *layer_disp) +{ + unode->layer_disp = layer_disp; +} + +const char *pbvh_undo_node_mptex_name(PBVHUndoNode *unode) +{ + return unode->mptex_name; +} + +void *pbvh_undo_node_mptex_data(PBVHUndoNode *unode, int ndx) +{ + return unode->mptex_data[ndx]; +} diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 505e37d95f6..7ddb42fa51a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -50,6 +50,9 @@ #include "BKE_cdderivedmesh.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_dmgrid.h" +#include "BKE_global.h" +#include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" #include "BKE_mesh.h" @@ -64,6 +67,7 @@ #include "WM_api.h" #include "WM_types.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -71,10 +75,6 @@ #include "RNA_access.h" #include "RNA_define.h" - -#include "RE_render_ext.h" -#include "RE_shader_ext.h" - #include "GPU_buffers.h" #include <math.h> @@ -89,49 +89,13 @@ * */ -void ED_sculpt_force_update(bContext *C) -{ - Object *ob= CTX_data_active_object(C); - - if(ob && (ob->mode & OB_MODE_SCULPT)) - multires_force_update(ob); -} - -/* Sculpt mode handles multires differently from regular meshes, but only if - it's the last modifier on the stack and it is not on the first level */ -struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob) -{ - Mesh *me= (Mesh*)ob->data; - ModifierData *md, *nmd; - - if(!CustomData_get_layer(&me->fdata, CD_MDISPS)) { - /* multires can't work without displacement layer */ - return NULL; - } - - for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { - if(md->type == eModifierType_Multires) { - MultiresModifierData *mmd= (MultiresModifierData*)md; - - /* Check if any of the modifiers after multires are active - * if not it can use the multires struct */ - for(nmd= md->next; nmd; nmd= nmd->next) - if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime)) - break; - - if(!nmd && mmd->sculptlvl > 0) - return mmd; - } - } - - return NULL; -} +static int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]); /* Checks whether full update mode (slower) needs to be used to work with modifiers */ int sculpt_modifiers_active(Scene *scene, Object *ob) { ModifierData *md; - MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + MultiresModifierData *mmd= ED_paint_multires_active(scene, ob); /* check if there are any modifiers after what we are sculpting, for a multires modifier with a deform modifier in front, we @@ -142,11 +106,22 @@ int sculpt_modifiers_active(Scene *scene, Object *ob) else md= modifiers_getVirtualModifierList(ob); - /* exception for shape keys because we can edit those */ for(; md; md= md->next) { - if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) - if(md->type != eModifierType_ShapeKey) + if(modifier_isEnabled(scene, md, eModifierMode_Realtime)) { + + /* exception for shape keys because we can edit those */ + if(md->type == eModifierType_ShapeKey) + continue; + + /*exception for multires on level zero, it's + not caught by the earlier multires check */ + else if(md->type == eModifierType_Multires && + ((MultiresModifierData*)md)->sculptlvl == 0) + continue; + + else return 1; + } } return 0; @@ -169,44 +144,26 @@ typedef enum StrokeFlags { */ typedef struct StrokeCache { /* Invariants */ - float initial_radius; float scale[3]; int flag; float clip_tolerance[3]; - float initial_mouse[2]; /* Variants */ - float radius; - float radius_squared; - //float traced_location[3]; - float true_location[3]; - float location[3]; - float pen_flip; float invert; - float pressure; - float mouse[2]; float bstrength; - float tex_mouse[2]; /* The rest is temporary storage that isn't saved as a property */ - int first_time; /* Beginning of stroke may do some things special */ - - bglMats *mats; - /* Clean this up! */ + PaintStroke *stroke; ViewContext *vc; Brush *brush; float (*face_norms)[3]; /* Copy of the mesh faces' normals */ - float special_rotation; /* Texture rotation (radians) for anchored and rake modes */ - int pixel_radius, previous_pixel_radius; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; - int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only; - 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ float true_view_normal[3]; float view_normal[3]; @@ -214,8 +171,6 @@ typedef struct StrokeCache { float last_center[3]; int radial_symmetry_pass; float symm_rot_mat[4][4]; - float symm_rot_mat_inv[4][4]; - float last_rake[2]; /* Last location of updating rake rotation */ int original; float vertex_rotation; @@ -226,177 +181,6 @@ typedef struct StrokeCache { float plane_trim_squared; } StrokeCache; -/* ===== OPENGL ===== - * - * Simple functions to get data from the GL - */ - -/* Convert a point in model coordinates to 2D screen coordinates. */ -static void projectf(bglMats *mats, const float v[3], float p[2]) -{ - double ux, uy, uz; - - gluProject(v[0],v[1],v[2], mats->modelview, mats->projection, - (GLint *)mats->viewport, &ux, &uy, &uz); - p[0]= ux; - p[1]= uy; -} - -/*XXX: static void project(bglMats *mats, const float v[3], short p[2]) -{ - float f[2]; - projectf(mats, v, f); - - p[0]= f[0]; - p[1]= f[1]; -} -*/ - -/*** BVH Tree ***/ - -/* Get a screen-space rectangle of the modified area */ -int sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, - Object *ob, rcti *rect) -{ - PBVH *pbvh= ob->sculpt->pbvh; - float bb_min[3], bb_max[3], pmat[4][4]; - int i, j, k; - - view3d_get_object_project_mat(rv3d, ob, pmat); - - if(!pbvh) - return 0; - - BLI_pbvh_redraw_BB(pbvh, bb_min, bb_max); - - rect->xmin = rect->ymin = INT_MAX; - rect->xmax = rect->ymax = INT_MIN; - - if(bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2]) - return 0; - - for(i = 0; i < 2; ++i) { - for(j = 0; j < 2; ++j) { - for(k = 0; k < 2; ++k) { - float vec[3], proj[2]; - vec[0] = i ? bb_min[0] : bb_max[0]; - vec[1] = j ? bb_min[1] : bb_max[1]; - vec[2] = k ? bb_min[2] : bb_max[2]; - view3d_project_float(ar, vec, proj, pmat); - rect->xmin = MIN2(rect->xmin, proj[0]); - rect->xmax = MAX2(rect->xmax, proj[0]); - rect->ymin = MIN2(rect->ymin, proj[1]); - rect->ymax = MAX2(rect->ymax, proj[1]); - } - } - } - - return rect->xmin < rect->xmax && rect->ymin < rect->ymax; -} - -void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, - RegionView3D *rv3d, Object *ob) -{ - PBVH *pbvh= ob->sculpt->pbvh; - BoundBox bb; - bglMats mats; - rcti rect; - - memset(&bb, 0, sizeof(BoundBox)); - - view3d_get_transformation(ar, rv3d, ob, &mats); - sculpt_get_redraw_rect(ar, rv3d,ob, &rect); - -#if 1 - /* use some extra space just in case */ - rect.xmin -= 2; - rect.xmax += 2; - rect.ymin -= 2; - rect.ymax += 2; -#else - /* it was doing this before, allows to redraw a smaller - part of the screen but also gives artifaces .. */ - rect.xmin += 2; - rect.xmax -= 2; - rect.ymin += 2; - rect.ymax -= 2; -#endif - - view3d_calculate_clipping(&bb, planes, &mats, &rect); - mul_m4_fl(planes, -1.0f); - - /* clear redraw flag from nodes */ - if(pbvh) - BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); -} - -/************************ Brush Testing *******************/ - -typedef struct SculptBrushTest { - float radius_squared; - float location[3]; - float dist; -} SculptBrushTest; - -static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) -{ - test->radius_squared= ss->cache->radius_squared; - copy_v3_v3(test->location, ss->cache->location); -} - -static int sculpt_brush_test(SculptBrushTest *test, float co[3]) -{ - float distsq = len_squared_v3v3(co, test->location); - - if(distsq <= test->radius_squared) { - test->dist = sqrt(distsq); - return 1; - } - else { - return 0; - } -} - -static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3]) -{ - float distsq = len_squared_v3v3(co, test->location); - - if(distsq <= test->radius_squared) { - test->dist = distsq; - return 1; - } - else { - return 0; - } -} - -static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) -{ - return len_squared_v3v3(co, test->location) <= test->radius_squared; -} - -static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) -{ - static const float side = 0.70710678118654752440084436210485; // sqrt(.5); - - float local_co[3]; - - mul_v3_m4v3(local_co, local, co); - - local_co[0] = fabs(local_co[0]); - local_co[1] = fabs(local_co[1]); - local_co[2] = fabs(local_co[2]); - - if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { - test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side; - - return 1; - } - else { - return 0; - } -} - static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3]) { if (brush->flag & BRUSH_FRONTFACE) { @@ -419,30 +203,6 @@ static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float } } -#if 0 - -static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) -{ - if (sculpt_brush_test_fast(test, co)) { - float t1[3], t2[3], t3[3], dist; - - sub_v3_v3v3(t1, location, co); - sub_v3_v3v3(t2, x2, location); - - cross_v3_v3v3(t3, an, t1); - - dist = len_v3(t3)/len_v3(t2); - - test->dist = dist; - - return 1; - } - - return 0; -} - -#endif - /* ===== Sculpting ===== * */ @@ -490,86 +250,6 @@ static float integrate_overlap(Brush* br) return max; } -/* Uses symm to selectively flip any axis of a coordinate. */ -static void flip_coord(float out[3], float in[3], const char symm) -{ - if(symm & SCULPT_SYMM_X) - out[0]= -in[0]; - else - out[0]= in[0]; - if(symm & SCULPT_SYMM_Y) - out[1]= -in[1]; - else - out[1]= in[1]; - if(symm & SCULPT_SYMM_Z) - out[2]= -in[2]; - else - out[2]= in[2]; -} - -float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) -{ - float mirror[3]; - float distsq; - float mat[4][4]; - - //flip_coord(mirror, cache->traced_location, symm); - flip_coord(mirror, cache->true_location, symm); - - unit_m4(mat); - rotate_m4(mat, axis, angle); - - mul_m4_v3(mat, mirror); - - //distsq = len_squared_v3v3(mirror, cache->traced_location); - distsq = len_squared_v3v3(mirror, cache->true_location); - - if (distsq <= 4*(cache->radius_squared)) - return (2*(cache->radius) - sqrt(distsq)) / (2*(cache->radius)); - else - return 0; -} - -static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis) -{ - int i; - float overlap; - - overlap = 0; - for(i = 1; i < sd->radial_symm[axis-'X']; ++i) { - const float angle = 2*M_PI*i/sd->radial_symm[axis-'X']; - overlap += calc_overlap(cache, symm, axis, angle); - } - - return overlap; -} - -static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache) -{ - if (sd->flags & SCULPT_SYMMETRY_FEATHER) { - float overlap; - int symm = cache->symmetry; - int i; - - overlap = 0; - for (i = 0; i <= symm; i++) { - if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - - overlap += calc_overlap(cache, i, 0, 0); - - overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); - } - } - - return 1/overlap; - } - else { - return 1; - } -} - /* 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. */ @@ -581,7 +261,7 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) const float root_alpha = brush_alpha(brush); float alpha = root_alpha*root_alpha; float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; - float pressure = brush_use_alpha_pressure(brush) ? cache->pressure : 1; + float pressure = brush_use_alpha_pressure(brush) ? paint_stroke_pressure(cache->stroke) : 1; float pen_flip = cache->pen_flip ? -1 : 1; float invert = cache->invert ? -1 : 1; float accum = integrate_overlap(brush); @@ -649,25 +329,6 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) } } -float get_tex_pixel(Brush* br, float u, float v) -{ - TexResult texres; - float co[3]; - int hasrgb; - - co[0] = u; - co[1] = v; - co[2] = 0; - - memset(&texres, 0, sizeof(TexResult)); - hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres); - - if (hasrgb & TEX_RGB) - texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta; - - return texres.tin; -} - #if 0 /* Get a pixel from the texcache at (px, py) */ @@ -709,131 +370,10 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float #endif -/* Return a multiplier for brush strength on a particular vertex. */ -static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len) +static float tex_strength(SculptSession *ss, Brush *UNUSED(br), float *co, + float mask, float dist) { - MTex *mtex = &br->mtex; - float avg= 1; - - if(!mtex->tex) { - avg= 1; - } - else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) { - float jnk; - - /* Get strength by feeding the vertex - location directly into a texture */ - externtex(mtex, point, &avg, - &jnk, &jnk, &jnk, &jnk, 0); - } - else if(ss->texcache) { - float rotation = -mtex->rot; - float x, y, point_2d[3]; - float radius; - - /* if the active area is being applied for symmetry, flip it - across the symmetry axis and rotate it back to the orignal - position in order to project it. This insures that the - brush texture will be oriented correctly. */ - - flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass); - - if (ss->cache->radial_symmetry_pass) - mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d); - - projectf(ss->cache->mats, point_2d, point_2d); - - /* if fixed mode, keep coordinates relative to mouse */ - if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) { - rotation += ss->cache->special_rotation; - - point_2d[0] -= ss->cache->tex_mouse[0]; - point_2d[1] -= ss->cache->tex_mouse[1]; - - radius = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode - - x = point_2d[0]; - y = point_2d[1]; - } - else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED), - leave the coordinates relative to the screen */ - { - radius = brush_size(br); // use unadjusted size for tiled mode - - x = point_2d[0] - ss->cache->vc->ar->winrct.xmin; - y = point_2d[1] - ss->cache->vc->ar->winrct.ymin; - } - - x /= ss->cache->vc->ar->winx; - y /= ss->cache->vc->ar->winy; - - if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { - x -= 0.5f; - y -= 0.5f; - } - - x *= ss->cache->vc->ar->winx / radius; - y *= ss->cache->vc->ar->winy / radius; - - /* it is probably worth optimizing for those cases where - the texture is not rotated by skipping the calls to - atan2, sqrtf, sin, and cos. */ - if (rotation > 0.001 || rotation < -0.001) { - const float angle = atan2(y, x) + rotation; - const float flen = sqrtf(x*x + y*y); - - x = flen * cos(angle); - y = flen * sin(angle); - } - - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; - - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; - - avg = get_tex_pixel(br, x, y); - } - - avg += br->texture_sample_bias; - - avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ - - return avg; -} - -typedef struct { - Sculpt *sd; - SculptSession *ss; - float radius_squared; - int original; -} SculptSearchSphereData; - -/* Test AABB against sphere */ -static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v) -{ - SculptSearchSphereData *data = data_v; - float *center = data->ss->cache->location, nearest[3]; - float t[3], bb_min[3], bb_max[3]; - int i; - - if(data->original) - BLI_pbvh_node_get_original_BB(node, bb_min, bb_max); - else - BLI_pbvh_node_get_BB(node, bb_min, bb_max); - - for(i = 0; i < 3; ++i) { - if(bb_min[i] > center[i]) - nearest[i] = bb_min[i]; - else if(bb_max[i] < center[i]) - nearest[i] = bb_max[i]; - else - nearest[i] = center[i]; - } - - sub_v3_v3v3(t, center, nearest); - - return dot_v3v3(t, t) < data->radius_squared; + return paint_stroke_combined_strength(ss->cache->stroke, dist, co, mask); } /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ @@ -842,7 +382,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va int i; for(i=0; i<3; ++i) { - if(sd->flags & (SCULPT_LOCK_X << i)) + if(sd->paint.flags & (PAINT_LOCK_X << i)) continue; if((ss->cache->flag & (CLIP_X << i)) && (fabs(co[i]) <= ss->cache->clip_tolerance[i])) @@ -861,8 +401,9 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa } } -static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) +static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; int n; float out_flip[3] = {0.0f, 0.0f, 0.0f}; @@ -871,31 +412,31 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNod zero_v3(an); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; + PaintStrokeTest test; + PBVHUndoNode *unode; float private_an[3] = {0.0f, 0.0f, 0.0f}; float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; - unode = sculpt_undo_push_node(ss, nodes[n]); - sculpt_brush_test_init(ss, &test); + unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + paint_stroke_test_init(&test, ss->cache->stroke); if(ss->cache->original) { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) { float fno[3]; - normal_short_to_float_v3(fno, unode->no[vd.i]); + normal_short_to_float_v3(fno, pbvh_undo_node_no(unode)[vd.i]); add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); } } BLI_pbvh_vertex_iter_end; } else { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, vd.co)) { if(vd.no) { float fno[3]; @@ -925,13 +466,14 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNod /* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it finds average normal for all active vertices - note that this is called once for each mirroring direction */ -static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode) +static void calc_sculpt_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + (paint_stroke_first_dab(ss->cache->stroke) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: @@ -957,7 +499,7 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN break; case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ss, an, nodes, totnode); + calc_area_normal(sd, ob, an, nodes, totnode); default: break; @@ -967,7 +509,7 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN } else { copy_v3_v3(an, ss->cache->last_area_normal); - flip_coord(an, an, ss->cache->mirror_symmetry_pass); + paint_flip_coord(an, an, ss->cache->mirror_symmetry_pass); mul_m4_v3(ss->cache->symm_rot_mat, an); } } @@ -975,8 +517,9 @@ static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHN /* For the smooth brush, uses the neighboring vertices around vert to calculate a smoothed location for vert. Skips corner vertices (used by only one polygon.) */ -static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert) +static void neighbor_average(Object *ob, float avg[3], const unsigned vert) { + SculptSession *ss = ob->paint->sculpt; int i, skip= -1, total=0; IndexNode *node= ss->fmap[vert].first; char ncount= BLI_countlist(&ss->fmap[vert]); @@ -1016,22 +559,23 @@ static void neighbor_average(SculptSession *ss, float avg[3], const unsigned ver copy_v3_v3(avg, ss->mvert[vert].co); } -static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) +static void do_mesh_smooth_brush(Sculpt *sd, Object *ob, PBVHNode *node, float bstrength) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; CLAMP(bstrength, 0.0f, 1.0f); - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); float avg[3], val[3]; - neighbor_average(ss, avg, vd.vert_indices[vd.i]); + neighbor_average(ob, avg, vd.vert_indices[vd.i]); sub_v3_v3v3(val, avg, vd.co); mul_v3_fl(val, fade); @@ -1046,22 +590,24 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, BLI_pbvh_vertex_iter_end; } -static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength) +static void do_multires_smooth_brush(Sculpt *sd, Object *ob, PBVHNode *node, float bstrength) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); - SculptBrushTest test; + PaintStrokeTest test; DMGridData **griddata, *data; DMGridAdjacency *gridadj, *adj; + GridKey *gridkey; float (*tmpgrid)[3], (*tmprow)[3]; int v1, v2, v3, v4; int *grid_indices, totgrid, gridsize, i, x, y; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); CLAMP(bstrength, 0.0f, 1.0f); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, - NULL, &gridsize, &griddata, &gridadj); + BLI_pbvh_node_get_grids(ob->paint->pbvh, node, &grid_indices, &totgrid, + NULL, &gridsize, &griddata, &gridadj, &gridkey); #pragma omp critical { @@ -1079,7 +625,8 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no float tmp[3]; v1 = y*gridsize; - add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co); + add_v3_v3v3(tmprow[0], GRIDELEM_CO_AT(data, v1, gridkey), + GRIDELEM_CO_AT(data, v1+gridsize, gridkey)); for (x= 0; x < gridsize-1; x++) { v1 = x + y*gridsize; @@ -1087,7 +634,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no v3 = v1 + gridsize; v4 = v3 + 1; - add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co); + add_v3_v3v3(tmprow[x+1], GRIDELEM_CO_AT(data, v2, gridkey), GRIDELEM_CO_AT(data, v4, gridkey)); add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]); add_v3_v3(tmpgrid[v1], tmp); @@ -1117,11 +664,13 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no continue; index = x + y*gridsize; - co= data[index].co; - fno= data[index].no; + co= GRIDELEM_CO_AT(data, index, gridkey); + fno= GRIDELEM_NO_AT(data, index, gridkey); - if(sculpt_brush_test(&test, co)) { - const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno); + if(paint_stroke_test(&test, co)) { + float mask = (gridkey->mask ? + *GRIDELEM_MASK_AT(data, x + y*gridsize, gridkey) : 0); + const float fade = bstrength*tex_strength(ss, brush, co, mask, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno); float *avg, val[3]; float n; @@ -1155,8 +704,9 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no } } -static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength) +static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength) { + SculptSession *ss = ob->paint->sculpt; const int max_iterations = 4; const float fract = 1.0f/max_iterations; int iteration, n, count; @@ -1168,55 +718,57 @@ static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, last = max_iterations*(bstrength - count*fract); for(iteration = 0; iteration <= count; ++iteration) { - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { if(ss->multires) { - do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); + do_multires_smooth_brush(sd, ob, nodes[n], iteration != count ? 1.0f : last); } else if(ss->fmap) - do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last); + do_mesh_smooth_brush(sd, ob, nodes[n], iteration != count ? 1.0f : last); } if(ss->multires) - multires_stitch_grids(ss->ob); + multires_stitch_grids(ob); } } -static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - smooth(sd, ss, nodes, totnode, ss->cache->bstrength); + SculptSession *ss = ob->paint->sculpt; + smooth(sd, ob, nodes, totnode, ss->cache->bstrength); } -static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float offset[3], area_normal[3]; float bstrength= ss->cache->bstrength; int n; - calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3fl(offset, area_normal, paint_stroke_radius(ss->cache->stroke)); mul_v3_v3(offset, ss->cache->scale); mul_v3_fl(offset, bstrength); /* threaded loop over nodes */ - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test(&test, vd.co)) { - //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, area_normal)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test(&test, vd.co)) { + //if(paint_stroke_test_cyl(&test, vd.co, ss->cache->location, area_normal)) { /* offset vertex */ - float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + float fade = tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -1228,18 +780,19 @@ static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t } } -static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float offset[3], area_normal[3]; float bstrength= ss->cache->bstrength; float flippedbstrength, crease_correction; int n; - calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, area_normal, ss->cache->radius); + mul_v3_v3fl(offset, area_normal, paint_stroke_radius(ss->cache->stroke)); mul_v3_v3(offset, ss->cache->scale); mul_v3_fl(offset, bstrength); @@ -1256,20 +809,20 @@ static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; /* threaded loop over nodes */ - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { /* offset vertex */ - const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + const float fade = tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); float val1[3]; float val2[3]; @@ -1291,25 +844,26 @@ static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; int n; - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); float val[3]; sub_v3_v3v3(val, test.location, vd.co); @@ -1323,8 +877,9 @@ static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush= paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; float grab_delta[3], an[3]; @@ -1332,7 +887,7 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t float len; if (brush->normal_weight > 0) - calc_sculpt_normal(sd, ss, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode); copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -1344,26 +899,26 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t add_v3_v3(grab_delta, an); } - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptUndoNode* unode; - SculptBrushTest test; + PBVHUndoNode* unode; + PaintStrokeTest test; float (*origco)[3]; short (*origno)[3]; float (*proxy)[3]; - unode= sculpt_undo_push_node(ss, nodes[n]); - origco= unode->co; - origno= unode->no; + unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + origco= pbvh_undo_node_co(unode); + origno= pbvh_undo_node_no(unode); - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1375,8 +930,9 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t } } -static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; float grab_delta[3]; @@ -1386,24 +942,24 @@ static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - calc_sculpt_normal(sd, ss, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode); cross_v3_v3v3(tmp, an, grab_delta); cross_v3_v3v3(cono, tmp, an); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1415,8 +971,9 @@ static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; float grab_delta[3], an[3]; @@ -1424,7 +981,7 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, float len; if (brush->normal_weight > 0) - calc_sculpt_normal(sd, ss, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode); copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); @@ -1439,19 +996,19 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, add_v3_v3(grab_delta, an); } - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -1463,8 +1020,9 @@ static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, } } -static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; float grab_delta[3]; @@ -1474,31 +1032,31 @@ static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - calc_sculpt_normal(sd, ss, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode); cross_v3_v3v3(tmp, an, grab_delta); cross_v3_v3v3(cono, tmp, an); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptUndoNode* unode; - SculptBrushTest test; + PBVHUndoNode* unode; + PaintStrokeTest test; float (*origco)[3]; short (*origno)[3]; float (*proxy)[3]; - unode= sculpt_undo_push_node(ss, nodes[n]); - origco= unode->co; - origno= unode->no; + unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + origco= pbvh_undo_node_co(unode); + origno= pbvh_undo_node_no(unode); - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1510,8 +1068,9 @@ static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush= paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; float an[3]; @@ -1520,30 +1079,30 @@ static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; - calc_sculpt_normal(sd, ss, an, nodes, totnode); + calc_sculpt_normal(sd, ob, an, nodes, totnode); axis_angle_to_mat3(m, an, angle); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptUndoNode* unode; - SculptBrushTest test; + PBVHUndoNode* unode; + PaintStrokeTest test; float (*origco)[3]; short (*origno)[3]; float (*proxy)[3]; - unode= sculpt_undo_push_node(ss, nodes[n]); - origco= unode->co; - origno= unode->no; + unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + origco= pbvh_undo_node_co(unode); + origno= pbvh_undo_node_no(unode); - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, origco[vd.i])) { - const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, origco[vd.i])) { + const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], vd.mask_combined, test.dist)*frontface(brush, an, origno[vd.i], NULL); mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]); sub_v3_v3(proxy[vd.i], origco[vd.i]); @@ -1557,46 +1116,47 @@ static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; float area_normal[3], offset[3]; - float lim= ss->cache->radius / 4; + float lim= paint_stroke_radius(ss->cache->stroke) / 4; int n; if(bstrength < 0) lim = -lim; - calc_sculpt_normal(sd, ss, area_normal, nodes, totnode); + calc_sculpt_normal(sd, ob, area_normal, nodes, totnode); mul_v3_v3v3(offset, ss->cache->scale, area_normal); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; + PaintStrokeTest test; + PBVHUndoNode *unode; float (*origco)[3], *layer_disp; //float (*proxy)[3]; // XXX layer brush needs conversion to proxy but its more complicated - //proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + //proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - unode= sculpt_undo_push_node(ss, nodes[n]); - origco=unode->co; - if(!unode->layer_disp) - { - #pragma omp critical - unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp"); - } + unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + origco= pbvh_undo_node_co(unode); + if(!pbvh_undo_node_layer_disp(unode)) { + #pragma omp critical + pbvh_undo_node_set_layer_disp(unode, MEM_callocN(sizeof(float)* + pbvh_undo_node_totvert(unode), "layer disp")); + } - layer_disp= unode->layer_disp; + layer_disp= pbvh_undo_node_layer_disp(unode); - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + const float fade = bstrength*paint_stroke_radius(ss->cache->stroke)*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, area_normal, vd.no, vd.fno); float *disp= &layer_disp[vd.i]; float val[3]; @@ -1628,31 +1188,32 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int } } -static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength= ss->cache->bstrength; int n; - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test(&test, vd.co)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test(&test, vd.co)) { + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno); float val[3]; if(vd.fno) copy_v3_v3(val, vd.fno); else normal_short_to_float_v3(val, vd.no); - mul_v3_fl(val, fade * ss->cache->radius); + mul_v3_fl(val, fade * paint_stroke_radius(ss->cache->stroke)); mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); if(vd.mvert) @@ -1663,8 +1224,9 @@ static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in } } -static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3]) +static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float fc[3]) { + SculptSession *ss = ob->paint->sculpt; int n; float count = 0; @@ -1673,20 +1235,20 @@ static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, zero_v3(fc); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; + PaintStrokeTest test; + PBVHUndoNode *unode; float private_fc[3] = {0.0f, 0.0f, 0.0f}; int private_count = 0; - unode = sculpt_undo_push_node(ss, nodes[n]); - sculpt_brush_test_init(ss, &test); + unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + paint_stroke_test_init(&test, ss->cache->stroke); if(ss->cache->original) { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) { add_v3_v3(private_fc, vd.co); private_count++; } @@ -1694,8 +1256,8 @@ static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, BLI_pbvh_vertex_iter_end; } else { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, vd.co)) { add_v3_v3(private_fc, vd.co); private_count++; } @@ -1715,8 +1277,9 @@ static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, /* this calculates flatten center and area normal together, amortizing the memory bandwidth and loop overhead to calculate both at the same time */ -static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) { + SculptSession *ss = ob->paint->sculpt; int n; // an @@ -1733,26 +1296,26 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P // fc zero_v3(fc); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; - SculptUndoNode *unode; + PaintStrokeTest test; + PBVHUndoNode *unode; float private_an[3] = {0.0f, 0.0f, 0.0f}; float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; float private_fc[3] = {0.0f, 0.0f, 0.0f}; int private_count = 0; - unode = sculpt_undo_push_node(ss, nodes[n]); - sculpt_brush_test_init(ss, &test); + unode = pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + paint_stroke_test_init(&test, ss->cache->stroke); if(ss->cache->original) { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, unode->co[vd.i])) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, pbvh_undo_node_co(unode)[vd.i])) { // an float fno[3]; - normal_short_to_float_v3(fno, unode->no[vd.i]); + normal_short_to_float_v3(fno, pbvh_undo_node_no(unode)[vd.i]); add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); // fc @@ -1763,8 +1326,8 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P BLI_pbvh_vertex_iter_end; } else { - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if(sculpt_brush_test_fast(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(paint_stroke_test_fast(&test, vd.co)) { // an if(vd.no) { float fno[3]; @@ -1811,13 +1374,14 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, P } } -static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) + (paint_stroke_first_dab(ss->cache->stroke) || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: @@ -1843,7 +1407,7 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i break; case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc); + calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc); default: break; @@ -1852,7 +1416,7 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i // fc /* flatten center has not been calculated yet if we are not using the area normal */ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) - calc_flatten_center(sd, ss, nodes, totnode, fc); + calc_flatten_center(sd, ob, nodes, totnode, fc); // an copy_v3_v3(ss->cache->last_area_normal, an); @@ -1868,10 +1432,10 @@ static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, i copy_v3_v3(fc, ss->cache->last_center); // an - flip_coord(an, an, ss->cache->mirror_symmetry_pass); + paint_flip_coord(an, an, ss->cache->mirror_symmetry_pass); // fc - flip_coord(fc, fc, ss->cache->mirror_symmetry_pass); + paint_flip_coord(fc, fc, ss->cache->mirror_symmetry_pass); // an mul_m4_v3(ss->cache->symm_rot_mat, an); @@ -1891,7 +1455,8 @@ static void point_plane_project(float intr[3], float co[3], float plane_normal[3 static int plane_trim(StrokeCache *cache, Brush *brush, float val[3]) { - return !(brush->flag & BRUSH_PLANE_TRIM) || (dot_v3v3(val, val) <= cache->radius_squared*cache->plane_trim_squared); + return !(brush->flag & BRUSH_PLANE_TRIM) || + (dot_v3v3(val, val) <= paint_stroke_radius_squared(cache->stroke)*cache->plane_trim_squared); } static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) @@ -1922,18 +1487,19 @@ static float get_offset(Sculpt *sd, SculptSession *ss) float rv = brush->plane_offset; if (brush->flag & BRUSH_OFFSET_PRESSURE) { - rv *= ss->cache->pressure; + rv *= paint_stroke_pressure(ss->cache->stroke); } return rv; } -static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; - const float radius = ss->cache->radius; + const float radius = paint_stroke_radius(ss->cache->stroke); float an[3]; float fc[3]; @@ -1946,7 +1512,7 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in float temp[3]; - calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); displace = radius*offset; @@ -1954,18 +1520,18 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in mul_v3_fl(temp, displace); add_v3_v3(fc, temp); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test_sq(&test, vd.co)) { float intr[3]; float val[3]; @@ -1974,7 +1540,7 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -1987,12 +1553,13 @@ static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, in } } -static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; - float radius = ss->cache->radius; + float radius = paint_stroke_radius(ss->cache->stroke); float offset = get_offset(sd, ss); float displace; @@ -2007,7 +1574,7 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t int flip; - calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); flip = bstrength < 0; @@ -2024,20 +1591,20 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t //add_v3_v3v3(p, ss->cache->location, an); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test_sq(&test, vd.co)) { if (plane_point_side_flip(vd.co, an, fc, flip)) { - //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) { + //if (paint_stroke_test_cyl(&test, vd.co, ss->cache->location, p)) { float intr[3]; float val[3]; @@ -2046,7 +1613,7 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2060,12 +1627,13 @@ static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t } } -static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_clay_tubes_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; - float radius = ss->cache->radius; + float radius = paint_stroke_radius(ss->cache->stroke); float offset = get_offset(sd, ss); float displace; @@ -2083,15 +1651,15 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int flip; - calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc); if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ss, an, nodes, totnode); + calc_area_normal(sd, ob, an, nodes, totnode); else copy_v3_v3(an, sn); - if (ss->cache->first_time) - return; // delay the first daub because grab delta is not setup + if(paint_stroke_first_dab(ss->cache->stroke)) + return; // delay the first dab because grab delta is not setup flip = bstrength < 0; @@ -2109,24 +1677,25 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); mat[0][3] = 0; cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0; copy_v3_v3(mat[2], an); mat[2][3] = 0; - copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; + paint_stroke_symmetry_location(ss->cache->stroke, mat[3]); + mat[3][3] = 1; normalize_m4(mat); - scale_m4_fl(scale, ss->cache->radius); + scale_m4_fl(scale, paint_stroke_radius(ss->cache->stroke)); mul_m4_m4m4(tmat, scale, mat); invert_m4_m4(mat, tmat); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_cube(&test, vd.co, mat)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test_cube(&test, vd.co, mat)) { if (plane_point_side_flip(vd.co, sn, fc, flip)) { float intr[3]; float val[3]; @@ -2136,7 +1705,7 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, ss->cache->radius*test.dist)*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, paint_stroke_radius(ss->cache->stroke)*test.dist)*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2150,12 +1719,13 @@ static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, } } -static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; - const float radius = ss->cache->radius; + const float radius = paint_stroke_radius(ss->cache->stroke); float an[3]; float fc[3]; @@ -2167,7 +1737,7 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t float temp[3]; - calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); displace = radius*offset; @@ -2175,18 +1745,18 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t mul_v3_fl(temp, displace); add_v3_v3(fc, temp); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test_sq(&test, vd.co)) { if (plane_point_side(vd.co, an, fc)) { float intr[3]; float val[3]; @@ -2196,7 +1766,7 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2210,12 +1780,13 @@ static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t } } -static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { + SculptSession *ss = ob->paint->sculpt; Brush *brush = paint_brush(&sd->paint); float bstrength = ss->cache->bstrength; - const float radius = ss->cache->radius; + const float radius = paint_stroke_radius(ss->cache->stroke); float an[3]; float fc[3]; @@ -2227,7 +1798,7 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int float temp[3]; - calc_sculpt_plane(sd, ss, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); displace = -radius*offset; @@ -2235,18 +1806,18 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int mul_v3_fl(temp, displace); add_v3_v3(fc, temp); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; - SculptBrushTest test; + PaintStrokeTest test; float (*proxy)[3]; - proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; + proxy= BLI_pbvh_node_add_proxy(ob->paint->pbvh, nodes[n])->co; - sculpt_brush_test_init(ss, &test); + paint_stroke_test_init(&test, ss->cache->stroke); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq(&test, vd.co)) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if (paint_stroke_test_sq(&test, vd.co)) { if (!plane_point_side(vd.co, an, fc)) { float intr[3]; float val[3]; @@ -2256,7 +1827,7 @@ static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); + const float fade = bstrength*tex_strength(ss, brush, vd.co, vd.mask_combined, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2322,139 +1893,155 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) } /* copy the modified vertices from bvh to the active key */ -static void sculpt_update_keyblock(SculptSession *ss) +static void sculpt_update_keyblock(Object *ob) { - float (*vertCos)[3]= BLI_pbvh_get_vertCos(ss->pbvh); + SculptSession *ss = ob->paint->sculpt; + float (*vertCos)[3]= BLI_pbvh_get_vertCos(ob->paint->pbvh); if (vertCos) { - sculpt_vertcos_to_key(ss->ob, ss->kb, vertCos); + sculpt_vertcos_to_key(ob, ss->kb, vertCos); MEM_freeN(vertCos); } } -static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush) +static void sculpt_stroke_brush_action(bContext *C, PaintStroke *stroke) { - SculptSearchSphereData data; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->paint->sculpt; + Brush *brush = paint_brush(&sd->paint); + PBVHSearchSphereData data; PBVHNode **nodes = NULL; + float location[3]; int n, totnode; /* Build a list of all nodes that are potentially within the brush's area of influence */ - data.ss = ss; - data.sd = sd; - data.radius_squared = ss->cache->radius_squared; + paint_stroke_symmetry_location(ss->cache->stroke, location); + data.center = location; + data.radius_squared = paint_stroke_radius_squared(ss->cache->stroke); data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); - BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); + BLI_pbvh_search_gather(ob->paint->pbvh, BLI_pbvh_search_sphere_cb, &data, &nodes, &totnode); /* Only act if some verts are inside the brush area */ if (totnode) { - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) - for (n= 0; n < totnode; n++) { - sculpt_undo_push_node(ss, nodes[n]); - BLI_pbvh_node_mark_update(nodes[n]); - } - /* Apply one type of brush action */ - switch(brush->sculpt_tool){ - case SCULPT_TOOL_DRAW: - do_draw_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_SMOOTH: - do_smooth_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_CREASE: - do_crease_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_BLOB: - do_crease_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_PINCH: - do_pinch_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_INFLATE: - do_inflate_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_GRAB: - do_grab_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_ROTATE: - do_rotate_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_SNAKE_HOOK: - do_snake_hook_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_NUDGE: - do_nudge_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_THUMB: - do_thumb_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_FLATTEN: - do_flatten_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_CLAY: - do_clay_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_CLAY_TUBES: - do_clay_tubes_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_FILL: - do_fill_brush(sd, ss, nodes, totnode); - break; - case SCULPT_TOOL_SCRAPE: - do_scrape_brush(sd, ss, nodes, totnode); - break; + if(brush->flag & BRUSH_MASK) + paintmask_brush_apply(&sd->paint, stroke, nodes, + totnode, + ss->cache->bstrength); + else { + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) + for (n= 0; n < totnode; n++) { + pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + BLI_pbvh_node_mark_update(nodes[n]); + } + + switch(brush->sculpt_tool){ + case SCULPT_TOOL_DRAW: + do_draw_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SMOOTH: + do_smooth_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_PINCH: + do_pinch_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_INFLATE: + do_inflate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_GRAB: + do_grab_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_LAYER: + do_layer_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FLATTEN: + do_flatten_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY: + do_clay_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_TUBES: + do_clay_tubes_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FILL: + do_fill_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SCRAPE: + do_scrape_brush(sd, ob, nodes, totnode); + break; + } } if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) { if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { - smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure)); + smooth(sd, ob, nodes, totnode, brush->autosmooth_factor * + (1 - paint_stroke_pressure(ss->cache->stroke))); } else { - smooth(sd, ss, nodes, totnode, brush->autosmooth_factor); + smooth(sd, ob, nodes, totnode, brush->autosmooth_factor); } } /* copy the modified vertices from mesh to the active key */ if(ss->kb) - mesh_to_key(ss->ob->data, ss->kb); + mesh_to_key(ob->data, ss->kb); /* optimization: we could avoid copying new coords to keyblock at each */ /* stroke step if there are no modifiers due to pbvh is used for displaying */ /* so to increase speed we'll copy new coords to keyblock when stroke is done */ - if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss); + if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ob); MEM_freeN(nodes); } } -static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) +static void sculpt_combine_proxies(Sculpt *sd, Object *ob) { + SculptSession *ss = ob->paint->sculpt; Brush *brush= paint_brush(&sd->paint); PBVHNode** nodes; int totnode; int n; - BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + BLI_pbvh_gather_proxies(ob->paint->pbvh, &nodes, &totnode); switch (brush->sculpt_tool) { case SCULPT_TOOL_GRAB: case SCULPT_TOOL_ROTATE: case SCULPT_TOOL_THUMB: - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n= 0; n < totnode; n++) { PBVHVertexIter vd; PBVHProxyNode* proxies; + PBVHUndoNode *unode; int proxy_count; float (*origco)[3]; - origco= sculpt_undo_push_node(ss, nodes[n])->co; + unode= pbvh_undo_push_node(nodes[n], PBVH_UNDO_CO_NO, ob, NULL); + origco= pbvh_undo_node_co(unode); BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { float val[3]; int p; @@ -2484,7 +2071,7 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) case SCULPT_TOOL_PINCH: case SCULPT_TOOL_SCRAPE: case SCULPT_TOOL_SNAKE_HOOK: - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for (n= 0; n < totnode; n++) { PBVHVertexIter vd; PBVHProxyNode* proxies; @@ -2492,7 +2079,7 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count); - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { float val[3]; int p; @@ -2521,99 +2108,6 @@ static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss) MEM_freeN(nodes); } -//static int max_overlap_count(Sculpt *sd) -//{ -// int count[3]; -// int i, j; -// -// for (i= 0; i < 3; i++) { -// count[i] = sd->radial_symm[i]; -// -// for (j= 0; j < 3; j++) { -// if (i != j && sd->flags & (SCULPT_SYMM_X<<i)) -// count[i] *= 2; -// } -// } -// -// return MAX3(count[0], count[1], count[2]); -//} - -/* Flip all the editdata across the axis/axes specified by symm. Used to - calculate multiple modifications to the mesh when symmetry is enabled. */ -static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, const char axis, const float angle, const float UNUSED(feather)) -{ - (void)sd; /* unused */ - - flip_coord(cache->location, cache->true_location, symm); - flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm); - flip_coord(cache->view_normal, cache->true_view_normal, 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 (sd->flags & SCULPT_SYMMETRY_FEATHER) { - // float frac = 1.0f/max_overlap_count(sd); - // float reduce = (feather-frac)/(1-frac); - - // printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); - - // if (frac < 1) - // mul_v3_fl(cache->grab_delta_symmetry, reduce); - //} - - unit_m4(cache->symm_rot_mat); - unit_m4(cache->symm_rot_mat_inv); - rotate_m4(cache->symm_rot_mat, axis, angle); - rotate_m4(cache->symm_rot_mat_inv, axis, -angle); - - mul_m4_v3(cache->symm_rot_mat, cache->location); - mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); -} - -static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather) -{ - int i; - - for(i = 1; i < sd->radial_symm[axis-'X']; ++i) { - const float angle = 2*M_PI*i/sd->radial_symm[axis-'X']; - ss->cache->radial_symmetry_pass= i; - calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather); - do_brush_action(sd, ss, brush); - } -} - -static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss) -{ - Brush *brush = paint_brush(&sd->paint); - StrokeCache *cache = ss->cache; - const char symm = sd->flags & 7; - int i; - - float feather = calc_symmetry_feather(sd, ss->cache); - - cache->bstrength= brush_strength(sd, cache, feather); - - 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 */ - for(i = 0; i <= symm; ++i) { - if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - cache->mirror_symmetry_pass= i; - cache->radial_symmetry_pass= 0; - - calc_brushdata_symm(sd, cache, i, 0, 0, feather); - do_brush_action(sd, ss, brush); - - do_radial_symmetry(sd, ss, brush, i, 'X', feather); - do_radial_symmetry(sd, ss, brush, i, 'Y', feather); - do_radial_symmetry(sd, ss, brush, i, 'Z', feather); - } - } - - sculpt_combine_proxies(sd, ss); - - cache->first_time= 0; -} - static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) { Brush *brush = paint_brush(&sd->paint); @@ -2635,10 +2129,10 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) { DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - SculptSession *ss = ob->sculpt; - MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + SculptSession *ss = ob->paint->sculpt; + MultiresModifierData *mmd= ED_paint_multires_active(scene, ob); - ss->ob= ob; + ob= ob; ss->modifiers_active= sculpt_modifiers_active(scene, ob); @@ -2663,16 +2157,16 @@ void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) ss->multires = NULL; } - ss->pbvh = dm->getPBVH(ob, dm); + ob->paint->pbvh = dm->getPBVH(ob, dm); ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL; /* if pbvh is deformed, key block is already applied to it */ - if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) { + if (ss->kb && !BLI_pbvh_isDeformed(ob->paint->pbvh)) { float (*vertCos)[3]= key_to_vertcos(ob, ss->kb); if (vertCos) { /* apply shape keys coordinates to PBVH */ - BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); + BLI_pbvh_apply_vertCos(ob->paint->pbvh, vertCos); MEM_freeN(vertCos); } } @@ -2791,27 +2285,10 @@ static void SCULPT_OT_radial_control(wmOperatorType *ot) /**** Operator for applying a stroke (various attributes including mouse path) using the current brush. ****/ -static float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset) -{ - float delta[3], scale, loc[3]; - - mul_v3_m4v3(loc, ob->obmat, center); - - initgrabz(vc->rv3d, loc[0], loc[1], loc[2]); - window_to_3d_delta(vc->ar, delta, offset, 0); - - scale= fabsf(mat4_to_scale(ob->obmat)); - scale= (scale == 0.0f)? 1.0f: scale; - - return len_v3(delta)/scale; -} - static void sculpt_cache_free(StrokeCache *cache) { if(cache->face_norms) MEM_freeN(cache->face_norms); - if(cache->mats) - MEM_freeN(cache->mats); MEM_freeN(cache); } @@ -2827,6 +2304,7 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio int mode; ss->cache = cache; + ss->cache->stroke = op->customdata; /* Set scaling adjustment */ ss->cache->scale[0] = 1.0f / ob->size[0]; @@ -2852,16 +2330,6 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio } } - /* Initial mouse location */ - if (event) { - ss->cache->initial_mouse[0] = event->x; - ss->cache->initial_mouse[1] = event->y; - } - else { - ss->cache->initial_mouse[0] = 0; - ss->cache->initial_mouse[1] = 0; - } - mode = RNA_int_get(op->ptr, "mode"); cache->invert = mode == WM_BRUSHSTROKE_INVERT; cache->alt_smooth = mode == WM_BRUSHSTROKE_SMOOTH; @@ -2880,18 +2348,12 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio } } - copy_v2_v2(cache->mouse, cache->initial_mouse); - copy_v2_v2(cache->tex_mouse, cache->initial_mouse); - /* Truly temporary data that isn't stored in properties */ cache->vc = vc; cache->brush = brush; - cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats"); - view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats); - viewvector(cache->vc->rv3d, cache->vc->rv3d->twmat[3], cache->true_view_normal); /* Initialize layer brush displacements and persistent coords */ if(brush->sculpt_tool == SCULPT_TOOL_LAYER) { @@ -2922,17 +2384,15 @@ static void sculpt_update_cache_invariants(bContext* C, Sculpt *sd, SculptSessio if(!(brush->flag & BRUSH_ACCUMULATE)) cache->original = 1; - cache->special_rotation = (brush->flag & BRUSH_RAKE) ? sd->last_angle : 0; //cache->last_rake[0] = sd->last_x; //cache->last_rake[1] = sd->last_y; - cache->first_time= 1; - cache->vertex_rotation= 0; } -static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brush) +static void sculpt_update_brush_delta(Object *ob, Brush *brush) { + SculptSession *ss = ob->paint->sculpt; StrokeCache *cache = ss->cache; int tool = brush->sculpt_tool; @@ -2941,13 +2401,17 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus SCULPT_TOOL_CLAY_TUBES, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) { float grab_location[3], imat[4][4], delta[3]; + float mouse[2], location[3]; + + paint_stroke_mouse_location(cache->stroke, mouse); + paint_stroke_location(cache->stroke, location); - if(cache->first_time) { + if(paint_stroke_first_dab(cache->stroke)) { copy_v3_v3(cache->orig_grab_location, - cache->true_location); + location); } else if(tool == SCULPT_TOOL_SNAKE_HOOK) - add_v3_v3(cache->true_location, cache->grab_delta); + add_v3_v3(location, cache->grab_delta); /* compute 3d coordinate at same z from original location + mouse */ initgrabz(cache->vc->rv3d, @@ -2956,27 +2420,27 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus cache->orig_grab_location[2]); window_to_3d_delta(cache->vc->ar, grab_location, - cache->mouse[0], cache->mouse[1]); + mouse[0], mouse[1]); /* compute delta to move verts by */ - if(!cache->first_time) { + if(!paint_stroke_first_dab(cache->stroke)) { switch(tool) { case SCULPT_TOOL_GRAB: case SCULPT_TOOL_THUMB: sub_v3_v3v3(delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ss->ob->obmat); + invert_m4_m4(imat, ob->obmat); mul_mat3_m4_v3(imat, delta); add_v3_v3(cache->grab_delta, delta); break; case SCULPT_TOOL_CLAY_TUBES: case SCULPT_TOOL_NUDGE: sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ss->ob->obmat); + invert_m4_m4(imat, ob->obmat); mul_mat3_m4_v3(imat, cache->grab_delta); break; case SCULPT_TOOL_SNAKE_HOOK: sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ss->ob->obmat); + invert_m4_m4(imat, ob->obmat); mul_mat3_m4_v3(imat, cache->grab_delta); break; } @@ -2987,18 +2451,9 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus copy_v3_v3(cache->old_grab_location, grab_location); - if(tool == SCULPT_TOOL_GRAB) - copy_v3_v3(sd->anchored_location, cache->true_location); - else if(tool == SCULPT_TOOL_THUMB) - copy_v3_v3(sd->anchored_location, cache->orig_grab_location); - if(ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { /* location stays the same for finding vertices in brush radius */ - copy_v3_v3(cache->true_location, cache->orig_grab_location); - - sd->draw_anchored = 1; - copy_v3_v3(sd->anchored_initial_mouse, cache->initial_mouse); - sd->anchored_size = cache->pixel_radius; + copy_v3_v3(location, cache->orig_grab_location); } } } @@ -3006,221 +2461,74 @@ static void sculpt_update_brush_delta(Sculpt *sd, SculptSession *ss, Brush *brus /* Initialize the stroke cache variants from operator properties */ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) { + Object *ob = CTX_data_active_object(C); StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - int dx, dy; - - //RNA_float_get_array(ptr, "location", cache->traced_location); - - if (cache->first_time || - !((brush->flag & BRUSH_ANCHORED)|| - (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK)|| - (brush->sculpt_tool == SCULPT_TOOL_ROTATE)) - ) - { - RNA_float_get_array(ptr, "location", cache->true_location); - } - cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); - RNA_float_get_array(ptr, "mouse", cache->mouse); - - cache->pressure = RNA_float_get(ptr, "pressure"); /* Truly temporary data that isn't stored in properties */ - sd->draw_pressure= 1; - sd->pressure_value= cache->pressure; - - cache->previous_pixel_radius = cache->pixel_radius; - cache->pixel_radius = brush_size(brush); - - if(cache->first_time) { - if (!brush_use_locked_size(brush)) { - cache->initial_radius= unproject_brush_radius(ss->ob, cache->vc, cache->true_location, brush_size(brush)); - brush_set_unprojected_radius(brush, cache->initial_radius); - } - else { - cache->initial_radius= brush_unprojected_radius(brush); - } - - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK)) - cache->initial_radius *= 2.0f; - } - - if(brush_use_size_pressure(brush)) { - cache->pixel_radius *= cache->pressure; - cache->radius= cache->initial_radius * cache->pressure; - } - else - cache->radius= cache->initial_radius; - - cache->radius_squared = cache->radius*cache->radius; - - if(!(brush->flag & BRUSH_ANCHORED || ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE))) { - copy_v2_v2(cache->tex_mouse, cache->mouse); - - if ( (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) && - (brush->flag & BRUSH_RANDOM_ROTATION) && - !(brush->flag & BRUSH_RAKE)) - { - cache->special_rotation = 2*M_PI*BLI_frand(); - } - } - - if(brush->flag & BRUSH_ANCHORED) { - int hit = 0; - - dx = cache->mouse[0] - cache->initial_mouse[0]; - dy = cache->mouse[1] - cache->initial_mouse[1]; - - sd->anchored_size = cache->pixel_radius = sqrt(dx*dx + dy*dy); - - cache->special_rotation = atan2(dx, dy) + M_PI; - - if (brush->flag & BRUSH_EDGE_TO_EDGE) { - float halfway[2]; - float out[3]; - - halfway[0] = dx*0.5 + cache->initial_mouse[0]; - halfway[1] = dy*0.5 + cache->initial_mouse[1]; - - if (sculpt_stroke_get_location(C, stroke, out, halfway)) { - copy_v3_v3(sd->anchored_location, out); - copy_v2_v2(sd->anchored_initial_mouse, halfway); - copy_v2_v2(cache->tex_mouse, halfway); - copy_v3_v3(cache->true_location, sd->anchored_location); - sd->anchored_size /= 2.0f; - cache->pixel_radius /= 2.0f; - hit = 1; - } - } - - if (!hit) - copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); - - cache->radius= unproject_brush_radius(ss->ob, paint_stroke_view_context(stroke), cache->true_location, cache->pixel_radius); - cache->radius_squared = cache->radius*cache->radius; + sculpt_update_brush_delta(ob, brush); - copy_v3_v3(sd->anchored_location, cache->true_location); - - sd->draw_anchored = 1; - } - else if(brush->flag & BRUSH_RAKE) { - const float u = 0.5f; - const float v = 1 - u; - const float r = 20; - - const float dx = cache->last_rake[0] - cache->mouse[0]; - const float dy = cache->last_rake[1] - cache->mouse[1]; - - if (cache->first_time) { - copy_v2_v2(cache->last_rake, cache->mouse); - } - else if (dx*dx + dy*dy >= r*r) { - cache->special_rotation = atan2(dx, dy); - - cache->last_rake[0] = u*cache->last_rake[0] + v*cache->mouse[0]; - cache->last_rake[1] = u*cache->last_rake[1] + v*cache->mouse[1]; - } - } + if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + float mouse[2], initial_mouse[2]; + float dx, dy; - sculpt_update_brush_delta(sd, ss, brush); + paint_stroke_mouse_location(cache->stroke, mouse); + paint_stroke_initial_mouse_location(cache->stroke, initial_mouse); - if(brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - dx = cache->mouse[0] - cache->initial_mouse[0]; - dy = cache->mouse[1] - cache->initial_mouse[1]; + dx = mouse[0] - initial_mouse[0]; + dy = mouse[1] - initial_mouse[1]; cache->vertex_rotation = -atan2(dx, dy); - - sd->draw_anchored = 1; - copy_v2_v2(sd->anchored_initial_mouse, cache->initial_mouse); - copy_v3_v3(sd->anchored_location, cache->true_location); - sd->anchored_size = cache->pixel_radius; } - - sd->special_rotation = cache->special_rotation; } -static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss) +static void sculpt_stroke_modifiers_check(bContext *C) { - if(ss->modifiers_active) { + Object *ob = CTX_data_active_object(C); + + if(ob->paint->sculpt->modifiers_active) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = paint_brush(&sd->paint); - sculpt_update_mesh_elements(CTX_data_scene(C), ss->ob, brush->sculpt_tool == SCULPT_TOOL_SMOOTH); + sculpt_update_mesh_elements(CTX_data_scene(C), ob, brush->sculpt_tool == SCULPT_TOOL_SMOOTH); } } -typedef struct { - SculptSession *ss; - float *ray_start, *ray_normal; - int hit; - float dist; - int original; -} SculptRaycastData; - -void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin) +static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float* tmin) { if (BLI_pbvh_node_get_tmin(node) < *tmin) { - SculptRaycastData *srd = data_v; + PaintStrokeRaycastData *data = data_v; + SculptSession *ss = data->mode_data; float (*origco)[3]= NULL; - if(srd->original && srd->ss->cache) { + if(data->original && ss->cache) { /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode= sculpt_undo_get_node(node); - origco= (unode)? unode->co: NULL; + PBVHUndoNode *unode= pbvh_undo_get_node(node); + origco= (unode)? pbvh_undo_node_co(unode): NULL; } - if (BLI_pbvh_node_raycast(srd->ss->pbvh, node, origco, srd->ray_start, srd->ray_normal, &srd->dist)) { - srd->hit = 1; - *tmin = srd->dist; + if(BLI_pbvh_node_raycast(data->ob->paint->pbvh, node, origco, data->ray_start, data->ray_normal, &data->dist, NULL, NULL)) { + data->hit = 1; + *tmin = data->dist; } } } -/* Do a raycast in the tree to find the 3d brush location - (This allows us to ignore the GL depth buffer) - Returns 0 if the ray doesn't hit the mesh, non-zero otherwise - */ -int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]) -{ - ViewContext *vc = paint_stroke_view_context(stroke); - SculptSession *ss= vc->obact->sculpt; - StrokeCache *cache= ss->cache; - float ray_start[3], ray_end[3], ray_normal[3], dist; - float obimat[4][4]; - float mval[2]; - SculptRaycastData srd; - - mval[0] = mouse[0] - vc->ar->winrct.xmin; - mval[1] = mouse[1] - vc->ar->winrct.ymin; - - sculpt_stroke_modifiers_check(C, ss); - - viewline(vc->ar, vc->v3d, mval, ray_start, ray_end); - - invert_m4_m4(obimat, ss->ob->obmat); - mul_m4_v3(obimat, ray_start); - mul_m4_v3(obimat, ray_end); - - sub_v3_v3v3(ray_normal, ray_end, ray_start); - dist= normalize_v3(ray_normal); - - srd.ss = vc->obact->sculpt; - srd.ray_start = ray_start; - srd.ray_normal = ray_normal; - srd.dist = dist; - srd.hit = 0; - srd.original = (cache)? cache->original: 0; - BLI_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, - ray_start, ray_normal, srd.original); - - copy_v3_v3(out, ray_normal); - mul_v3_fl(out, srd.dist); - add_v3_v3(out, ray_start); +static int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]) +{ + ViewContext *vc = paint_stroke_view_context(stroke); + SculptSession *ss= vc->obact->paint->sculpt; + StrokeCache *cache= ss->cache; + int original; - return srd.hit; + sculpt_stroke_modifiers_check(C); + original = (cache)? cache->original: 0; + + return paint_stroke_get_location(C, stroke, sculpt_raycast_cb, ss, + out, mouse, original); } static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) @@ -3228,7 +2536,7 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) Scene *scene= CTX_data_scene(C); Object *ob= CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = CTX_data_active_object(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->paint->sculpt; Brush *brush = paint_brush(&sd->paint); if(ob_get_key(ob) && !(ob->shapeflag & OB_SHAPE_LOCK)) { @@ -3248,7 +2556,7 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) return 1; } -static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) +static void sculpt_restore_mesh(Sculpt *sd, Object *ob) { Brush *brush = paint_brush(&sd->paint); @@ -3257,26 +2565,27 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) (brush->sculpt_tool == SCULPT_TOOL_GRAB && brush_use_size_pressure(brush)) || (brush->flag & BRUSH_RESTORE_MESH)) { + SculptSession *ss = ob->paint->sculpt; StrokeCache *cache = ss->cache; int i; PBVHNode **nodes; int n, totnode; - BLI_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + BLI_pbvh_search_gather(ob->paint->pbvh, NULL, NULL, &nodes, &totnode); - #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) + #pragma omp parallel for schedule(guided) if (sd->paint.flags & PAINT_USE_OPENMP) for(n=0; n<totnode; n++) { - SculptUndoNode *unode; + PBVHUndoNode *unode; - unode= sculpt_undo_get_node(nodes[n]); + unode= pbvh_undo_get_node(nodes[n]); if(unode) { PBVHVertexIter vd; - BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { - copy_v3_v3(vd.co, unode->co[vd.i]); - if(vd.no) VECCOPY(vd.no, unode->no[vd.i]) - else normal_short_to_float_v3(vd.fno, unode->no[vd.i]); + BLI_pbvh_vertex_iter_begin(ob->paint->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + copy_v3_v3(vd.co, pbvh_undo_node_co(unode)[vd.i]); + if(vd.no) VECCOPY(vd.no, pbvh_undo_node_no(unode)[vd.i]) + else normal_short_to_float_v3(vd.fno, pbvh_undo_node_no(unode)[vd.i]); if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } @@ -3300,7 +2609,7 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) static void sculpt_flush_update(bContext *C) { Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->paint->sculpt; ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; @@ -3314,64 +2623,31 @@ static void sculpt_flush_update(bContext *C) ED_region_tag_redraw(ar); } else { - rcti r; + BLI_pbvh_update(ob->paint->pbvh, PBVH_UpdateBB, NULL); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - - if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { - //rcti tmp; - - r.xmin += ar->winrct.xmin + 1; - r.xmax += ar->winrct.xmin - 1; - r.ymin += ar->winrct.ymin + 1; - r.ymax += ar->winrct.ymin - 1; - - //tmp = r; - - //if (!BLI_rcti_is_empty(&ss->previous_r)) - // BLI_union_rcti(&r, &ss->previous_r); - - //ss->previous_r= tmp; - - ss->partial_redraw = 1; - ED_region_tag_redraw_partial(ar, &r); - } + paint_tag_partial_redraw(C, ob); } } -/* Returns whether the mouse/stylus is over the mesh (1) - or over the background (0) */ -static int over_mesh(bContext *C, struct wmOperator *op, float x, float y) +static int sculpt_stroke_test_start(bContext *C, wmOperator *op, wmEvent *event) { - float mouse[2], co[3]; - - mouse[0] = x; - mouse[1] = y; - - return sculpt_stroke_get_location(C, op->customdata, co, mouse); -} - -static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, - wmEvent *event) -{ - /* Don't start the stroke until mouse goes over the mesh */ - if(over_mesh(C, op, event->x, event->y)) { + if(paint_stroke_over_mesh(C, op->customdata, event->x, event->y)) { Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->paint->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); sculpt_update_cache_invariants(C, sd, ss, op, event); - sculpt_undo_push_begin(sculpt_tool_name(sd)); + pbvh_undo_push_begin(sculpt_tool_name(sd)); #ifdef _OPENMP /* If using OpenMP then create a number of threads two times the number of processor cores. Justification: Empirically I've found that two threads per processor gives higher throughput. */ - if (sd->flags & SCULPT_USE_OPENMP) { + if (sd->paint.flags & PAINT_USE_OPENMP) { int num_procs; num_procs = omp_get_num_procs(); @@ -3385,15 +2661,37 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, return 0; } +static void sculpt_stroke_update_symmetry(bContext *C, struct PaintStroke *stroke, + char symm, char axis, float angle, + int mirror_symmetry_pass, int radial_symmetry_pass, + float (*symmetry_rot_mat)[4]) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->paint->sculpt; + StrokeCache *cache = ss->cache; + + paint_flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm); + paint_flip_coord(cache->view_normal, cache->true_view_normal, symm); + + copy_m4_m4(cache->symm_rot_mat, symmetry_rot_mat); + + mul_m4_v3(symmetry_rot_mat, cache->grab_delta_symmetry); +} + static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = CTX_data_active_object(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->paint->sculpt; + StrokeCache *cache = ss->cache; - sculpt_stroke_modifiers_check(C, ss); + sculpt_stroke_modifiers_check(C); sculpt_update_cache_variants(C, sd, ss, stroke, itemptr); - sculpt_restore_mesh(sd, ss); - do_symmetrical_brush_actions(sd, ss); + sculpt_restore_mesh(sd, ob); + + cache->bstrength= brush_strength(sd, cache, paint_stroke_feather(stroke)); + paint_stroke_apply_brush(C, stroke, &sd->paint); + sculpt_combine_proxies(sd, ob); /* Cleanup */ sculpt_flush_update(C); @@ -3402,19 +2700,14 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused) { Object *ob= CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->paint->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; (void)unused; - // reset values used to draw brush after completing the stroke - sd->draw_anchored= 0; - sd->draw_pressure= 0; - sd->special_rotation= 0; - /* Finished */ if(ss->cache) { - sculpt_stroke_modifiers_check(C, ss); + sculpt_stroke_modifiers_check(C); /* Alt-Smooth */ if (ss->cache->alt_smooth) { @@ -3428,16 +2721,16 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused) sculpt_cache_free(ss->cache); ss->cache = NULL; - sculpt_undo_push_end(); + pbvh_undo_push_end(); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); + BLI_pbvh_update(ob->paint->pbvh, PBVH_UpdateOriginalBB, NULL); /* optimization: if there is locked key and active modifiers present in */ /* the stack, keyblock is updating at each step. otherwise we could update */ /* keyblock only when stroke is finished */ - if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ss); + if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ob); - ss->partial_redraw = 0; + ob->paint->partial_redraw = 0; /* try to avoid calling this, only for e.g. linked duplicates now */ if(((Mesh*)ob->data)->id.us > 1) @@ -3447,8 +2740,22 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *unused) } } +static void sculpt_stroke_set_modifiers(PaintStroke *stroke, Brush *brush) +{ + if(ELEM(brush->sculpt_tool, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ROTATE)) + paint_stroke_set_modifier_use_original_location(stroke); + + if(ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK)) + paint_stroke_set_modifier_initial_radius_factor(stroke, 2.0f); + + if(ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) + paint_stroke_set_modifier_use_original_texture_coords(stroke); +} + static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event) { + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; struct PaintStroke *stroke; int ignore_background_click; @@ -3458,15 +2765,18 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even stroke = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, + sculpt_stroke_update_symmetry, + sculpt_stroke_brush_action, sculpt_stroke_done); op->customdata = stroke; + sculpt_stroke_set_modifiers(stroke, paint_brush(&sd->paint)); /* For tablet rotation */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if(ignore_background_click && !over_mesh(C, op, event->x, event->y)) { + if(ignore_background_click && !paint_stroke_over_mesh(C, stroke, event->x, event->y)) { paint_stroke_free(stroke); return OPERATOR_PASS_THROUGH; } @@ -3482,13 +2792,19 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = CTX_data_active_object(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->paint->sculpt; if(!sculpt_brush_stroke_init(C, op->reports)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, - sculpt_stroke_update_step, sculpt_stroke_done); + op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + sculpt_stroke_update_symmetry, + sculpt_stroke_brush_action, + sculpt_stroke_done); + + sculpt_stroke_set_modifiers(op->customdata, paint_brush(&sd->paint)); sculpt_update_cache_invariants(C, sd, ss, op, NULL); @@ -3519,7 +2835,7 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->exec= sculpt_brush_stroke_exec; ot->poll= sculpt_poll; - /* flags (sculpt does own undo? (ton) */ + /* flags */ ot->flag= OPTYPE_BLOCKING; /* properties */ @@ -3536,11 +2852,114 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) "Clicks on the background do not start the stroke"); } +static void sculpt_area_hide_update(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + DerivedMesh *dm; + + dm = mesh_get_derived_final(scene, ob, 0); + /* Force the removal of the old pbvh */ + if(ob->paint->pbvh) { + BLI_pbvh_free(ob->paint->pbvh); + ob->paint->pbvh = NULL; + } + dm->getPBVH(NULL, dm); + + /* Update */ + sculpt_update_mesh_elements(scene, ob, 0); +} + +static int sculpt_area_hide_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + int show_all = RNA_boolean_get(op->ptr, "show_all"); + + if(show_all) { + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->paint->sculpt; + ARegion *ar= CTX_wm_region(C); + + if(ss->hidden_areas.first) { + /* Free all hidden areas */ + BLI_freelistN(&ss->hidden_areas); + sculpt_area_hide_update(C); + + /* Avoid cracks in multires */ + if(ss->multires) { + BLI_pbvh_search_callback(ob->paint->pbvh, NULL, NULL, + BLI_pbvh_node_set_flags, + SET_INT_IN_POINTER(PBVH_UpdateAll)); + multires_stitch_grids(ob); + } + + ED_region_tag_redraw(ar); + } + return OPERATOR_FINISHED; + } + else + return WM_border_select_invoke(C, op, event); +} + +static int sculpt_area_hide_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->paint->sculpt; + ViewContext vc; + bglMats mats; + BoundBox bb; + rcti rect; + HiddenArea *area; + + memset(&mats, 0, sizeof(bglMats)); + rect.xmin= RNA_int_get(op->ptr, "xmin"); + rect.ymin= RNA_int_get(op->ptr, "ymin"); + rect.xmax= RNA_int_get(op->ptr, "xmax"); + rect.ymax= RNA_int_get(op->ptr, "ymax"); + + area = MEM_callocN(sizeof(HiddenArea), "hidden_area"); + area->hide_inside = RNA_boolean_get(op->ptr, "hide_inside"); + + view3d_operator_needs_opengl(C); + view3d_set_viewcontext(C, &vc); + view3d_get_transformation(vc.ar, vc.rv3d, ob, &mats); + view3d_calculate_clipping(&bb, area->clip_planes, &mats, &rect); + mul_m4_fl(area->clip_planes, -1.0f); + + BLI_addtail(&ss->hidden_areas, area); + + sculpt_area_hide_update(C); + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_area_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Hide Area"; + ot->idname= "SCULPT_OT_area_hide"; + + /* api callbacks */ + ot->invoke= sculpt_area_hide_invoke; + ot->modal= WM_border_select_modal; + ot->exec= sculpt_area_hide_exec; + ot->poll= sculpt_mode_poll; + + ot->flag= OPTYPE_REGISTER; + + /* rna */ + RNA_def_boolean(ot->srna, "show_all", 0, "Show All", ""); + RNA_def_boolean(ot->srna, "hide_inside", 0, "Hide Inside", ""); + RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX); +} + /**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/ static int sculpt_set_persistent_base(bContext *C, wmOperator *unused) { - SculptSession *ss = CTX_data_active_object(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->paint->sculpt; (void)unused; @@ -3570,8 +2989,9 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) static void sculpt_init_session(Scene *scene, Object *ob) { - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->ob = ob; + create_paintsession(ob); + + ob->paint->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); sculpt_update_mesh_elements(scene, ob, 0); } @@ -3581,7 +3001,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused) Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); - MultiresModifierData *mmd= sculpt_multires_active(scene, ob); + MultiresModifierData *mmd= ED_paint_multires_active(scene, ob); int flush_recalc= 0; (void)unused; @@ -3601,7 +3021,7 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused) /* Leave sculptmode */ ob->mode &= ~OB_MODE_SCULPT; - free_sculptsession(ob); + free_paintsession(ob); } else { /* Enter sculptmode */ @@ -3615,13 +3035,9 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *unused) ts->sculpt = MEM_callocN(sizeof(Sculpt), "sculpt mode data"); /* Turn on X plane mirror symmetry by default */ - ts->sculpt->flags |= SCULPT_SYMM_X; + ts->sculpt->paint.flags |= PAINT_SYMM_X; } - /* Create sculpt mode session data */ - if(ob->sculpt) - free_sculptsession(ob); - sculpt_init_session(scene, ob); paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT); @@ -3653,5 +3069,6 @@ void ED_operatortypes_sculpt() WM_operatortype_append(SCULPT_OT_brush_stroke); WM_operatortype_append(SCULPT_OT_sculptmode_toggle); WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_area_hide); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 254876d9b68..f8704f9c69b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -37,79 +37,18 @@ #include "BLI_pbvh.h" struct bContext; -struct Brush; struct KeyBlock; -struct Mesh; -struct MultiresModifierData; struct Object; struct Scene; -struct Sculpt; -struct SculptStroke; - -/* Interface */ -void sculptmode_selectbrush_menu(void); -void sculptmode_draw_mesh(int); -void sculpt_paint_brush(char clear); -void sculpt_stroke_draw(struct SculptStroke *); -void sculpt_radialcontrol_start(int mode); -struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); - -struct Brush *sculptmode_brush(void); -//void do_symmetrical_brush_actions(struct Sculpt *sd, struct wmOperator *wm, struct BrushAction *a, short *, short *); - -void sculpt(Sculpt *sd); +struct PBVHNode; +struct SculptUndoNode; int sculpt_poll(struct bContext *C); void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap); -/* Stroke */ -struct SculptStroke *sculpt_stroke_new(const int max); -void sculpt_stroke_free(struct SculptStroke *); -void sculpt_stroke_add_point(struct SculptStroke *, const short x, const short y); -void sculpt_stroke_apply(struct Sculpt *sd, struct SculptStroke *); -void sculpt_stroke_apply_all(struct Sculpt *sd, struct SculptStroke *); -int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float out[3], float mouse[2]); - -/* Partial Mesh Visibility */ -void sculptmode_pmv(int mode); - -/* Undo */ - -typedef struct SculptUndoNode { - struct SculptUndoNode *next, *prev; - - char idname[MAX_ID_NAME]; /* name instead of pointer*/ - void *node; /* only during push, not valid afterwards! */ - - float (*co)[3]; - short (*no)[3]; - int totvert; - - /* non-multires */ - int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ - - /* multires */ - int maxgrid; /* same for grid */ - int gridsize; /* same for grid */ - int totgrid; /* to restore into right location */ - int *grids; /* to restore into right location */ - - /* layer brush */ - float *layer_disp; - - /* shape keys */ - char *shapeName[32]; /* keep size in sync with keyblock dna */ -} SculptUndoNode; - -SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node); -SculptUndoNode *sculpt_undo_get_node(PBVHNode *node); -void sculpt_undo_push_begin(char *name); -void sculpt_undo_push_end(void); - struct MultiresModifierData *sculpt_multires_active(struct Scene *scene, struct Object *ob); -int sculpt_modifiers_active(Scene *scene, Object *ob); -void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); +int sculpt_modifiers_active(struct Scene *scene, struct Object *ob); +void sculpt_vertcos_to_key(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]); void brush_jitter_pos(struct Brush *brush, float *pos, float *jitterpos); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c deleted file mode 100644 index e92740678fd..00000000000 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * $Id$ - * - * ***** 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. - * - * The Original Code is Copyright (C) 2006 by Nicholas Bishop - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL LICENSE BLOCK ***** - * - * Implements the Sculpt Mode tools - * - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_math.h" -#include "BLI_ghash.h" -#include "BLI_threads.h" - -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_mesh_types.h" - -#include "BKE_cdderivedmesh.h" -#include "BKE_context.h" -#include "BKE_depsgraph.h" -#include "BKE_modifier.h" -#include "BKE_multires.h" -#include "BKE_paint.h" -#include "BKE_key.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_sculpt.h" -#include "paint_intern.h" -#include "sculpt_intern.h" - -/************************** Undo *************************/ - -static void update_cb(PBVHNode *node, void *unused) -{ - (void)unused; - BLI_pbvh_node_mark_update(node); -} - -static void sculpt_undo_restore(bContext *C, ListBase *lb) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); - SculptSession *ss = ob->sculpt; - SculptUndoNode *unode; - MVert *mvert; - MultiresModifierData *mmd; - int *index; - int i, j, update= 0; - - sculpt_update_mesh_elements(scene, ob, 0); - - for(unode=lb->first; unode; unode=unode->next) { - if(!(strcmp(unode->idname, ob->id.name)==0)) - continue; - - if(unode->maxvert) { - char *shapeName= (char*)unode->shapeName; - - /* regular mesh restore */ - if(ss->totvert != unode->maxvert) - continue; - - if (ss->kb && strcmp(ss->kb->name, shapeName)) { - /* shape key has been changed before calling undo operator */ - - Key *key= ob_get_key(ob); - KeyBlock *kb= key_get_named_keyblock(key, shapeName); - - if (kb) { - ob->shapenr= BLI_findindex(&key->block, kb) + 1; - ob->shapeflag|= OB_SHAPE_LOCK; - - sculpt_update_mesh_elements(scene, ob, 0); - WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob); - } else { - /* key has been removed -- skip this undo node */ - continue; - } - } - - index= unode->index; - mvert= ss->mvert; - - if (ss->kb) { - float (*vertCos)[3]; - vertCos= key_to_vertcos(ob, ss->kb); - - for(i=0; i<unode->totvert; i++) - swap_v3_v3(vertCos[index[i]], unode->co[i]); - - /* propagate new coords to keyblock */ - sculpt_vertcos_to_key(ob, ss->kb, vertCos); - - /* pbvh uses it's own mvert array, so coords should be */ - /* propagated to pbvh here */ - BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); - - MEM_freeN(vertCos); - } else { - for(i=0; i<unode->totvert; i++) { - swap_v3_v3(mvert[index[i]].co, unode->co[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } - } - else if(unode->maxgrid && dm->getGridData) { - /* multires restore */ - DMGridData **grids, *grid; - float (*co)[3]; - int gridsize; - - if(dm->getNumGrids(dm) != unode->maxgrid) - continue; - if(dm->getGridSize(dm) != unode->gridsize) - continue; - - grids= dm->getGridData(dm); - gridsize= dm->getGridSize(dm); - - co = unode->co; - for(j=0; j<unode->totgrid; j++) { - grid= grids[unode->grids[j]]; - - for(i=0; i<gridsize*gridsize; i++, co++) - swap_v3_v3(grid[i].co, co[0]); - } - } - - update= 1; - } - - if(update) { - /* we update all nodes still, should be more clever, but also - needs to work correct when exiting/entering sculpt mode and - the nodes get recreated, though in that case it could do all */ - BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL); - - if((mmd=sculpt_multires_active(scene, ob))) - multires_mark_as_modified(ob); - - if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1) - DAG_id_flush_update(&ob->id, OB_RECALC_DATA); - } -} - -static void sculpt_undo_free(ListBase *lb) -{ - SculptUndoNode *unode; - - for(unode=lb->first; unode; unode=unode->next) { - if(unode->co) - MEM_freeN(unode->co); - if(unode->no) - MEM_freeN(unode->no); - if(unode->index) - MEM_freeN(unode->index); - if(unode->grids) - MEM_freeN(unode->grids); - if(unode->layer_disp) - MEM_freeN(unode->layer_disp); - } -} - -SculptUndoNode *sculpt_undo_get_node(PBVHNode *node) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode; - - if(!lb) - return NULL; - - for(unode=lb->first; unode; unode=unode->next) - if(unode->node == node) - return unode; - - return NULL; -} - -SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - Object *ob= ss->ob; - SculptUndoNode *unode; - int totvert, allvert, totgrid, maxgrid, gridsize, *grids; - - /* list is manipulated by multiple threads, so we lock */ - BLI_lock_thread(LOCK_CUSTOM1); - - if((unode= sculpt_undo_get_node(node))) { - BLI_unlock_thread(LOCK_CUSTOM1); - return unode; - } - - unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); - strcpy(unode->idname, ob->id.name); - unode->node= node; - - BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); - - unode->totvert= totvert; - /* we will use this while sculpting, is mapalloc slow to access then? */ - unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co"); - unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no"); - undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert); - BLI_addtail(lb, unode); - - if(maxgrid) { - /* multires */ - unode->maxgrid= maxgrid; - unode->totgrid= totgrid; - unode->gridsize= gridsize; - unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids"); - } - else { - /* regular mesh */ - unode->maxvert= ss->totvert; - unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index"); - } - - BLI_unlock_thread(LOCK_CUSTOM1); - - /* copy threaded, hopefully this is the performance critical part */ - { - PBVHVertexIter vd; - - BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { - copy_v3_v3(unode->co[vd.i], vd.co); - if(vd.no) VECCOPY(unode->no[vd.i], vd.no) - else normal_float_to_short_v3(unode->no[vd.i], vd.fno); - if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i]; - } - BLI_pbvh_vertex_iter_end; - } - - if(unode->grids) - memcpy(unode->grids, grids, sizeof(int)*totgrid); - - /* store active shape key */ - if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); - else unode->shapeName[0]= '\0'; - - return unode; -} - -void sculpt_undo_push_begin(char *name) -{ - undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free); -} - -void sculpt_undo_push_end(void) -{ - ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH); - SculptUndoNode *unode; - - /* we don't need normals in the undo stack */ - for(unode=lb->first; unode; unode=unode->next) { - if(unode->no) { - MEM_freeN(unode->no); - unode->no= NULL; - } - - if(unode->layer_disp) { - MEM_freeN(unode->layer_disp); - unode->layer_disp= NULL; - } - } - - undo_paint_push_end(UNDO_PAINT_MESH); -} diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 0df2888f4a0..6b166b470be 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -648,7 +648,7 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *o dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, me->edit_mesh); } else if(faceselect) { if(ob->mode & OB_MODE_WEIGHT_PAINT) - dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me, 1, GPU_enable_material); + dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, me, GPU_enable_material, DM_DRAW_VERTEX_COLORS); else dm->drawMappedFacesTex(dm, draw_tface_mapped__set_draw, me); } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index e7a69642e9f..5ac76719517 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -1872,7 +1872,7 @@ static void draw_dm_faces_sel(DerivedMesh *dm, unsigned char *baseCol, unsigned data.cols[2] = actCol; data.efa_act = efa_act; - dm->drawMappedFaces(dm, draw_dm_faces_sel__setDrawOptions, &data, 0, GPU_enable_material); + dm->drawMappedFaces(dm, NULL, draw_dm_faces_sel__setDrawOptions, &data, GPU_enable_material, 0); } static int draw_dm_creases__setDrawOptions(void *UNUSED(userData), int index) @@ -2292,7 +2292,7 @@ static void draw_em_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object glEnable(GL_LIGHTING); glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW); - finalDM->drawMappedFaces(finalDM, draw_em_fancy__setFaceOpts, 0, 0, GPU_enable_material); + finalDM->drawMappedFaces(finalDM, NULL, draw_em_fancy__setFaceOpts, NULL, GPU_enable_material, 0); glFrontFace(GL_CCW); glDisable(GL_LIGHTING); @@ -2425,7 +2425,7 @@ static void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm) drawFacesSolid() doesn't draw the transparent faces */ if(ob->dtx & OB_DRAWTRANSP) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); + dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GPU_disable_material(); } @@ -2454,6 +2454,9 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D int totvert, totedge, totface; DispList *dl; DerivedMesh *dm= mesh_get_derived_final(scene, ob, v3d->customdata_mask); + Paint *p; + float planes[4][4], (*paint_redraw_planes)[4] = NULL; + DMDrawFlags fast_navigate = 0; if(!dm) return; @@ -2465,6 +2468,24 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D totvert = dm->getNumVerts(dm); totedge = dm->getNumEdges(dm); totface = dm->getNumFaces(dm); + + /* setup for fast paint/sculpt drawing */ + if((ob->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)) && + (p=paint_get_active(scene))) { + /* drop down to a low multires level during navigation */ + fast_navigate = ((p->flags & PAINT_FAST_NAVIGATE) && + (rv3d->rflag & RV3D_NAVIGATING))? DM_DRAW_LOWEST_SUBDIVISION_LEVEL : 0; + + if(ob->paint && ob->paint->partial_redraw) { + if(ar->do_draw & RGN_DRAW_PARTIAL) { + paint_get_redraw_planes(planes, ar, rv3d, ob); + paint_redraw_planes = planes; + ob->paint->partial_redraw = 0; + } + + } + } + /* vertexpaint, faceselect wants this, but it doesnt work for shaded? */ if(dt!=OB_SHADED) @@ -2485,7 +2506,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D else if(dt==OB_WIRE || totface==0) { draw_wire = 1; /* draw wire only, no depth buffer stuff */ } - else if( (ob==OBACT && (ob->mode & OB_MODE_TEXTURE_PAINT || paint_facesel_test(ob))) || + else if( (ob==OBACT && (ob->mode & OB_MODE_TEXTURE_PAINT)) || CHECK_OB_DRAWTEXTURE(v3d, dt)) { int faceselect= (ob==OBACT && paint_facesel_test(ob)); @@ -2522,8 +2543,11 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D /* weight paint in solid mode, special case. focus on making the weights clear * rather then the shading, this is also forced in wire view */ GPU_enable_material(0, NULL); - dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me->mface, 1, GPU_enable_material); - + + dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, + me->mface, GPU_enable_material, + DM_DRAW_VERTEX_COLORS); + bglPolygonOffset(rv3d->dist, 1.0); glDepthMask(0); // disable write in zbuffer, selected edge wires show better @@ -2544,9 +2568,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D draw_wire= 0; } else { - Paint *p; - - if((v3d->flag&V3D_SELECT_OUTLINE) && ((v3d->flag2 & V3D_RENDER_OVERRIDE)==0) && (base->flag&SELECT) && !draw_wire && !ob->sculpt) + if((v3d->flag&V3D_SELECT_OUTLINE) && ((v3d->flag2 & V3D_RENDER_OVERRIDE)==0) && (base->flag&SELECT) && !draw_wire && !ob->paint) draw_mesh_object_outline(v3d, ob, dm); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, me->flag & ME_TWOSIDED ); @@ -2554,23 +2576,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D glEnable(GL_LIGHTING); glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW); - if(ob->sculpt && (p=paint_get_active(scene))) { - float planes[4][4]; - float (*fpl)[4] = NULL; - int fast= (p->flags & PAINT_FAST_NAVIGATE) && (rv3d->rflag & RV3D_NAVIGATING); - - if(ob->sculpt->partial_redraw) { - if(ar->do_draw & RGN_DRAW_PARTIAL) { - sculpt_get_redraw_planes(planes, ar, rv3d, ob); - fpl = planes; - ob->sculpt->partial_redraw = 0; - } - } - - dm->drawFacesSolid(dm, fpl, fast, GPU_enable_material); - } - else - dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); + dm->drawFacesSolid(dm, paint_redraw_planes, GPU_enable_material, fast_navigate|DM_DRAW_PAINT_MASK); GPU_disable_material(); @@ -2582,7 +2588,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D } else { UI_ThemeColor(TH_WIRE); } - if(!ob->sculpt && (v3d->flag2 & V3D_RENDER_OVERRIDE)==0) + if(!ob->paint && (v3d->flag2 & V3D_RENDER_OVERRIDE)==0) dm->drawLooseEdges(dm); } } @@ -2605,18 +2611,36 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); - dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, me->mface, 1, GPU_enable_material); + dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, + me->mface, GPU_enable_material, + DM_DRAW_VERTEX_COLORS); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); GPU_disable_material(); } else if(ob->mode & (OB_MODE_VERTEX_PAINT|OB_MODE_TEXTURE_PAINT)) { - if(me->mcol) - dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, NULL, 1, GPU_enable_material); + if(me->mcol) { + MFace *mface = get_mesh(ob)->mface; + DMDrawFlags dm_flags = DM_DRAW_PTEX; + + /* XXX - temporary - set up nicer drawing for new vpaint */ + GPU_enable_material(mface->mat_nr+1, NULL); + glEnable(GL_LIGHTING); + dm_flags |= fast_navigate; + if(me->editflag & ME_EDIT_PTEX) + dm_flags |= DM_DRAW_PTEX_TEXELS; + + dm->drawMappedFaces(dm, paint_redraw_planes, + wpaint__setSolidDrawOptions, NULL, + GPU_enable_material, + dm_flags); + glDisable(GL_LIGHTING); + } else { glColor3f(1.0f, 1.0f, 1.0f); - dm->drawMappedFaces(dm, wpaint__setSolidDrawOptions, NULL, 0, GPU_enable_material); + + dm->drawMappedFaces(dm, NULL, wpaint__setSolidDrawOptions, NULL, GPU_enable_material, 0); } } else do_draw= 1; @@ -3082,7 +3106,7 @@ static int drawCurveDerivedMesh(Scene *scene, View3D *v3d, RegionView3D *rv3d, B if(!glsl) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0); glEnable(GL_LIGHTING); - dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); + dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0); glDisable(GL_LIGHTING); } else @@ -6334,7 +6358,7 @@ static void bbs_mesh_solid_EM(Scene *scene, View3D *v3d, Object *ob, DerivedMesh cpack(0); if (facecol) { - dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, (void*)(intptr_t) 1, 0, GPU_enable_material); + dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setSolidDrawOptions, (void*)(intptr_t) 1, GPU_enable_material, 0); if(check_ob_drawface_dot(scene, v3d, ob->dt)) { glPointSize(UI_GetThemeValuef(TH_FACEDOT_SIZE)); @@ -6345,7 +6369,7 @@ static void bbs_mesh_solid_EM(Scene *scene, View3D *v3d, Object *ob, DerivedMesh } } else { - dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, (void*) 0, 0, GPU_enable_material); + dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setSolidDrawOptions, (void*) 0, GPU_enable_material, 0); } } @@ -6375,8 +6399,14 @@ static void bbs_mesh_solid(Scene *scene, View3D *v3d, Object *ob) glColor3ub(0, 0, 0); - if(face_sel_mode) dm->drawMappedFaces(dm, bbs_mesh_solid_hide__setDrawOpts, me, 0, GPU_enable_material); - else dm->drawMappedFaces(dm, bbs_mesh_solid__setDrawOpts, me, 0, GPU_enable_material); + if(face_sel_mode) + dm->drawMappedFaces(dm, NULL, bbs_mesh_solid_hide__setDrawOpts, + me, GPU_enable_material, + DM_DRAW_BACKBUF_SELECTION); + else + dm->drawMappedFaces(dm, NULL, bbs_mesh_solid__setDrawOpts, + me, GPU_enable_material, + DM_DRAW_BACKBUF_SELECTION); dm->release(dm); } @@ -6479,11 +6509,11 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r glEnable(GL_LIGHTING); if(dm) { - dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); + dm->drawFacesSolid(dm, NULL, GPU_enable_material, 0); GPU_end_object_materials(); } else if(edm) - edm->drawMappedFaces(edm, NULL, NULL, 0, GPU_enable_material); + edm->drawMappedFaces(edm, NULL, NULL, NULL, GPU_enable_material, 0); glDisable(GL_LIGHTING); } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 67c2a4f1c56..ef3894a02e0 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -74,6 +74,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_screen_types.h" +#include "ED_sculpt.h" #include "ED_transform.h" #include "UI_interface.h" @@ -2205,6 +2206,37 @@ static void draw_viewport_fps(Scene *scene, ARegion *ar) BLF_draw_default(22, ar->winy-17, 0.0f, printable); } + +void debug_draw_redraw_area(ARegion *ar) +{ + rcti winrct; + + if(G.rt != 444) + return; + + /* do nothing if it looks like this isn't a partial redraw */ + region_scissor_winrct(ar, &winrct); + if(ar->drawrct.xmin == winrct.xmin && + ar->drawrct.xmax == winrct.xmax && + ar->drawrct.ymin == winrct.ymin && + ar->drawrct.ymax == winrct.ymax) + return; + + /* choose a nice pastel color so that debugging is kept cheerful */ + glColor3f((rand() / (float)RAND_MAX) * 0.3 + 0.7, + (rand() / (float)RAND_MAX) * 0.3 + 0.7, + (rand() / (float)RAND_MAX) * 0.3 + 0.7); + + /* draw the redraw area, pull in by 2px so it doesn't get + clipped by the scissor */ + glBegin(GL_LINE_LOOP); + glVertex2i(ar->drawrct.xmin-winrct.xmin+2, ar->drawrct.ymin-winrct.ymin+2); + glVertex2i(ar->drawrct.xmax-winrct.xmin-2, ar->drawrct.ymin-winrct.ymin+2); + glVertex2i(ar->drawrct.xmax-winrct.xmin-2, ar->drawrct.ymax-winrct.ymin-2); + glVertex2i(ar->drawrct.xmin-winrct.xmin+2, ar->drawrct.ymax-winrct.ymin-2); + glEnd(); +} + void view3d_main_area_draw(const bContext *C, ARegion *ar) { Scene *scene= CTX_data_scene(C); @@ -2404,12 +2436,12 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) } ED_region_pixelspace(ar); - -// retopo_paint_view_update(v3d); -// retopo_draw_paint_lines(); - - /* Draw particle edit brush XXX (removed) */ - + + debug_draw_redraw_area(ar); + + /* would be nicer to have as a region callback, but + can't get the right context then? -nicholas */ + ED_paint_overlay_draw(C, ar); if(rv3d->persp==RV3D_CAMOB) drawviewborder(scene, ar, v3d); diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 5ca8843b658..c8a24acd582 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -471,7 +471,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) PointerRNA meshptr; RNA_pointer_create(&ob->id, &RNA_Mesh, ob->data, &meshptr); - uiItemR(layout, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY, "", 0); + uiItemR(layout, &meshptr, "ptex_edit_mode", UI_ITEM_R_ICON_ONLY, "", 0); } else { char *str_menu; diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index a4f661e9511..40bd5792e4f 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -140,7 +140,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) if(!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) do_glob_undo= 1; } - else if(obact && obact->mode & OB_MODE_SCULPT) { + else if(obact && obact->mode & (OB_MODE_SCULPT|OB_MODE_VERTEX_PAINT)) { if(!ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname) && undoname) do_glob_undo= 1; } |