diff options
Diffstat (limited to 'source/blender/editors')
26 files changed, 1841 insertions, 1104 deletions
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 764efb4ef0c..aae79e9a1de 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -28,18 +28,28 @@ #ifndef ED_SCULPT_H #define ED_SCULPT_H +struct ARegion; struct bContext; +struct Object; +struct RegionView3D; 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); void ED_keymap_paint(struct wmKeyConfig *keyconf); -/* paint_image.c */ -void undo_imagepaint_step(int step); -void undo_imagepaint_clear(void); +/* paint_undo.c */ +#define UNDO_PAINT_IMAGE 0 +#define UNDO_PAINT_MESH 1 + +void ED_undo_paint_step(struct bContext *C, int type, int step); +void ED_undo_paint_free(void); #endif diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 0e7f55bef8d..13ef235d8fe 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -30,6 +30,7 @@ /* ********* exports for space_view3d/ module ********** */ struct ARegion; +struct BoundBox; struct View3D; struct RegionView3D; struct ViewContext; @@ -44,6 +45,7 @@ struct ImBuf; struct Scene; struct bContext; struct Main; +struct rcti; /* for derivedmesh drawing callbacks, for view3d_select, .... */ typedef struct ViewContext { @@ -80,6 +82,8 @@ void request_depth_update(struct RegionView3D *rv3d); /* Projection */ #define IS_CLIPPED 12000 +void view3d_calculate_clipping(struct BoundBox *bb, float planes[4][4], struct bglMats *mats, struct rcti *rect); + void project_short(struct ARegion *ar, float *vec, short *adr); void project_short_noclip(struct ARegion *ar, float *vec, short *adr); @@ -131,7 +135,7 @@ short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigne void view3d_set_viewcontext(struct bContext *C, struct ViewContext *vc); void view3d_operator_needs_opengl(const struct bContext *C); void view3d_get_view_aligned_coordinate(struct ViewContext *vc, float *fp, short mval[2]); -void view3d_get_transformation(struct ViewContext *vc, struct Object *ob, struct bglMats *mats); +void view3d_get_transformation(struct ARegion *ar, struct RegionView3D *rv3d, struct Object *ob, struct bglMats *mats); /* XXX should move to arithb.c */ int edge_inside_circle(short centx, short centy, short rad, short x1, short y1, short x2, short y2); diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index d034fe4a783..d7df018acd7 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -99,15 +99,17 @@ void object_facesel_flush_dm(Object *ob) int totface; int i; - - if(me==NULL || dm==NULL || !CustomData_has_layer( &dm->faceData, CD_ORIGINDEX)) + if(me==NULL || dm==NULL) + return; + + index_array = dm->getFaceDataArray(dm, CD_ORIGINDEX); + + if(!index_array) return; faces = dm->getFaceArray(dm); totface = dm->getNumFaces(dm); - index_array = dm->getFaceDataArray(dm, CD_ORIGINDEX); - mf= faces; for (i= 0; i<totface; i++, mf++) { /* loop over derived mesh faces */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index c87210d6070..848855da1b0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -143,6 +143,8 @@ void OBJECT_OT_modifier_convert(struct wmOperatorType *ot); void OBJECT_OT_modifier_copy(struct wmOperatorType *ot); void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot); void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot); +void OBJECT_OT_multires_save_external(struct wmOperatorType *ot); +void OBJECT_OT_multires_pack_external(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 14c342d4ecd..a1e70c011fe 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -44,6 +44,7 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_util.h" #include "BKE_action.h" #include "BKE_curve.h" @@ -187,6 +188,12 @@ int ED_object_modifier_remove(ReportList *reports, Scene *scene, Object *ob, Mod else if(md->type == eModifierType_Smoke) { ob->dt = OB_TEXTURE; } + else if(md->type == eModifierType_Multires) { + Mesh *me= ob->data; + + CustomData_external_remove(&me->fdata, &me->id, CD_MDISPS, me->totface); + CustomData_free_layer_active(&me->fdata, CD_MDISPS, me->totface); + } BLI_remlink(&ob->modifiers, md); modifier_free(md); @@ -414,7 +421,7 @@ static int modifier_apply_obdata(ReportList *reports, Scene *scene, Object *ob, BKE_report(reports, RPT_INFO, "Applied modifier only changed CV points, not tesselated/bevel vertices"); - if (!(md->mode&eModifierMode_Realtime) || (mti->isDisabled && mti->isDisabled(md))) { + if (!(md->mode&eModifierMode_Realtime) || (mti->isDisabled && mti->isDisabled(md, 0))) { BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); return 0; } @@ -745,6 +752,13 @@ void OBJECT_OT_modifier_copy(wmOperatorType *ot) /************* multires delete higher levels operator ****************/ +static int multires_poll(bContext *C) +{ + PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); + ID *id= ptr.id.data; + return (ptr.data && id && !id->lib); +} + static int multires_higher_levels_delete_exec(bContext *C, wmOperator *op) { PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); @@ -763,8 +777,8 @@ void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot) { ot->name= "Delete Higher Levels"; ot->idname= "OBJECT_OT_multires_higher_levels_delete"; - ot->poll= ED_operator_object_active_editable; + ot->poll= multires_poll; ot->exec= multires_higher_levels_delete_exec; /* flags */ @@ -779,27 +793,113 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) Object *ob= ptr.id.data; MultiresModifierData *mmd= ptr.data; - multiresModifier_subdivide(mmd, ob, 1, 0, mmd->simple); + multiresModifier_subdivide(mmd, ob, 0, mmd->simple); + + 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_subdivide_poll(bContext *C) -{ - PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); - ID *id= ptr.id.data; - return (ptr.data && id && !id->lib); -} - void OBJECT_OT_multires_subdivide(wmOperatorType *ot) { ot->name= "Multires Subdivide"; ot->description= "Add a new level of subdivision."; ot->idname= "OBJECT_OT_multires_subdivide"; + ot->poll= multires_poll; ot->exec= multires_subdivide_exec; - ot->poll= multires_subdivide_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; +} + +/****************** multires save external operator *********************/ + +static int multires_save_external_exec(bContext *C, wmOperator *op) +{ + PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); + Object *ob= ptr.id.data; + Mesh *me= (ob)? ob->data: op->customdata; + char path[FILE_MAX]; + + if(CustomData_external_test(&me->fdata, CD_MDISPS)) + return OPERATOR_CANCELLED; + + RNA_string_get(op->ptr, "path", path); + if(G.save_over) + BLI_makestringcode(G.sce, path); /* make relative */ + + CustomData_external_add(&me->fdata, &me->id, CD_MDISPS, me->totface, path); + CustomData_external_write(&me->fdata, &me->id, CD_MASK_MESH, me->totface, 0); + + return OPERATOR_FINISHED; +} + +static int multires_save_external_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); + Object *ob= ptr.id.data; + Mesh *me= ob->data; + char path[FILE_MAX]; + + if(CustomData_external_test(&me->fdata, CD_MDISPS)) + return OPERATOR_CANCELLED; + + if(RNA_property_is_set(op->ptr, "path")) + return multires_save_external_exec(C, op); + + op->customdata= me; + + BLI_snprintf(path, sizeof(path), "//%s.btx", me->id.name+2); + RNA_string_set(op->ptr, "path", path); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +void OBJECT_OT_multires_save_external(wmOperatorType *ot) +{ + ot->name= "Multires Save External"; + ot->description= "Save displacements to an external file."; + ot->idname= "OBJECT_OT_multires_save_external"; + + ot->poll= multires_poll; + ot->exec= multires_save_external_exec; + ot->invoke= multires_save_external_invoke; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + WM_operator_properties_filesel(ot, FOLDERFILE|BTXFILE, FILE_SPECIAL); +} + +/****************** multires pack operator *********************/ + +static int multires_pack_external_exec(bContext *C, wmOperator *op) +{ + PointerRNA ptr= CTX_data_pointer_get_type(C, "modifier", &RNA_MultiresModifier); + Object *ob= ptr.id.data; + Mesh *me= ob->data; + + if(!CustomData_external_test(&me->fdata, CD_MDISPS)) + return OPERATOR_CANCELLED; + + // XXX don't remove.. + CustomData_external_remove(&me->fdata, &me->id, CD_MDISPS, me->totface); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_multires_pack_external(wmOperatorType *ot) +{ + ot->name= "Multires Pack External"; + ot->description= "Pack displacements from an external file."; + ot->idname= "OBJECT_OT_multires_pack_external"; + + ot->poll= multires_poll; + ot->exec= multires_pack_external_exec; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 66a03d4aec5..8def741ed2b 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -136,6 +136,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_modifier_copy); WM_operatortype_append(OBJECT_OT_multires_subdivide); WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete); + WM_operatortype_append(OBJECT_OT_multires_save_external); + WM_operatortype_append(OBJECT_OT_multires_pack_external); WM_operatortype_append(OBJECT_OT_meshdeform_bind); WM_operatortype_append(OBJECT_OT_explode_refresh); @@ -227,6 +229,7 @@ void ED_keymap_object(wmKeyConfig *keyconf) { wmKeyMap *keymap; wmKeyMapItem *kmi; + int i; /* Objects, Regardless of Mode -------------------------------------------------- */ keymap= WM_keymap_find(keyconf, "Object Non-modal", 0, 0); @@ -314,16 +317,10 @@ void ED_keymap_object(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "GROUP_OT_objects_add_active", GKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0); WM_keymap_verify_item(keymap, "GROUP_OT_objects_remove_active", GKEY, KM_PRESS, KM_SHIFT|KM_ALT, 0); - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subsurf_set", ONEKEY, KM_PRESS, KM_CTRL, 0); - RNA_int_set(kmi->ptr, "level", 1); - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subsurf_set", TWOKEY, KM_PRESS, KM_CTRL, 0); - RNA_int_set(kmi->ptr, "level", 2); - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subsurf_set", THREEKEY, KM_PRESS, KM_CTRL, 0); - RNA_int_set(kmi->ptr, "level", 3); - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subsurf_set", FOURKEY, KM_PRESS, KM_CTRL, 0); - RNA_int_set(kmi->ptr, "level", 4); - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subsurf_set", FIVEKEY, KM_PRESS, KM_CTRL, 0); - RNA_int_set(kmi->ptr, "level", 5); + for(i=1; i<=5; i++) { + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0); + RNA_int_set(kmi->ptr, "level", i); + } /* Lattice -------------------------------------------------------------------- */ keymap= WM_keymap_find(keyconf, "Lattice", 0, 0); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 7c6b3a5ee26..ac986ba7df6 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -362,7 +362,7 @@ static void PE_set_view3d_data(bContext *C, PEData *data) PE_set_data(C, data); view3d_set_viewcontext(C, &data->vc); - view3d_get_transformation(&data->vc, data->ob, &data->mats); + view3d_get_transformation(data->vc.ar, data->vc.rv3d, data->ob, &data->mats); if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT)) view3d_validate_backbuf(&data->vc); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 55f7a6a23b9..1f0d158ece9 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -86,6 +86,7 @@ #include "ED_image.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "ED_view3d.h" #include "WM_api.h" @@ -113,8 +114,6 @@ #define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) #define IMAPAINT_TILE_NUMBER(size) (((size)+IMAPAINT_TILE_SIZE-1) >> IMAPAINT_TILE_BITS) -#define MAXUNDONAME 64 - static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint); @@ -204,7 +203,7 @@ typedef struct ProjPaintImage { Image *ima; ImBuf *ibuf; ImagePaintPartialRedraw *partRedrawRect; - struct UndoTile **undoRect; /* only used to build undo tiles after painting */ + void **undoRect; /* only used to build undo tiles after painting */ int touch; } ProjPaintImage; @@ -332,32 +331,20 @@ typedef struct ProjPixelClone { /* Finish projection painting structs */ +typedef struct UndoImageTile { + struct UndoImageTile *next, *prev; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ -typedef struct UndoTile { - struct UndoTile *next, *prev; - ID id; void *rect; int x, y; -} UndoTile; - -typedef struct UndoElem { - struct UndoElem *next, *prev; - char name[MAXUNDONAME]; - uintptr_t undosize; +} UndoImageTile; - ImBuf *ibuf; - ListBase tiles; -} UndoElem; - -static ListBase undobase = {NULL, NULL}; -static UndoElem *curundo = NULL; static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; /* UNDO */ -/* internal functions */ - -static void undo_copy_tile(UndoTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) +static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) { /* copy or swap contents of tile->rect and region in ibuf->rect */ IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x*IMAPAINT_TILE_SIZE, @@ -374,49 +361,52 @@ static void undo_copy_tile(UndoTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int rest tile->y*IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); } -static UndoTile *undo_init_tile(ID *id, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) +static void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) { - UndoTile *tile; + ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; int allocsize; + + for(tile=lb->first; tile; tile=tile->next) + if(tile->x == x_tile && tile->y == y_tile && strcmp(tile->idname, ima->id.name)==0) + return tile->rect; if (*tmpibuf==NULL) *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat|IB_rect, 0); - tile= MEM_callocN(sizeof(UndoTile), "ImaUndoTile"); - tile->id= *id; + tile= MEM_callocN(sizeof(UndoImageTile), "UndoImageTile"); + strcpy(tile->idname, ima->id.name); tile->x= x_tile; tile->y= y_tile; allocsize= IMAPAINT_TILE_SIZE*IMAPAINT_TILE_SIZE*4; allocsize *= (ibuf->rect_float)? sizeof(float): sizeof(char); - tile->rect= MEM_mapallocN(allocsize, "ImaUndoRect"); + tile->rect= MEM_mapallocN(allocsize, "UndeImageTile.rect"); undo_copy_tile(tile, *tmpibuf, ibuf, 0); - curundo->undosize += allocsize; + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); - BLI_addtail(&curundo->tiles, tile); + BLI_addtail(lb, tile); - return tile; + return tile->rect; } -static void undo_restore(UndoElem *undo) +static void image_undo_restore(bContext *C, ListBase *lb) { + Main *bmain= CTX_data_main(C); Image *ima = NULL; ImBuf *ibuf, *tmpibuf; - UndoTile *tile; - - if(!undo) - return; + UndoImageTile *tile; tmpibuf= IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat|IB_rect, 0); - for(tile=undo->tiles.first; tile; tile=tile->next) { + for(tile=lb->first; tile; tile=tile->next) { /* find image based on name, pointer becomes invalid with global undo */ - if(ima && strcmp(tile->id.name, ima->id.name)==0); + if(ima && strcmp(tile->idname, ima->id.name)==0); else { - for(ima=G.main->image.first; ima; ima=ima->id.next) - if(strcmp(tile->id.name, ima->id.name)==0) + for(ima=bmain->image.first; ima; ima=ima->id.next) + if(strcmp(tile->idname, ima->id.name)==0) break; } @@ -435,117 +425,12 @@ static void undo_restore(UndoElem *undo) IMB_freeImBuf(tmpibuf); } -static void undo_free(UndoElem *undo) +static void image_undo_free(ListBase *lb) { - UndoTile *tile; + UndoImageTile *tile; - for(tile=undo->tiles.first; tile; tile=tile->next) + for(tile=lb->first; tile; tile=tile->next) MEM_freeN(tile->rect); - BLI_freelistN(&undo->tiles); -} - -static void undo_imagepaint_push_begin(char *name) -{ - UndoElem *uel; - int nr; - - /* Undo push is split up in begin and end, the reason is that as painting - * happens more tiles are added to the list, and at the very end we know - * how much memory the undo used to remove old undo elements */ - - /* remove all undos after (also when curundo==NULL) */ - while(undobase.last != curundo) { - uel= undobase.last; - undo_free(uel); - BLI_freelinkN(&undobase, uel); - } - - /* make new */ - curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file"); - BLI_addtail(&undobase, uel); - - /* name can be a dynamic string */ - strncpy(uel->name, name, MAXUNDONAME-1); - - /* limit amount to the maximum amount*/ - nr= 0; - uel= undobase.last; - while(uel) { - nr++; - if(nr==U.undosteps) break; - uel= uel->prev; - } - if(uel) { - while(undobase.first!=uel) { - UndoElem *first= undobase.first; - undo_free(first); - BLI_freelinkN(&undobase, first); - } - } -} - -static void undo_imagepaint_push_end() -{ - UndoElem *uel; - uintptr_t totmem, maxmem; - - if(U.undomemory != 0) { - /* limit to maximum memory (afterwards, we can't know in advance) */ - totmem= 0; - maxmem= ((uintptr_t)U.undomemory)*1024*1024; - - uel= undobase.last; - while(uel) { - totmem+= uel->undosize; - if(totmem>maxmem) break; - uel= uel->prev; - } - - if(uel) { - while(undobase.first!=uel) { - UndoElem *first= undobase.first; - undo_free(first); - BLI_freelinkN(&undobase, first); - } - } - } -} - -void undo_imagepaint_step(int step) -{ - UndoElem *undo; - - if(step==1) { - if(curundo==NULL); - else { - if(G.f & G_DEBUG) printf("undo %s\n", curundo->name); - undo_restore(curundo); - curundo= curundo->prev; - } - } - else if(step==-1) { - if((curundo!=NULL && curundo->next==NULL) || undobase.first==NULL); - else { - undo= (curundo && curundo->next)? curundo->next: undobase.first; - undo_restore(undo); - curundo= undo; - if(G.f & G_DEBUG) printf("redo %s\n", undo->name); - } - } -} - -void undo_imagepaint_clear(void) -{ - UndoElem *uel; - - uel= undobase.first; - while(uel) { - undo_free(uel); - uel= uel->next; - } - - BLI_freelistN(&undobase); - curundo= NULL; } /* fast projection bucket array lookup, use the safe version for bound checking */ @@ -3315,7 +3200,7 @@ static void project_paint_end(ProjPaintState *ps) ProjPixel *projPixel; ImBuf *tmpibuf = NULL, *tmpibuf_float = NULL; LinkNode *pixel_node; - UndoTile *tile; + void *tilerect; MemArena *arena = ps->arena_mt[0]; /* threaded arena re-used for non threaded case */ int bucket_tot = (ps->buckets_x * ps->buckets_y); /* we could get an X/Y but easier to loop through all possible buckets */ @@ -3331,8 +3216,8 @@ static void project_paint_end(ProjPaintState *ps) int last_tile_width=0; for(a=0, last_projIma=ps->projImages; a < ps->image_tot; a++, last_projIma++) { - int size = sizeof(UndoTile **) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->y); - last_projIma->undoRect = (UndoTile **) BLI_memarena_alloc(arena, size); + int size = sizeof(void **) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->y); + last_projIma->undoRect = (void **) BLI_memarena_alloc(arena, size); memset(last_projIma->undoRect, 0, size); last_projIma->ibuf->userflags |= IB_BITMAPDIRTY; } @@ -3372,21 +3257,21 @@ static void project_paint_end(ProjPaintState *ps) if (last_projIma->undoRect[tile_index]==NULL) { /* add the undo tile from the modified image, then write the original colors back into it */ - tile = last_projIma->undoRect[tile_index] = undo_init_tile(&last_projIma->ima->id, last_projIma->ibuf, is_float ? (&tmpibuf_float):(&tmpibuf) , x_tile, y_tile); + tilerect = last_projIma->undoRect[tile_index] = image_undo_push_tile(last_projIma->ima, last_projIma->ibuf, is_float ? (&tmpibuf_float):(&tmpibuf) , x_tile, y_tile); } else { - tile = last_projIma->undoRect[tile_index]; + tilerect = last_projIma->undoRect[tile_index]; } /* This is a BIT ODD, but overwrite the undo tiles image info with this pixels original color * because allocating the tiles allong the way slows down painting */ if (is_float) { - float *rgba_fp = (float *)tile->rect + (((projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE)) * 4; + float *rgba_fp = (float *)tilerect + (((projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE)) * 4; QUATCOPY(rgba_fp, projPixel->origColor.f); } else { - ((unsigned int *)tile->rect)[ (projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE ] = projPixel->origColor.uint; + ((unsigned int *)tilerect)[ (projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE ] = projPixel->origColor.uint; } } } @@ -3957,7 +3842,6 @@ static void imapaint_clear_partial_redraw() static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) { ImBuf *tmpibuf = NULL; - UndoTile *tile; int srcx= 0, srcy= 0, origx; IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); @@ -3984,17 +3868,9 @@ static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, origx = (x >> IMAPAINT_TILE_BITS); y = (y >> IMAPAINT_TILE_BITS); - for (; y <= h; y++) { - for (x=origx; x <= w; x++) { - for(tile=curundo->tiles.first; tile; tile=tile->next) - if(tile->x == x && tile->y == y && strcmp(tile->id.name, ima->id.name)==0) - break; - - if(!tile) { - undo_init_tile(&ima->id, ibuf, &tmpibuf, x, y); - } - } - } + for (; y <= h; y++) + for (x=origx; x <= w; x++) + image_undo_push_tile(ima, ibuf, &tmpibuf, x, y); ibuf->userflags |= IB_BITMAPDIRTY; @@ -4592,7 +4468,8 @@ static int texture_paint_init(bContext *C, wmOperator *op) } settings->imapaint.flag |= IMAGEPAINT_DRAWING; - undo_imagepaint_push_begin("Image Paint"); + undo_paint_push_begin(UNDO_PAINT_IMAGE, "Image Paint", + image_undo_restore, image_undo_free); /* create painter */ pop->painter= brush_painter_new(pop->s.brush); @@ -4656,7 +4533,7 @@ static void paint_exit(bContext *C, wmOperator *op) } paint_redraw(C, &pop->s, 1); - undo_imagepaint_push_end(); + undo_paint_push_end(UNDO_PAINT_IMAGE); if(pop->s.warnmultifile) BKE_reportf(op->reports, RPT_WARNING, "Image requires 4 color channels to paint: %s", pop->s.warnmultifile); @@ -5255,3 +5132,4 @@ int facemask_paint_poll(bContext *C) { return paint_facesel_test(CTX_data_active_object(C)); } + diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f86e1077ef3..108be50267d 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -41,13 +41,16 @@ struct wmOperator; struct wmOperatorType; struct ARegion; struct VPaint; +struct ListBase; /* paint_stroke.c */ +typedef int (*StrokeGetLocation)(struct bContext *C, struct 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); -struct PaintStroke *paint_stroke_new(struct bContext *C, StrokeTestStart test_start, +struct PaintStroke *paint_stroke_new(struct bContext *C, + StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeDone done); int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event); int paint_stroke_exec(struct bContext *C, struct wmOperator *op); @@ -100,5 +103,14 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot); int facemask_paint_poll(struct bContext *C); +/* paint_undo.c */ +typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); +typedef void (*UndoFreeCb)(struct ListBase *lb); + +void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free); +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); + #endif /* ED_PAINT_INTERN_H */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 92812c2ab08..ae694ed863b 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -179,6 +179,7 @@ static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path) void ED_keymap_paint(wmKeyConfig *keyconf) { wmKeyMap *keymap; + int i; /* Sculpt mode */ keymap= WM_keymap_find(keyconf, "Sculpt", 0, 0); @@ -193,6 +194,9 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_paint_brush_switch(keymap, "tool_settings.sculpt.active_brush_index"); + for(i=1; i<=5; i++) + RNA_int_set(WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0)->ptr, "level", i); + /* Vertex Paint mode */ keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0); keymap->poll= vertex_paint_poll; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 8e795c5542e..e26b1945e7f 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -73,6 +73,7 @@ typedef struct PaintStroke { passes over the mesh */ int stroke_started; + StrokeGetLocation get_location; StrokeTestStart test_start; StrokeUpdateStep update_step; StrokeDone done; @@ -100,7 +101,11 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata static void paint_draw_cursor(bContext *C, int x, int y, void *customdata) { - Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C))); + Paint *paint = paint_get_active(CTX_data_scene(C)); + Brush *brush = paint_brush(paint); + + if(!(paint->flags & PAINT_SHOW_BRUSH)) + return; glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); glEnable(GL_LINE_SMOOTH); @@ -118,13 +123,14 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *customdata) static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2]) { PointerRNA itemptr; - float cur_depth, pressure = 1; - float center[3]; + float pressure = 1; + float center[3] = {0, 0, 0}; int flip= event->shift?1:0; PaintStroke *stroke = op->customdata; - cur_depth = read_cached_depth(&stroke->vc, mouse[0], mouse[1]); - view3d_unproject(&stroke->mats, center, mouse[0], mouse[1], cur_depth); + /* XXX: can remove the if statement once all modes have this */ + if(stroke->get_location) + stroke->get_location(C, stroke, center, mouse); /* Tablet */ if(event->custom == EVT_DATA_TABLET) { @@ -211,15 +217,19 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const /**** Public API ****/ -PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start, - StrokeUpdateStep update_step, StrokeDone done) +PaintStroke *paint_stroke_new(bContext *C, + StrokeGetLocation get_location, + StrokeTestStart test_start, + StrokeUpdateStep update_step, + 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, stroke->vc.obact, &stroke->mats); + view3d_get_transformation(stroke->vc.ar, stroke->vc.rv3d, stroke->vc.obact, &stroke->mats); + stroke->get_location = get_location; stroke->test_start = test_start; stroke->update_step = update_step; stroke->done = done; @@ -229,12 +239,9 @@ PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start, int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) { - ARegion *ar = CTX_wm_region(C); PaintStroke *stroke = op->customdata; float mouse[2]; - - if(event->type == TIMER && (event->customdata != stroke->timer)) - return OPERATOR_RUNNING_MODAL; + int first= 0; if(!stroke->stroke_started) { stroke->last_mouse_position[0] = event->x; @@ -249,26 +256,13 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); } - ED_region_tag_redraw(ar); - } - - if(stroke->stroke_started) { - if(paint_smooth_stroke(stroke, mouse, event)) { - if(paint_space_stroke_enabled(stroke->brush)) { - if(!paint_space_stroke(C, op, event, mouse)) - ED_region_tag_redraw(ar); - } - else - paint_brush_stroke_add_step(C, op, event, mouse); - } - else - ED_region_tag_redraw(ar); + first= 1; + //ED_region_tag_redraw(ar); } - /* TODO: fix hardcoded event here */ + /* TODO: fix hardcoded events here */ if(event->type == LEFTMOUSE && event->val == KM_RELEASE) { - /* Exit stroke, free data */ - + /* exit stroke, free data */ if(stroke->smooth_stroke_cursor) WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor); @@ -279,8 +273,22 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) MEM_freeN(stroke); return OPERATOR_FINISHED; } - else - return OPERATOR_RUNNING_MODAL; + else if(first || event->type == 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)) { + if(!paint_space_stroke(C, op, event, mouse)) + ;//ED_region_tag_redraw(ar); + } + else + paint_brush_stroke_add_step(C, op, event, mouse); + } + else + ;//ED_region_tag_redraw(ar); + } + } + + return OPERATOR_RUNNING_MODAL; } int paint_stroke_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c new file mode 100644 index 00000000000..05f2b565e82 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -0,0 +1,235 @@ +/** + * $Id$ + * + * Undo system for painting and sculpting. + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_userdef_types.h" + +#include "BLI_listbase.h" + +#include "BKE_context.h" +#include "BKE_global.h" + +#include "ED_sculpt.h" + +#include "paint_intern.h" + +#define MAXUNDONAME 64 + +typedef struct UndoElem { + struct UndoElem *next, *prev; + char name[MAXUNDONAME]; + uintptr_t undosize; + + ListBase elems; + + UndoRestoreCb restore; + UndoFreeCb free; +} UndoElem; + +typedef struct UndoStack { + int type; + ListBase elems; + UndoElem *current; +} UndoStack; + +static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL}; +static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL}; + +/* Generic */ + +static void undo_restore(bContext *C, UndoStack *stack, UndoElem *uel) +{ + if(uel && uel->restore) + uel->restore(C, &uel->elems); +} + +static void undo_elem_free(UndoStack *stack, UndoElem *uel) +{ + if(uel && uel->free) { + uel->free(&uel->elems); + BLI_freelistN(&uel->elems); + } +} + +static void undo_stack_push_begin(UndoStack *stack, char *name, UndoRestoreCb restore, UndoFreeCb free) +{ + UndoElem *uel; + int nr; + + /* Undo push is split up in begin and end, the reason is that as painting + * happens more tiles/nodes are added to the list, and at the very end we + * know how much memory the undo used to remove old undo elements */ + + /* remove all undos after (also when stack->current==NULL) */ + while(stack->elems.last != stack->current) { + uel= stack->elems.last; + undo_elem_free(stack, uel); + BLI_freelinkN(&stack->elems, uel); + } + + /* make new */ + stack->current= uel= MEM_callocN(sizeof(UndoElem), "undo file"); + uel->restore= restore; + uel->free= free; + BLI_addtail(&stack->elems, uel); + + /* name can be a dynamic string */ + strncpy(uel->name, name, MAXUNDONAME-1); + + /* limit amount to the maximum amount*/ + nr= 0; + uel= stack->elems.last; + while(uel) { + nr++; + if(nr==U.undosteps) break; + uel= uel->prev; + } + if(uel) { + while(stack->elems.first!=uel) { + UndoElem *first= stack->elems.first; + undo_elem_free(stack, first); + BLI_freelinkN(&stack->elems, first); + } + } +} + +static void undo_stack_push_end(UndoStack *stack) +{ + UndoElem *uel; + uintptr_t totmem, maxmem; + + if(U.undomemory != 0) { + /* limit to maximum memory (afterwards, we can't know in advance) */ + totmem= 0; + maxmem= ((uintptr_t)U.undomemory)*1024*1024; + + uel= stack->elems.last; + while(uel) { + totmem+= uel->undosize; + if(totmem>maxmem) break; + uel= uel->prev; + } + + if(uel) { + while(stack->elems.first!=uel) { + UndoElem *first= stack->elems.first; + undo_elem_free(stack, first); + BLI_freelinkN(&stack->elems, first); + } + } + } +} + +static void undo_stack_step(bContext *C, UndoStack *stack, int step) +{ + UndoElem *undo; + + if(step==1) { + if(stack->current==NULL); + else { + if(G.f & G_DEBUG) printf("undo %s\n", stack->current->name); + undo_restore(C, stack, stack->current); + stack->current= stack->current->prev; + } + } + else if(step==-1) { + if((stack->current!=NULL && stack->current->next==NULL) || stack->elems.first==NULL); + else { + undo= (stack->current && stack->current->next)? stack->current->next: stack->elems.first; + undo_restore(C, stack, undo); + stack->current= undo; + if(G.f & G_DEBUG) printf("redo %s\n", undo->name); + } + } +} + +static void undo_stack_free(UndoStack *stack) +{ + UndoElem *uel; + + for(uel=stack->elems.first; uel; uel=uel->next) + undo_elem_free(stack, uel); + + BLI_freelistN(&stack->elems); + stack->current= NULL; +} + +/* Exported Functions */ + +void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFreeCb free) +{ + if(type == UNDO_PAINT_IMAGE) + undo_stack_push_begin(&ImageUndoStack, name, restore, free); + else if(type == UNDO_PAINT_MESH) + undo_stack_push_begin(&MeshUndoStack, name, restore, free); +} + +ListBase *undo_paint_push_get_list(int type) +{ + if(type == UNDO_PAINT_IMAGE) { + if(ImageUndoStack.current) + return &ImageUndoStack.current->elems; + } + else if(type == UNDO_PAINT_MESH) { + if(MeshUndoStack.current) + return &MeshUndoStack.current->elems; + } + + return NULL; +} + +void undo_paint_push_count_alloc(int type, int size) +{ + if(type == UNDO_PAINT_IMAGE) + ImageUndoStack.current->undosize += size; + else if(type == UNDO_PAINT_MESH) + MeshUndoStack.current->undosize += size; +} + +void undo_paint_push_end(int type) +{ + if(type == UNDO_PAINT_IMAGE) + undo_stack_push_end(&ImageUndoStack); + else if(type == UNDO_PAINT_MESH) + undo_stack_push_end(&MeshUndoStack); +} + +void ED_undo_paint_step(bContext *C, int type, int step) +{ + if(type == UNDO_PAINT_IMAGE) + undo_stack_step(C, &ImageUndoStack, step); + else if(type == UNDO_PAINT_MESH) + undo_stack_step(C, &MeshUndoStack, step); +} + +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 496d15d793f..b968930128a 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -96,7 +96,7 @@ void imapaint_pick_uv(Scene *scene, Object *ob, Mesh *mesh, unsigned int faceind DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); int *index = dm->getFaceDataArray(dm, CD_ORIGINDEX); MTFace *tface = dm->getFaceDataArray(dm, CD_MTFACE), *tf; - int numfaces = dm->getNumFaces(dm), a; + int numfaces = dm->getNumFaces(dm), a, findex; float p[2], w[3], absw, minabsw; MFace mf; MVert mv[4]; @@ -106,7 +106,9 @@ void imapaint_pick_uv(Scene *scene, Object *ob, Mesh *mesh, unsigned int faceind /* test all faces in the derivedmesh with the original index of the picked face */ for(a = 0; a < numfaces; a++) { - if(index[a] == faceindex) { + findex= (index)? index[a]: a; + + if(findex == faceindex) { dm->getFace(dm, a, &mf); dm->getVert(dm, mf.v1, &mv[0]); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 2512ecbc248..7021d76b1c4 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1629,7 +1629,7 @@ static void wpaint_stroke_done(bContext *C, struct PaintStroke *stroke) static int wpaint_invoke(bContext *C, wmOperator *op, wmEvent *event) { - op->customdata = paint_stroke_new(C, wpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, wpaint_stroke_update_step, wpaint_stroke_done); @@ -1931,7 +1931,7 @@ static void vpaint_stroke_done(bContext *C, struct PaintStroke *stroke) static int vpaint_invoke(bContext *C, wmOperator *op, wmEvent *event) { - op->customdata = paint_stroke_new(C, vpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, vpaint_stroke_update_step, vpaint_stroke_done); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index f4006e63dc0..8b705ff5b5e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -35,6 +35,9 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_pbvh.h" +#include "BLI_threads.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" @@ -52,6 +55,7 @@ #include "DNA_color_types.h" #include "BKE_brush.h" +#include "BKE_cdderivedmesh.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_DerivedMesh.h" @@ -106,15 +110,6 @@ * */ -/* ActiveData stores an Index into the mvert array of Mesh, plus Fade, which - stores how far the vertex is from the brush center, scaled to the range [0,1]. */ -typedef struct ActiveData { - struct ActiveData *next, *prev; - unsigned int Index; - float Fade; - float dist; -} ActiveData; - typedef enum StrokeFlags { CLIP_X = 1, CLIP_Y = 2, @@ -133,7 +128,6 @@ typedef struct StrokeCache { int flag; float clip_tolerance[3]; float initial_mouse[2]; - float depth; /* Variants */ float radius; @@ -142,6 +136,7 @@ typedef struct StrokeCache { float flip; float pressure; float mouse[2]; + float bstrength; float tex_mouse[2]; /* The rest is temporary storage that isn't saved as a property */ @@ -150,32 +145,24 @@ typedef struct StrokeCache { bglMats *mats; - short (*orig_norms)[3]; /* Copy of the mesh vertices' normals */ + /* Clean this up! */ + ViewContext *vc; + Brush *brush; + float (*face_norms)[3]; /* Copy of the mesh faces' normals */ float rotation; /* Texture rotation (radians) for anchored and rake modes */ int pixel_radius, previous_pixel_radius; - ListBase grab_active_verts[8]; /* The same list of verts is used throught grab stroke */ + PBVHNode **grab_active_nodes[8]; /* The same list of nodes is used throught grab stroke */ + int grab_active_totnode[8]; + float grab_active_location[8][3]; float grab_delta[3], grab_delta_symmetry[3]; - float old_grab_location[3]; + float old_grab_location[3], orig_grab_location[3]; int symmetry; /* Symmetry index between 0 and 7 */ float view_normal[3], view_normal_symmetry[3]; int last_rake[2]; /* Last location of updating rake rotation */ + int original; } StrokeCache; -typedef struct RectNode { - struct RectNode *next, *prev; - rcti r; -} RectNode; - -/* Used to store to 2D screen coordinates of each vertex in the mesh. */ -typedef struct ProjVert { - short co[2]; - - /* Used to mark whether a vertex is inside a rough bounding box - containing the brush. */ - char inside; -} ProjVert; - /* ===== OPENGL ===== * * Simple functions to get data from the GL @@ -192,7 +179,7 @@ static void projectf(bglMats *mats, const float v[3], float p[2]) p[1]= uy; } -static void project(bglMats *mats, const float v[3], short p[2]) +/*XXX: static void project(bglMats *mats, const float v[3], short p[2]) { float f[2]; projectf(mats, v, f); @@ -200,6 +187,337 @@ static void project(bglMats *mats, const float v[3], short p[2]) 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) +{ + float bb_min[3], bb_max[3], pmat[4][4]; + int i, j, k; + + view3d_get_object_project_mat(rv3d, ob, pmat); + + BLI_pbvh_redraw_BB(ob->sculpt->tree, 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) +{ + BoundBox *bb = MEM_callocN(sizeof(BoundBox), "sculpt boundbox"); + bglMats mats; + int i; + rcti rect; + + 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); + + for(i = 0; i < 16; ++i) + ((float*)planes)[i] = -((float*)planes)[i]; + + MEM_freeN(bb); + + /* clear redraw flag from nodes */ + BLI_pbvh_update(ob->sculpt->tree, PBVH_UpdateRedraw, NULL); +} + +/************************** 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 */ +} SculptUndoNode; + +static void update_cb(PBVHNode *node, void *data) +{ + 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) { + /* regular mesh restore */ + if(ss->totvert != unode->maxvert) + continue; + + index= unode->index; + mvert= ss->mvert; + + 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) { + /* 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) { + if(ss->kb) sculpt_mesh_to_key(ss->ob, ss->kb); + if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob); + + /* 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->tree, NULL, NULL, update_cb, NULL); + BLI_pbvh_update(ss->tree, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL); + + if((mmd=sculpt_multires_active(ob))) + multires_mark_as_modified(ob); + } +} + +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); + } +} + +static SculptUndoNode *sculpt_undo_get_node(SculptSession *ss, 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; +} + +static 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(ss, 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->tree, node, &totvert, &allvert); + BLI_pbvh_node_get_grids(ss->tree, 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->tree, 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); + + return unode; +} + +static void sculpt_undo_push_begin(SculptSession *ss, char *name) +{ + undo_paint_push_begin(UNDO_PAINT_MESH, name, + sculpt_undo_restore, sculpt_undo_free); +} + +static void sculpt_undo_push_end(SculptSession *ss) +{ + 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; + } + } + + undo_paint_push_end(UNDO_PAINT_MESH); +} + +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); +} + +/************************ 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*ss->cache->radius; + copy_v3_v3(test->location, ss->cache->location); +} + +static int sculpt_brush_test(SculptBrushTest *test, float co[3]) +{ + float distsq, delta[3]; + + sub_v3_v3v3(delta, co, test->location); + distsq = INPR(delta, delta); + + if(distsq < test->radius_squared) { + test->dist = sqrt(distsq); + return 1; + } + + return 0; +} /* ===== Sculpting ===== * @@ -239,6 +557,169 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache) } } +/* 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]; +} + +/* Get a pixel from the texcache at (px, py) */ +static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) +{ + unsigned *p; + p = ss->texcache + py * ss->texcache_side + px; + return ((unsigned char*)(p))[0]; +} + +static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) +{ + int x, y, x2, y2; + const int tc_max = ss->texcache_side - 1; + float urat, vrat, uopp; + + if(u < 0) u = 0; + else if(u >= ss->texcache_side) u = tc_max; + if(v < 0) v = 0; + else if(v >= ss->texcache_side) v = tc_max; + + x = floor(u); + y = floor(v); + x2 = x + 1; + y2 = y + 1; + + if(x2 > ss->texcache_side) x2 = tc_max; + if(y2 > ss->texcache_side) y2 = tc_max; + + urat = u - x; + vrat = v - y; + uopp = 1 - urat; + + return ((get_texcache_pixel(ss, x, y) * uopp + + get_texcache_pixel(ss, x2, y) * urat) * (1 - vrat) + + (get_texcache_pixel(ss, x, y2) * uopp + + get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0; +} + +/* Return a multiplier for brush strength on a particular vertex. */ +static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len) +{ + MTex *tex = NULL; + float avg= 1; + + if(br->texact >= 0) + tex = br->mtex[br->texact]; + + if(!tex) { + avg= 1; + } + else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) { + float jnk; + + /* Get strength by feeding the vertex + location directly into a texture */ + externtex(tex, point, &avg, + &jnk, &jnk, &jnk, &jnk); + } + else if(ss->texcache) { + const float bsize= ss->cache->pixel_radius * 2; + const float rot= tex->rot + ss->cache->rotation; + int px, py; + float flip[3], point_2d[2]; + + /* If the active area is being applied for symmetry, flip it + across the symmetry axis in order to project it. This insures + that the brush texture will be oriented correctly. */ + copy_v3_v3(flip, point); + flip_coord(flip, flip, ss->cache->symmetry); + projectf(ss->cache->mats, flip, point_2d); + + /* For Tile and Drag modes, get the 2D screen coordinates of the + and scale them up or down to the texture size. */ + if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) { + const int sx= (const int)tex->size[0]; + const int sy= (const int)tex->size[1]; + + float fx= point_2d[0]; + float fy= point_2d[1]; + + float angle= atan2(fy, fx) - rot; + float flen= sqrtf(fx*fx + fy*fy); + + if(rot<0.001 && rot>-0.001) { + px= point_2d[0]; + py= point_2d[1]; + } else { + px= flen * cos(angle) + 2000; + py= flen * sin(angle) + 2000; + } + if(sx != 1) + px %= sx-1; + if(sy != 1) + py %= sy-1; + avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy); + } + else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) { + float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize; + float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize; + + float angle= atan2(fy, fx) - rot; + float flen= sqrtf(fx*fx + fy*fy); + + fx = flen * cos(angle) + 0.5; + fy = flen * sin(angle) + 0.5; + + avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side); + } + } + + avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ + + return avg; +} + +typedef struct { + Sculpt *sd; + SculptSession *ss; + float radius_squared; + ListBase *active_verts; + float area_normal[3]; +} 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; + + 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 t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared; +} + /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float val[3]) { @@ -255,12 +736,8 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va } } -static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], const short no[3]) +static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) { - float fno[3] = {no[0], no[1], no[2]}; - - normalize_v3(fno); - if((dot_v3v3(view_vec, fno)) > 0) { add_v3_v3v3(out, out, fno); } else { @@ -268,74 +745,119 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], cons } } -/* Currently only for the draw brush; finds average normal for all active - vertices */ -static void calc_area_normal(Sculpt *sd, SculptSession *ss, float out[3], const ListBase* active_verts) +/* For draw/layer/flatten; finds average normal for all active vertices */ +static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3], PBVHNode **nodes, int totnode) { - Brush *brush = paint_brush(&sd->paint); + PBVH *bvh= ss->tree; StrokeCache *cache = ss->cache; - ActiveData *node = active_verts->first; const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */ - float out_flip[3]; - float *out_dir = cache->view_normal_symmetry; - - out[0]=out[1]=out[2] = out_flip[0]=out_flip[1]=out_flip[2] = 0; + float out[3] = {0.0f, 0.0f, 0.0f}; + float out_flip[3] = {0.0f, 0.0f, 0.0f}; + float out_dir[3]; + int n; + + copy_v3_v3(out_dir, cache->view_normal_symmetry); + + /* threaded loop over nodes */ + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + SculptUndoNode *unode; + float fno[3]; + float nout[3] = {0.0f, 0.0f, 0.0f}; + float nout_flip[3] = {0.0f, 0.0f, 0.0f}; + + // XXX push instead of get for thread safety in draw + // brush .. lame, but also not harmful really + unode= sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + if(ss->cache->original) { + BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, unode->co[vd.i])) { + normal_short_to_float_v3(fno, unode->no[vd.i]); + add_norm_if(out_dir, nout, nout_flip, fno); + } + } + BLI_pbvh_vertex_iter_end; + } + else { + BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + if(vd.no) { + normal_short_to_float_v3(fno, vd.no); + add_norm_if(out_dir, nout, nout_flip, fno); + } + else + add_norm_if(out_dir, nout, nout_flip, vd.fno); + } + } + BLI_pbvh_vertex_iter_end; + } - if(brush->flag & BRUSH_ANCHORED) { - for(; node; node = node->next) - add_norm_if(out_dir, out, out_flip, cache->orig_norms[node->Index]); - } - else { - for(; node; node = node->next) - add_norm_if(out_dir, out, out_flip, ss->mvert[node->Index].no); + #pragma omp critical + { + /* we sum per node and add together later for threads */ + add_v3_v3v3(out, out, nout); + add_v3_v3v3(out_flip, out_flip, nout_flip); + } } if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) { - VECCOPY(out, out_flip); + copy_v3_v3(out, out_flip); } normalize_v3(out); - if(out_dir) { - out[0] = out_dir[0] * view + out[0] * (10-view); - out[1] = out_dir[1] * view + out[1] * (10-view); - out[2] = out_dir[2] * view + out[2] * (10-view); - } + out[0] = out_dir[0] * view + out[0] * (10-view); + out[1] = out_dir[1] * view + out[1] * (10-view); + out[2] = out_dir[2] * view + out[2] * (10-view); normalize_v3(out); + copy_v3_v3(area_normal, out); } -static void do_draw_brush(Sculpt *sd, SculptSession *ss, const ListBase* active_verts) +static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - float area_normal[3]; - ActiveData *node= active_verts->first; - float* buffer; - - calc_area_normal(sd, ss, area_normal, active_verts); - - buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; - - while(node){ - float *co= ss->mvert[node->Index].co; - - const float val[3]= {co[0]+area_normal[0]*ss->cache->radius*node->Fade*ss->cache->scale[0], - co[1]+area_normal[1]*ss->cache->radius*node->Fade*ss->cache->scale[1], - co[2]+area_normal[2]*ss->cache->radius*node->Fade*ss->cache->scale[2]}; - - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(sd, ss, &buffer[cur->element*3], val); - cur = cur->next; + Brush *brush = paint_brush(&sd->paint); + float offset[3], area_normal[3]; + float bstrength= ss->cache->bstrength; + int n; + + /* area normal */ + calc_area_normal(sd, ss, area_normal, nodes, totnode); + + /* offset with as much as possible factored in already */ + offset[0]= area_normal[0]*ss->cache->radius*ss->cache->scale[0]*bstrength; + offset[1]= area_normal[1]*ss->cache->radius*ss->cache->scale[1]*bstrength; + offset[2]= area_normal[2]*ss->cache->radius*ss->cache->scale[2]*bstrength; + + /* threaded loop over nodes */ + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + + sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + /* offset vertex */ + float fade = tex_strength(ss, brush, vd.co, test.dist); + float val[3]= {vd.co[0] + offset[0]*fade, + vd.co[1] + offset[1]*fade, + vd.co[2] + offset[2]*fade}; + + sculpt_clip(sd, ss, vd.co, val); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } + BLI_pbvh_vertex_iter_end; - sculpt_clip(sd, ss, co, val); - - node= node->next; + BLI_pbvh_node_mark_update(nodes[n]); } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); } /* For the smooth brush, uses the neighboring vertices around vert to calculate @@ -382,195 +904,340 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert) copy_v3_v3(avg, ss->mvert[vert].co); } -static void do_smooth_brush(Sculpt *s, SculptSession *ss, const ListBase* active_verts) +static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) { - ActiveData *node= active_verts->first; - float *buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; - int i; + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + PBVHVertexIter vd; + SculptBrushTest test; - for(i = 0; i < 2; ++i) { - while(node){ - float *co= ss->mvert[node->Index].co; + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, node, vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; float avg[3], val[3]; - neighbor_average(ss, avg, node->Index); - val[0] = co[0]+(avg[0]-co[0])*node->Fade; - val[1] = co[1]+(avg[1]-co[1])*node->Fade; - val[2] = co[2]+(avg[2]-co[2])*node->Fade; + neighbor_average(ss, avg, vd.vert_indices[vd.i]); + val[0] = vd.co[0]+(avg[0]-vd.co[0])*fade; + val[1] = vd.co[1]+(avg[1]-vd.co[1])*fade; + val[2] = vd.co[2]+(avg[2]-vd.co[2])*fade; - sculpt_clip(s, ss, co, val); - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(s, ss, &buffer[cur->element*3], val); - cur = cur->next; + sculpt_clip(sd, ss, vd.co, val); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BLI_pbvh_vertex_iter_end; +} + +static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node) +{ + Brush *brush = paint_brush(&sd->paint); + SculptBrushTest test; + DMGridData **griddata, *data; + DMGridAdjacency *gridadj, *adj; + float bstrength= ss->cache->bstrength; + float co[3], (*tmpgrid)[3]; + int v1, v2, v3, v4; + int *grid_indices, totgrid, gridsize, i, x, y; + + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_node_get_grids(ss->tree, node, &grid_indices, &totgrid, + NULL, &gridsize, &griddata, &gridadj); + + #pragma omp critical + tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid"); + + for(i = 0; i < totgrid; ++i) { + data = griddata[grid_indices[i]]; + adj = &gridadj[grid_indices[i]]; + + memset(tmpgrid, 0, sizeof(float)*3*gridsize*gridsize); + + /* average grid values */ + for(y = 0; y < gridsize-1; ++y) { + for(x = 0; x < gridsize-1; ++x) { + v1 = x + y*gridsize; + v2 = (x + 1) + y*gridsize; + v3 = (x + 1) + (y + 1)*gridsize; + v4 = x + (y + 1)*gridsize; + + cent_quad_v3(co, data[v1].co, data[v2].co, data[v3].co, data[v4].co); + mul_v3_fl(co, 0.25f); + + add_v3_v3(tmpgrid[v1], co); + add_v3_v3(tmpgrid[v2], co); + add_v3_v3(tmpgrid[v3], co); + add_v3_v3(tmpgrid[v4], co); + } + } + + /* blend with existing coordinates */ + for(y = 0; y < gridsize; ++y) { + for(x = 0; x < gridsize; ++x) { + if(x == 0 && adj->index[0] == -1) continue; + if(x == gridsize - 1 && adj->index[2] == -1) continue; + if(y == 0 && adj->index[3] == -1) continue; + if(y == gridsize - 1 && adj->index[1] == -1) continue; + + copy_v3_v3(co, data[x + y*gridsize].co); + + if(sculpt_brush_test(&test, co)) { + float fade = tex_strength(ss, brush, co, test.dist)*bstrength; + float avg[3], val[3]; + + copy_v3_v3(avg, tmpgrid[x + y*gridsize]); + if(x == 0 || x == gridsize - 1) + mul_v3_fl(avg, 2.0f); + if(y == 0 || y == gridsize - 1) + mul_v3_fl(avg, 2.0f); + + val[0] = co[0]+(avg[0]-co[0])*fade; + val[1] = co[1]+(avg[1]-co[1])*fade; + val[2] = co[2]+(avg[2]-co[2])*fade; + + sculpt_clip(sd, ss, data[x + y*gridsize].co, val); } } - node= node->next; } } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); + + #pragma omp critical + MEM_freeN(tmpgrid); } -static void do_pinch_brush(Sculpt *s, SculptSession *ss, const ListBase* active_verts) +static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - ActiveData *node= active_verts->first; - float *buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; + int iteration, n; + + for(iteration = 0; iteration < 2; ++iteration) { + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + sculpt_undo_push_node(ss, nodes[n]); + + if(ss->fmap) + do_mesh_smooth_brush(sd, ss, nodes[n]); + else + do_multires_smooth_brush(sd, ss, nodes[n]); + + BLI_pbvh_node_mark_update(nodes[n]); + } + + if(!ss->fmap) + multires_stitch_grids(ss->ob); + } +} - while(node) { - float *co= ss->mvert[node->Index].co; - const float val[3]= {co[0]+(ss->cache->location[0]-co[0])*node->Fade, - co[1]+(ss->cache->location[1]-co[1])*node->Fade, - co[2]+(ss->cache->location[2]-co[2])*node->Fade}; +static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) +{ + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + int n; - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(s, ss, &buffer[cur->element*3], val); - cur = cur->next; + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + + sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + float val[3]= {vd.co[0]+(test.location[0]-vd.co[0])*fade, + vd.co[1]+(test.location[1]-vd.co[1])*fade, + vd.co[2]+(test.location[2]-vd.co[2])*fade}; + + sculpt_clip(sd, ss, vd.co, val); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } + BLI_pbvh_vertex_iter_end; - sculpt_clip(s, ss, co, val); - node= node->next; + BLI_pbvh_node_mark_update(nodes[n]); } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); } -static void do_grab_brush(Sculpt *sd, SculptSession *ss) +static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - ActiveData *node= ss->cache->grab_active_verts[ss->cache->symmetry].first; - float add[3]; + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; float grab_delta[3]; - float *buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; + int n; copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - while(node) { - float *co= ss->mvert[node->Index].co; + + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*origco)[3]; - copy_v3_v3(add, grab_delta); - mul_v3_fl(add, node->Fade); - add_v3_v3v3(add, add, co); - - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(sd, ss, &buffer[cur->element*3], add); - cur = cur->next; + origco= sculpt_undo_push_node(ss, nodes[n])->co; + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, origco[vd.i])) { + float fade = tex_strength(ss, brush, origco[vd.i], test.dist)*bstrength; + float add[3]= {vd.co[0]+fade*grab_delta[0], + vd.co[1]+fade*grab_delta[1], + vd.co[2]+fade*grab_delta[2]}; + + sculpt_clip(sd, ss, vd.co, add); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } + BLI_pbvh_vertex_iter_end; - sculpt_clip(sd, ss, co, add); - - node= node->next; + BLI_pbvh_node_mark_update(nodes[n]); } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); - } -static void do_layer_brush(Sculpt *sd, SculptSession *ss, const ListBase *active_verts) +static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - float area_normal[3]; - ActiveData *node= active_verts->first; - float *buffer; + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + float area_normal[3], offset[3]; float lim= ss->cache->radius / 4; + int n; + + /* XXX not working yet for multires */ + if(!ss->mvert) + return; if(ss->cache->flip) lim = -lim; - calc_area_normal(sd, ss, area_normal, active_verts); + calc_area_normal(sd, ss, area_normal, nodes, totnode); - buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; - while(node){ - float *disp= &ss->layer_disps[node->Index]; - float *co= ss->mvert[node->Index].co; - float val[3]; - - *disp+= node->Fade; - - /* Don't let the displacement go past the limit */ - if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim)) - *disp = lim; + offset[0]= ss->cache->scale[0]*area_normal[0]; + offset[1]= ss->cache->scale[1]*area_normal[1]; + offset[2]= ss->cache->scale[2]*area_normal[2]; + + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + float (*origco)[3]; - val[0] = ss->mesh_co_orig[node->Index][0]+area_normal[0] * *disp*ss->cache->scale[0]; - val[1] = ss->mesh_co_orig[node->Index][1]+area_normal[1] * *disp*ss->cache->scale[1]; - val[2] = ss->mesh_co_orig[node->Index][2]+area_normal[2] * *disp*ss->cache->scale[2]; - - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(sd, ss, &buffer[cur->element*3], val); - cur = cur->next; + origco= sculpt_undo_push_node(ss, nodes[n])->co; + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + int index= vd.vert_indices[vd.i]; + float *disp= &ss->layer_disps[index]; + float val[3]; + + *disp+= fade; + + /* Don't let the displacement go past the limit */ + if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim)) + *disp = lim; + + if(ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + /* persistent base */ + val[0] = ss->layer_co[index][0] + (*disp)*offset[0]; + val[1] = ss->layer_co[index][1] + (*disp)*offset[1]; + val[2] = ss->layer_co[index][2] + (*disp)*offset[2]; + } + else { + val[0] = origco[vd.i][0] + (*disp)*offset[0]; + val[1] = origco[vd.i][1] + (*disp)*offset[1]; + val[2] = origco[vd.i][2] + (*disp)*offset[2]; + } + + sculpt_clip(sd, ss, vd.co, val); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } + BLI_pbvh_vertex_iter_end; - sculpt_clip(sd, ss, co, val); - - node= node->next; + BLI_pbvh_node_mark_update(nodes[n]); } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); } -static void do_inflate_brush(Sculpt *s, SculptSession *ss, const ListBase *active_verts) +static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode) { - ActiveData *node= active_verts->first; - float add[3]; - float *buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; - - while(node) { - float *co= ss->mvert[node->Index].co; - short *no= ss->mvert[node->Index].no; + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; + int n; - add[0]= no[0]/ 32767.0f; - add[1]= no[1]/ 32767.0f; - add[2]= no[2]/ 32767.0f; - mul_v3_fl(add, node->Fade * ss->cache->radius); - add[0]*= ss->cache->scale[0]; - add[1]*= ss->cache->scale[1]; - add[2]*= ss->cache->scale[2]; - add_v3_v3v3(add, add, co); + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(s, ss, &buffer[cur->element*3], add); - cur = cur->next; + sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + float add[3]; + + if(vd.fno) copy_v3_v3(add, vd.fno); + else normal_short_to_float_v3(add, vd.no); + + mul_v3_fl(add, fade * ss->cache->radius); + add[0]*= ss->cache->scale[0]; + add[1]*= ss->cache->scale[1]; + add[2]*= ss->cache->scale[2]; + add_v3_v3v3(add, add, vd.co); + + sculpt_clip(sd, ss, vd.co, add); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } } + BLI_pbvh_vertex_iter_end; - sculpt_clip(s, ss, co, add); - - node= node->next; + BLI_pbvh_node_mark_update(nodes[n]); } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); } -static void calc_flatten_center(SculptSession *ss, ActiveData *node, float co[3]) +static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float co[3]) { - ActiveData *outer[FLATTEN_SAMPLE_SIZE]; - int i; + float outer_dist[FLATTEN_SAMPLE_SIZE]; + float outer_co[FLATTEN_SAMPLE_SIZE][3]; + int i, n; - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) - outer[i] = node; + for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) { + zero_v3(outer_co[i]); + outer_dist[i]= -1.0f; + } + + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; + int j; - for(; node; node = node->next) { - for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) { - if(node->dist > outer[i]->dist) { - outer[i] = node; - break; + sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + for(j = 0; j < FLATTEN_SAMPLE_SIZE; ++j) { + if(test.dist > outer_dist[j]) { + copy_v3_v3(outer_co[j], vd.co); + outer_dist[j] = test.dist; + break; + } + } } } + BLI_pbvh_vertex_iter_end; + + BLI_pbvh_node_mark_update(nodes[n]); } co[0] = co[1] = co[2] = 0.0f; for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) - add_v3_v3v3(co, co, ss->mvert[outer[i]->Index].co); + if(outer_dist[i] >= 0.0f) + add_v3_v3v3(co, co, outer_co[i]); mul_v3_fl(co, 1.0f / FLATTEN_SAMPLE_SIZE); } @@ -602,16 +1269,17 @@ static int plane_point_side(float co[3], float plane_normal[3], float plane_cent return d <= 0.0f; } -static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, const ListBase *active_verts, int clay) +static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, int clay) { - ActiveData *node= active_verts->first; /* area_normal and cntr define the plane towards which vertices are squashed */ + Brush *brush = paint_brush(&sd->paint); + float bstrength= ss->cache->bstrength; float area_normal[3]; float cntr[3], cntr2[3], bstr = 0; - int flip = 0; - float *buffer; - calc_area_normal(sd, ss, area_normal, active_verts); - calc_flatten_center(ss, node, cntr); + int n, flip = 0; + + calc_area_normal(sd, ss, area_normal, nodes, totnode); + calc_flatten_center(sd, ss, nodes, totnode, cntr); if(clay) { bstr= brush_strength(sd, ss->cache); @@ -622,295 +1290,123 @@ static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, const ListBase flip = bstr < 0; } - buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->vertices ):0; - - while(node){ - float *co= ss->mvert[node->Index].co; - float intr[3], val[3]; + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + PBVHVertexIter vd; + SculptBrushTest test; - if(!clay || plane_point_side(co, area_normal, cntr2, flip)) { - /* Find the intersection between squash-plane and vertex (along the area normal) */ - point_plane_project(intr, co, area_normal, cntr); - - sub_v3_v3v3(val, intr, co); - - if(clay) { - if(bstr > FLT_EPSILON) - mul_v3_fl(val, node->Fade / bstr); - else - mul_v3_fl(val, node->Fade); - /* Clay displacement */ - val[0]+=area_normal[0] * ss->cache->scale[0]*node->Fade; - val[1]+=area_normal[1] * ss->cache->scale[1]*node->Fade; - val[2]+=area_normal[2] * ss->cache->scale[2]*node->Fade; - } - else - mul_v3_fl(val, fabs(node->Fade)); + sculpt_undo_push_node(ss, nodes[n]); + sculpt_brush_test_init(ss, &test); + + BLI_pbvh_vertex_iter_begin(ss->tree, nodes[n], vd, PBVH_ITER_UNIQUE) { + if(sculpt_brush_test(&test, vd.co)) { + float intr[3], val[3]; + + if(!clay || plane_point_side(vd.co, area_normal, cntr2, flip)) { + const float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength; + + /* Find the intersection between squash-plane and vertex (along the area normal) */ + point_plane_project(intr, vd.co, area_normal, cntr); + + sub_v3_v3v3(val, intr, vd.co); + + if(clay) { + if(bstr > FLT_EPSILON) + mul_v3_fl(val, fade / bstr); + else + mul_v3_fl(val, fade); + /* Clay displacement */ + val[0]+=area_normal[0] * ss->cache->scale[0]*fade; + val[1]+=area_normal[1] * ss->cache->scale[1]*fade; + val[2]+=area_normal[2] * ss->cache->scale[2]*fade; + } + else + mul_v3_fl(val, fabs(fade)); - add_v3_v3v3(val, val, co); + add_v3_v3v3(val, val, vd.co); - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[node->Index]; - while( cur != 0 && cur->element != -1 ) { - sculpt_clip(sd, ss, &buffer[cur->element*3], val); - cur = cur->next; + sculpt_clip(sd, ss, vd.co, val); + if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } - } - sculpt_clip(sd, ss, co, val); - - } - - node= node->next; - } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->vertices ); -} - -/* 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]; -} - -/* Get a pixel from the texcache at (px, py) */ -static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py) -{ - unsigned *p; - p = ss->texcache + py * ss->texcache_side + px; - return ((unsigned char*)(p))[0]; -} - -static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v) -{ - int x, y, x2, y2; - const int tc_max = ss->texcache_side - 1; - float urat, vrat, uopp; - - if(u < 0) u = 0; - else if(u >= ss->texcache_side) u = tc_max; - if(v < 0) v = 0; - else if(v >= ss->texcache_side) v = tc_max; - - x = floor(u); - y = floor(v); - x2 = x + 1; - y2 = y + 1; - - if(x2 > ss->texcache_side) x2 = tc_max; - if(y2 > ss->texcache_side) y2 = tc_max; - - urat = u - x; - vrat = v - y; - uopp = 1 - urat; - - return ((get_texcache_pixel(ss, x, y) * uopp + - get_texcache_pixel(ss, x2, y) * urat) * (1 - vrat) + - (get_texcache_pixel(ss, x, y2) * uopp + - get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0; -} - -/* Return a multiplier for brush strength on a particular vertex. */ -static float tex_strength(Sculpt *sd, SculptSession *ss, float *point, const float len) -{ - Brush *br = paint_brush(&sd->paint); - MTex *tex = NULL; - float avg= 1; - - if(br->texact >= 0) - tex = br->mtex[br->texact]; - - if(!tex) { - avg= 1; - } - else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) { - float jnk; - - /* Get strength by feeding the vertex - location directly into a texture */ - externtex(tex, point, &avg, - &jnk, &jnk, &jnk, &jnk); - } - else if(ss->texcache) { - const float bsize= ss->cache->pixel_radius * 2; - const float rot= tex->rot + ss->cache->rotation; - int px, py; - float flip[3], point_2d[2]; - - /* If the active area is being applied for symmetry, flip it - across the symmetry axis in order to project it. This insures - that the brush texture will be oriented correctly. */ - copy_v3_v3(flip, point); - flip_coord(flip, flip, ss->cache->symmetry); - projectf(ss->cache->mats, flip, point_2d); - - /* For Tile and Drag modes, get the 2D screen coordinates of the - and scale them up or down to the texture size. */ - if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) { - const int sx= (const int)tex->size[0]; - const int sy= (const int)tex->size[1]; - - float fx= point_2d[0]; - float fy= point_2d[1]; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - if(rot<0.001 && rot>-0.001) { - px= point_2d[0]; - py= point_2d[1]; - } else { - px= flen * cos(angle) + 2000; - py= flen * sin(angle) + 2000; } - if(sx != 1) - px %= sx-1; - if(sy != 1) - py %= sy-1; - avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy); - } - else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) { - float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize; - float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize; - - float angle= atan2(fy, fx) - rot; - float flen= sqrtf(fx*fx + fy*fy); - - fx = flen * cos(angle) + 0.5; - fy = flen * sin(angle) + 0.5; - - avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side); } - } - - avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */ - - return avg; -} + BLI_pbvh_vertex_iter_end; -/* Mark area around the brush as damaged. projverts are marked if they are - inside the area and the damaged rectangle in 2D screen coordinates is - added to damaged_rects. */ -static void sculpt_add_damaged_rect(SculptSession *ss) -{ - short p[2]; - RectNode *rn= MEM_mallocN(sizeof(RectNode),"RectNode"); - const float radius = MAX2(ss->cache->pixel_radius, ss->cache->previous_pixel_radius); - unsigned i; - - /* Find center */ - project(ss->cache->mats, ss->cache->location, p); - rn->r.xmin= p[0] - radius; - rn->r.ymin= p[1] - radius; - rn->r.xmax= p[0] + radius; - rn->r.ymax= p[1] + radius; - - BLI_addtail(&ss->damaged_rects, rn); - - /* Update insides */ - for(i=0; i<ss->totvert; ++i) { - if(!ss->projverts[i].inside) { - if(ss->projverts[i].co[0] > rn->r.xmin && ss->projverts[i].co[1] > rn->r.ymin && - ss->projverts[i].co[0] < rn->r.xmax && ss->projverts[i].co[1] < rn->r.ymax) { - ss->projverts[i].inside= 1; - } - } - // XXX: remember to fix this! - // temporary pass - ss->projverts[i].inside = 1; + BLI_pbvh_node_mark_update(nodes[n]); } } static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache) { + SculptSearchSphereData data; Brush *brush = paint_brush(&sd->paint); - float av_dist; - ListBase active_verts={0,0}; - ListBase *grab_active_verts = &ss->cache->grab_active_verts[ss->cache->symmetry]; - ActiveData *adata= 0; - float *vert; - const float bstrength= brush_strength(sd, cache); - Brush *b = brush; - int i; + PBVHNode **nodes= NULL; + int totnode; - sculpt_add_damaged_rect(ss); - - /* Build a list of all vertices that are potentially within the brush's - area of influence. Only do this once for the grab brush. */ - if((b->sculpt_tool != SCULPT_TOOL_GRAB) || cache->first_time) { - for(i=0; i<ss->totvert; ++i) { - /* Projverts.inside provides a rough bounding box */ - if(ss->multires || ss->projverts[i].inside) { - //vert= ss->vertexcosnos ? &ss->vertexcosnos[i*6] : a->verts[i].co; - vert= ss->mvert[i].co; - av_dist= len_v3v3(ss->cache->location, vert); - if(av_dist < cache->radius) { - adata= (ActiveData*)MEM_mallocN(sizeof(ActiveData), "ActiveData"); - - adata->Index = i; - /* Fade is used to store the final strength at which the brush - should modify a particular vertex. */ - adata->Fade= tex_strength(sd, ss, vert, av_dist) * bstrength; - adata->dist = av_dist; - - if(b->sculpt_tool == SCULPT_TOOL_GRAB && cache->first_time) - BLI_addtail(grab_active_verts, adata); - else - BLI_addtail(&active_verts, adata); - } - } + data.ss = ss; + data.sd = sd; + data.radius_squared = ss->cache->radius * ss->cache->radius; + + /* Build a list of all nodes that are potentially within the brush's + area of influence */ + if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { + if(cache->first_time) { + /* For the grab tool we store these nodes once in the beginning + and then reuse them. */ + BLI_pbvh_search_gather(ss->tree, sculpt_search_sphere_cb, &data, + &nodes, &totnode); + + ss->cache->grab_active_nodes[ss->cache->symmetry]= nodes; + ss->cache->grab_active_totnode[ss->cache->symmetry]= totnode; + copy_v3_v3(ss->cache->grab_active_location[ss->cache->symmetry], ss->cache->location); + } + else { + nodes= ss->cache->grab_active_nodes[ss->cache->symmetry]; + totnode= ss->cache->grab_active_totnode[ss->cache->symmetry]; + copy_v3_v3(ss->cache->location, ss->cache->grab_active_location[ss->cache->symmetry]); } } + else { + BLI_pbvh_search_gather(ss->tree, sculpt_search_sphere_cb, &data, + &nodes, &totnode); + } /* Only act if some verts are inside the brush area */ - if(active_verts.first || (b->sculpt_tool == SCULPT_TOOL_GRAB && grab_active_verts->first)) { + if(totnode) { /* Apply one type of brush action */ - switch(b->sculpt_tool){ + switch(brush->sculpt_tool){ case SCULPT_TOOL_DRAW: - do_draw_brush(sd, ss, &active_verts); + do_draw_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_SMOOTH: - do_smooth_brush(sd, ss, &active_verts); + do_smooth_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_PINCH: - do_pinch_brush(sd, ss, &active_verts); + do_pinch_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_INFLATE: - do_inflate_brush(sd, ss, &active_verts); + do_inflate_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_GRAB: - do_grab_brush(sd, ss); + do_grab_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ss, &active_verts); + do_layer_brush(sd, ss, nodes, totnode); break; case SCULPT_TOOL_FLATTEN: - do_flatten_clay_brush(sd, ss, &active_verts, 0); + do_flatten_clay_brush(sd, ss, nodes, totnode, 0); break; case SCULPT_TOOL_CLAY: - do_flatten_clay_brush(sd, ss, &active_verts, 1); + do_flatten_clay_brush(sd, ss, nodes, totnode, 1); + break; } - /* Copy the modified vertices from mesh to the active key */ + /* copy the modified vertices from mesh to the active key */ if(ss->kb) mesh_to_key(ss->ob->data, ss->kb); - - if(ss->vertexcosnos && !ss->multires) - BLI_freelistN(&active_verts); - else { - if(b->sculpt_tool != SCULPT_TOOL_GRAB) - addlisttolist(&ss->damaged_verts, &active_verts); - } - } + + if((brush->sculpt_tool != SCULPT_TOOL_GRAB) && nodes) + MEM_freeN(nodes); + } } /* Flip all the editdata across the axis/axes specified by symm. Used to @@ -932,6 +1428,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss) copy_v3_v3(cache->location, cache->true_location); copy_v3_v3(cache->grab_delta_symmetry, cache->grab_delta); cache->symmetry = 0; + cache->bstrength = brush_strength(sd, cache); do_brush_action(sd, ss, cache); for(i = 1; i <= symm; ++i) { @@ -944,110 +1441,6 @@ static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss) cache->first_time = 0; } -static void add_face_normal(vec3f *norm, MVert *mvert, const MFace* face, float *fn) -{ - vec3f c= {mvert[face->v1].co[0],mvert[face->v1].co[1],mvert[face->v1].co[2]}; - vec3f b= {mvert[face->v2].co[0],mvert[face->v2].co[1],mvert[face->v2].co[2]}; - vec3f a= {mvert[face->v3].co[0],mvert[face->v3].co[1],mvert[face->v3].co[2]}; - vec3f s1, s2; - float final[3]; - - sub_v3_v3v3(&s1.x,&a.x,&b.x); - sub_v3_v3v3(&s2.x,&c.x,&b.x); - - final[0] = s1.y * s2.z - s1.z * s2.y; - final[1] = s1.z * s2.x - s1.x * s2.z; - final[2] = s1.x * s2.y - s1.y * s2.x; - - if(fn) - copy_v3_v3(fn, final); - - norm->x+= final[0]; - norm->y+= final[1]; - norm->z+= final[2]; -} - -static void update_damaged_vert(SculptSession *ss, ListBase *lb) -{ - ActiveData *vert; - - float *buffer = ss->drawobject!=0?(float *)GPU_buffer_lock( ss->drawobject->normals ):0; - for(vert= lb->first; vert; vert= vert->next) { - vec3f norm= {0,0,0}; - IndexNode *face= ss->fmap[vert->Index].first; - - while(face){ - float *fn = NULL; - if(ss->face_normals) - fn = &ss->face_normals[face->index*3]; - add_face_normal(&norm, ss->mvert, &ss->mface[face->index], fn); - face= face->next; - } - normalize_v3(&norm.x); - - ss->mvert[vert->Index].no[0]=norm.x*32767; - ss->mvert[vert->Index].no[1]=norm.y*32767; - ss->mvert[vert->Index].no[2]=norm.z*32767; - - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[vert->Index]; - while( cur != 0 && cur->element != -1 ) { - int i = ss->drawobject->faceRemap[cur->element/3]; - if( ss->mface[i].flag & ME_SMOOTH ) { - VECCOPY(&buffer[cur->element*3],ss->mvert[vert->Index].no); - } - else { - float norm[3]; - if( ss->mface[i].v4 ) - normal_quad_v3( norm,ss->mvert[ss->mface[i].v1].co, ss->mvert[ss->mface[i].v2].co, ss->mvert[ss->mface[i].v3].co, ss->mvert[ss->mface[i].v4].co); - else - normal_tri_v3( norm,ss->mvert[ss->mface[i].v1].co, ss->mvert[ss->mface[i].v2].co, ss->mvert[ss->mface[i].v3].co); - VECCOPY(&buffer[(cur->element-cur->element%3)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3+1)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3+2)*3],norm); - - /* maybe this was a quad - need to update the other triangle of the quad */ - if( ss->drawobject->faceRemap[cur->element/3-1] == i ) { - VECCOPY(&buffer[(cur->element-cur->element%3-3)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3-2)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3-1)*3],norm); - } - if( ss->drawobject->faceRemap[cur->element/3+1] == i ) { - VECCOPY(&buffer[(cur->element-cur->element%3+3)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3+4)*3],norm); - VECCOPY(&buffer[(cur->element-cur->element%3+5)*3],norm); - } - } - - //VECCOPY(&buffer[cur->element*3],ss->mvert[vert->Index].no); - cur = cur->next; - } - } - } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->normals ); -} - -static void calc_damaged_verts(SculptSession *ss) -{ - int i; - - for(i=0; i<8; ++i) - update_damaged_vert(ss, &ss->cache->grab_active_verts[i]); - update_damaged_vert(ss, &ss->damaged_verts); - BLI_freelistN(&ss->damaged_verts); - ss->damaged_verts.first = ss->damaged_verts.last = NULL; -} - -#if 0 -static void projverts_clear_inside(SculptSession *ss) -{ - int i; - for(i = 0; i < ss->totvert; ++i) - ss->projverts[i].inside = 0; -} -#endif - static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) { Brush *brush = paint_brush(&sd->paint); @@ -1065,28 +1458,15 @@ static void sculpt_update_tex(Sculpt *sd, SculptSession *ss) } } -static void sculptmode_update_all_projverts(SculptSession *ss) -{ - unsigned i; - - if(!ss->projverts) - ss->projverts = MEM_mallocN(sizeof(ProjVert)*ss->totvert,"ProjVerts"); - - for(i=0; i<ss->totvert; ++i) { - project(ss->cache->mats, ss->vertexcosnos ? &ss->vertexcosnos[i * 6] : ss->mvert[i].co, - ss->projverts[i].co); - ss->projverts[i].inside= 0; - } -} - /* Checks whether full update mode (slower) needs to be used to work with modifiers */ char sculpt_modifiers_active(Object *ob) { ModifierData *md; for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) { - if(modifier_isEnabled(md, eModifierMode_Realtime) && md->type != eModifierType_Multires) - return 1; + if(modifier_isEnabled(md, eModifierMode_Realtime)) + if(!ELEM(md->type, eModifierType_Multires, eModifierType_ShapeKey)) + return 1; } return 0; @@ -1094,7 +1474,7 @@ char sculpt_modifiers_active(Object *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 */ -static struct MultiresModifierData *sculpt_multires_active(Object *ob) +struct MultiresModifierData *sculpt_multires_active(Object *ob) { ModifierData *md, *nmd; @@ -1116,22 +1496,46 @@ static struct MultiresModifierData *sculpt_multires_active(Object *ob) return NULL; } -static void sculpt_update_mesh_elements(bContext *C) +void sculpt_key_to_mesh(KeyBlock *kb, Object *ob) { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - int oldtotvert = ss->totvert; - DerivedMesh *dm = mesh_get_derived_final(CTX_data_scene(C), ob, CD_MASK_BAREMESH); + Mesh *me= ob->data; + + key_to_mesh(kb, me); + mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); +} + +void sculpt_mesh_to_key(Object *ob, KeyBlock *kb) +{ + Mesh *me= ob->data; + + mesh_to_key(me, kb); +} +void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap) +{ + DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0); + SculptSession *ss = ob->sculpt; + ss->ob= ob; + if((ob->shapeflag & OB_SHAPE_LOCK) && !sculpt_multires_active(ob)) { + ss->kb= ob_get_keyblock(ob); + ss->refkb= ob_get_reference_keyblock(ob); + } + else { + ss->kb= NULL; + ss->refkb= NULL; + } + + /* need to make PBVH with shape key coordinates */ + if(ss->kb) sculpt_key_to_mesh(ss->kb, ss->ob); + if((ss->multires = sculpt_multires_active(ob))) { - //DerivedMesh *dm = mesh_get_derived_final(CTX_data_scene(C), ob, CD_MASK_BAREMESH); ss->totvert = dm->getNumVerts(dm); ss->totface = dm->getNumFaces(dm); - ss->mvert = dm->getVertDataArray(dm, CD_MVERT); - ss->mface = dm->getFaceDataArray(dm, CD_MFACE); - ss->face_normals = dm->getFaceDataArray(dm, CD_NORMAL); + ss->mvert= NULL; + ss->mface= NULL; + ss->face_normals= NULL; } else { Mesh *me = get_mesh(ob); @@ -1141,31 +1545,9 @@ static void sculpt_update_mesh_elements(bContext *C) ss->mface = me->mface; ss->face_normals = NULL; } - if( GPU_buffer_legacy( dm ) ) { - ss->drawobject = 0; - } - else { - ss->drawobject = dm->drawObject; - } - if(ss->totvert != oldtotvert) { - if(ss->projverts) MEM_freeN(ss->projverts); - ss->projverts = NULL; - - if(ss->fmap) MEM_freeN(ss->fmap); - if(ss->fmap_mem) MEM_freeN(ss->fmap_mem); - create_vert_face_map(&ss->fmap, &ss->fmap_mem, ss->mface, ss->totvert, ss->totface); - ss->fmap_size = ss->totvert; - } - - if((ob->shapeflag & OB_SHAPE_LOCK) && !sculpt_multires_active(ob)) { - ss->kb= ob_get_keyblock(ob); - ss->refkb= ob_get_reference_keyblock(ob); - } - else { - ss->kb= NULL; - ss->refkb= NULL; - } + ss->tree = dm->getPBVH(ob, dm); + ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(dm): NULL; } static int sculpt_mode_poll(bContext *C) @@ -1179,27 +1561,27 @@ int sculpt_poll(bContext *C) return sculpt_mode_poll(C) && paint_poll(C); } -static void sculpt_undo_push(bContext *C, Sculpt *sd) +static char *sculpt_tool_name(Sculpt *sd) { Brush *brush = paint_brush(&sd->paint); switch(brush->sculpt_tool) { case SCULPT_TOOL_DRAW: - ED_undo_push(C, "Draw Brush"); break; + return "Draw Brush"; break; case SCULPT_TOOL_SMOOTH: - ED_undo_push(C, "Smooth Brush"); break; + return "Smooth Brush"; break; case SCULPT_TOOL_PINCH: - ED_undo_push(C, "Pinch Brush"); break; + return "Pinch Brush"; break; case SCULPT_TOOL_INFLATE: - ED_undo_push(C, "Inflate Brush"); break; + return "Inflate Brush"; break; case SCULPT_TOOL_GRAB: - ED_undo_push(C, "Grab Brush"); break; + return "Grab Brush"; break; case SCULPT_TOOL_LAYER: - ED_undo_push(C, "Layer Brush"); break; + return "Layer Brush"; break; case SCULPT_TOOL_FLATTEN: - ED_undo_push(C, "Flatten Brush"); break; + return "Flatten Brush"; break; default: - ED_undo_push(C, "Sculpting"); break; + return "Sculpting"; break; } } @@ -1251,28 +1633,26 @@ 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(SculptSession *ss, float offset) +static float unproject_brush_radius(ViewContext *vc, float center[3], float offset) { - float brush_edge[3]; - - /* In anchored mode, brush size changes with mouse loc, otherwise it's fixed using the brush radius */ - view3d_unproject(ss->cache->mats, brush_edge, ss->cache->initial_mouse[0] + offset, - ss->cache->initial_mouse[1], ss->cache->depth); + float delta[3]; - return len_v3v3(ss->cache->true_location, brush_edge); + initgrabz(vc->rv3d, center[0], center[1], center[2]); + window_to_3d_delta(vc->ar, delta, offset, 0); + return len_v3(delta); } static void sculpt_cache_free(StrokeCache *cache) { int i; - if(cache->orig_norms) - MEM_freeN(cache->orig_norms); if(cache->face_norms) MEM_freeN(cache->face_norms); if(cache->mats) MEM_freeN(cache->mats); - for(i = 0; i < 8; ++i) - BLI_freelistN(&cache->grab_active_verts[i]); + for(i = 0; i < 8; ++i) { + if(cache->grab_active_nodes[i]) + MEM_freeN(cache->grab_active_nodes[i]); + } MEM_freeN(cache); } @@ -1290,70 +1670,63 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte cache->flag = RNA_int_get(op->ptr, "flag"); RNA_float_get_array(op->ptr, "clip_tolerance", cache->clip_tolerance); RNA_float_get_array(op->ptr, "initial_mouse", cache->initial_mouse); - cache->depth = RNA_float_get(op->ptr, "depth"); 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->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats"); - view3d_get_transformation(vc, vc->obact, cache->mats); + cache->vc = vc; + cache->brush = brush; - sculpt_update_mesh_elements(C); - - /* Initialize layer brush displacements */ - if(brush->sculpt_tool == SCULPT_TOOL_LAYER && - (!ss->layer_disps || !(brush->flag & BRUSH_PERSISTENT))) { - if(ss->layer_disps) - MEM_freeN(ss->layer_disps); - ss->layer_disps = MEM_callocN(sizeof(float) * ss->totvert, "layer brush displacements"); - } - - /* Make copies of the mesh vertex locations and normals for some tools */ - if(brush->sculpt_tool == SCULPT_TOOL_LAYER || (brush->flag & BRUSH_ANCHORED)) { - if(brush->sculpt_tool != SCULPT_TOOL_LAYER || - !ss->mesh_co_orig || !(brush->flag & BRUSH_PERSISTENT)) { - if(!ss->mesh_co_orig) - ss->mesh_co_orig= MEM_mallocN(sizeof(float) * 3 * ss->totvert, + cache->mats = MEM_callocN(sizeof(bglMats), "sculpt bglMats"); + view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, cache->mats); + + /* Initialize layer brush displacements and persistent coords */ + if(brush->sculpt_tool == SCULPT_TOOL_LAYER) { + if(!ss->layer_disps || !(brush->flag & BRUSH_PERSISTENT)) { + if(ss->layer_disps) + MEM_freeN(ss->layer_disps); + ss->layer_disps = MEM_callocN(sizeof(float) * ss->totvert, "layer brush displacements"); + } + if(!ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + if(!ss->layer_co) + ss->layer_co= MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); for(i = 0; i < ss->totvert; ++i) - copy_v3_v3(ss->mesh_co_orig[i], ss->mvert[i].co); + copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); } + } - if(brush->flag & BRUSH_ANCHORED) { - cache->orig_norms= MEM_mallocN(sizeof(short) * 3 * ss->totvert, "Sculpt orig norm"); - for(i = 0; i < ss->totvert; ++i) { - cache->orig_norms[i][0] = ss->mvert[i].no[0]; - cache->orig_norms[i][1] = ss->mvert[i].no[1]; - cache->orig_norms[i][2] = ss->mvert[i].no[2]; - } - - if(ss->face_normals) { - float *fn = ss->face_normals; - cache->face_norms= MEM_mallocN(sizeof(float) * 3 * ss->totface, "Sculpt face norms"); - for(i = 0; i < ss->totface; ++i, fn += 3) - copy_v3_v3(cache->face_norms[i], fn); - } + /* Make copies of the mesh vertex locations and normals for some tools */ + if(brush->flag & BRUSH_ANCHORED) { + if(ss->face_normals) { + float *fn = ss->face_normals; + cache->face_norms= MEM_mallocN(sizeof(float) * 3 * ss->totface, "Sculpt face norms"); + for(i = 0; i < ss->totface; ++i, fn += 3) + copy_v3_v3(cache->face_norms[i], fn); } + + cache->original = 1; } - view3d_unproject(cache->mats, cache->true_location, cache->initial_mouse[0], cache->initial_mouse[1], cache->depth); - cache->initial_radius = unproject_brush_radius(ss, brush->size); + if(ELEM3(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE)) + if(!(brush->flag & BRUSH_ACCUMULATE)) + cache->original = 1; + cache->rotation = 0; cache->first_time = 1; } /* Initialize the stroke cache variants from operator properties */ -static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerRNA *ptr) +static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct PaintStroke *stroke, PointerRNA *ptr) { StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - float grab_location[3]; int dx, dy; - if(!(brush->flag & BRUSH_ANCHORED)) + if(!(brush->flag & BRUSH_ANCHORED) || cache->first_time) RNA_float_get_array(ptr, "location", cache->true_location); cache->flip = RNA_boolean_get(ptr, "flip"); RNA_float_get_array(ptr, "mouse", cache->mouse); @@ -1364,6 +1737,9 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerR cache->previous_pixel_radius = cache->pixel_radius; cache->pixel_radius = brush->size; + if(cache->first_time) + cache->initial_radius = unproject_brush_radius(cache->vc, cache->true_location, brush->size); + if(brush->flag & BRUSH_SIZE_PRESSURE) { cache->pixel_radius *= cache->pressure; cache->radius = cache->initial_radius * cache->pressure; @@ -1378,7 +1754,8 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerR dx = cache->mouse[0] - cache->initial_mouse[0]; dy = cache->mouse[1] - cache->initial_mouse[1]; cache->pixel_radius = sqrt(dx*dx + dy*dy); - cache->radius = unproject_brush_radius(ss, cache->pixel_radius); + cache->radius = unproject_brush_radius(paint_stroke_view_context(stroke), + cache->true_location, cache->pixel_radius); cache->rotation = atan2(dy, dx); } else if(brush->flag & BRUSH_RAKE) { @@ -1401,17 +1778,91 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, PointerR /* Find the grab delta */ if(brush->sculpt_tool == SCULPT_TOOL_GRAB) { - view3d_unproject(cache->mats, grab_location, cache->mouse[0], cache->mouse[1], cache->depth); + float grab_location[3]; + + if(cache->first_time) + copy_v3_v3(cache->orig_grab_location, cache->true_location); + + initgrabz(cache->vc->rv3d, cache->orig_grab_location[0], cache->orig_grab_location[1], cache->orig_grab_location[2]); + window_to_3d_delta(cache->vc->ar, grab_location, cache->mouse[0], cache->mouse[1]); + if(!cache->first_time) sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); copy_v3_v3(cache->old_grab_location, grab_location); } } +static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss) +{ + if(sculpt_modifiers_active(ss->ob)) + sculpt_update_mesh_elements(CTX_data_scene(C), ss->ob, 0); // XXX 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) +{ + SculptRaycastData *srd = data_v; + float (*origco)[3]= NULL; + + if(srd->original && srd->ss->cache) { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode= sculpt_undo_get_node(srd->ss, node); + origco= (unode)? unode->co: NULL; + } + + srd->hit |= BLI_pbvh_node_raycast(srd->ss->tree, node, origco, + srd->ray_start, srd->ray_normal, &srd->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_normal[3]; + float obimat[4][4]; + float mval[2] = {mouse[0] - vc->ar->winrct.xmin, + mouse[1] - vc->ar->winrct.ymin}; + SculptRaycastData srd; + + sculpt_stroke_modifiers_check(C, ss); + + viewray(vc->ar, vc->v3d, mval, ray_start, ray_normal); + + invert_m4_m4(obimat, ss->ob->obmat); + mul_m4_v3(obimat, ray_start); + mul_mat3_m4_v3(obimat, ray_normal); + + srd.ss = vc->obact->sculpt; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.dist = FLT_MAX; + srd.hit = 0; + srd.original = (cache)? cache->original: 0; + BLI_pbvh_raycast(ss->tree, sculpt_raycast_cb, &srd, + ray_start, ray_normal, srd.original); + + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, srd.dist); + add_v3_v3v3(out, out, ray_start); + + return srd.hit; +} + /* Initialize stroke operator properties */ static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmEvent *event, SculptSession *ss) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob= CTX_data_active_object(C); ModifierData *md; float scale[3], clip_tolerance[3] = {0,0,0}; @@ -1444,18 +1895,15 @@ static void sculpt_brush_stroke_init_properties(bContext *C, wmOperator *op, wmE mouse[0] = event->x; mouse[1] = event->y; RNA_float_set_array(op->ptr, "initial_mouse", mouse); - - /* Initial screen depth under the mouse */ - RNA_float_set(op->ptr, "depth", read_cached_depth(paint_stroke_view_context(op->customdata), event->x, event->y)); - - sculpt_update_cache_invariants(sd, ss, C, op); } 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; + Brush *brush = paint_brush(&sd->paint); if(ob_get_key(ob) && !(ob->shapeflag & OB_SHAPE_LOCK)) { BKE_report(reports, RPT_ERROR, "Shape key sculpting requires a locked shape."); @@ -1469,9 +1917,7 @@ static int sculpt_brush_stroke_init(bContext *C, ReportList *reports) changes are made to the texture. */ sculpt_update_tex(sd, ss); - sculpt_update_mesh_elements(C); - - if(ss->kb) key_to_mesh(ss->kb, ss->ob->data); + sculpt_update_mesh_elements(scene, ob, brush->sculpt_tool == SCULPT_TOOL_SMOOTH); return 1; } @@ -1480,30 +1926,31 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) { StrokeCache *cache = ss->cache; Brush *brush = paint_brush(&sd->paint); - float *buffer= NULL; int i; /* Restore the mesh before continuing with anchored stroke */ - if((brush->flag & BRUSH_ANCHORED) && ss->mesh_co_orig) { - - if(ss->drawobject) - buffer= (float *)GPU_buffer_lock(ss->drawobject->normals); - - for(i = 0; i < ss->totvert; ++i) { - copy_v3_v3(ss->mvert[i].co, ss->mesh_co_orig[i]); - ss->mvert[i].no[0] = cache->orig_norms[i][0]; - ss->mvert[i].no[1] = cache->orig_norms[i][1]; - ss->mvert[i].no[2] = cache->orig_norms[i][2]; - if( buffer != 0 ) { - IndexLink *cur = &ss->drawobject->indices[i]; - while( cur != 0 && cur->element != -1 ) { - VECCOPY(&buffer[cur->element*3],cache->orig_norms[i]); - cur = cur->next; + if(brush->flag & BRUSH_ANCHORED) { + PBVHNode **nodes; + int n, totnode; + + BLI_pbvh_search_gather(ss->tree, NULL, NULL, &nodes, &totnode); + + #pragma omp parallel for private(n) schedule(static) + for(n=0; n<totnode; n++) { + SculptUndoNode *unode; + + unode= sculpt_undo_get_node(ss, nodes[n]); + if(unode) { + PBVHVertexIter vd; + + BLI_pbvh_vertex_iter_begin(ss->tree, 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_end; } } - if( buffer != 0 ) - GPU_buffer_unlock( ss->drawobject->normals ); if(ss->face_normals) { float *fn = ss->face_normals; @@ -1516,50 +1963,59 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss) } } -static void sculpt_post_stroke_free(SculptSession *ss) -{ - BLI_freelistN(&ss->damaged_rects); - BLI_freelistN(&ss->damaged_verts); -} - static void sculpt_flush_update(bContext *C) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; ARegion *ar = CTX_wm_region(C); MultiresModifierData *mmd = ss->multires; + int redraw = 0; - calc_damaged_verts(ss); - - if(mmd) { - if(mmd->undo_verts && mmd->undo_verts != ss->mvert) - MEM_freeN(mmd->undo_verts); - - mmd->undo_verts = ss->mvert; - mmd->undo_verts_tot = ss->totvert; + if(mmd) multires_mark_as_modified(ob); - } - if(sculpt_modifiers_active(ob)) + if(sculpt_modifiers_active(ob)) { DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + ED_region_tag_redraw(ar); + } + else { + rcti r; - ED_region_tag_redraw(ar); + BLI_pbvh_update(ss->tree, PBVH_UpdateBB, NULL); + redraw = sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r); + + if(redraw) { + r.xmin += ar->winrct.xmin + 1; + r.xmax += ar->winrct.xmin - 1; + r.ymin += ar->winrct.ymin + 1; + r.ymax += ar->winrct.ymin - 1; + + ss->partial_redraw = 1; + ED_region_tag_redraw_partial(ar, &r); + } + } } static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *event) { - ViewContext vc; - float cur_depth; - - view3d_set_viewcontext(C, &vc); - cur_depth = read_cached_depth(&vc, event->x, event->y); + float mouse[2] = {event->x, event->y}, location[3]; + int over_mesh; - /* Don't start the stroke until a valid depth is found */ - if(cur_depth < 1.0 - FLT_EPSILON) { - SculptSession *ss = CTX_data_active_object(C)->sculpt; + over_mesh = sculpt_stroke_get_location(C, op->customdata, location, mouse); + + /* Don't start the stroke until mouse goes over the mesh */ + if(over_mesh) { + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); sculpt_brush_stroke_init_properties(C, op, event, ss); - sculptmode_update_all_projverts(ss); + + sculpt_update_cache_invariants(sd, ss, C, op); + + sculpt_undo_push_begin(ss, sculpt_tool_name(sd)); return 1; } @@ -1572,13 +2028,13 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, P Sculpt *sd = CTX_data_tool_settings(C)->sculpt; SculptSession *ss = CTX_data_active_object(C)->sculpt; - sculpt_update_cache_variants(sd, ss, itemptr); + sculpt_stroke_modifiers_check(C, ss); + sculpt_update_cache_variants(sd, ss, stroke, itemptr); sculpt_restore_mesh(sd, ss); do_symmetrical_brush_actions(sd, ss); /* Cleanup */ sculpt_flush_update(C); - sculpt_post_stroke_free(ss); } static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke) @@ -1588,14 +2044,18 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke) /* Finished */ if(ss->cache) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - - if(ss->refkb) key_to_mesh(ss->refkb, ob->data); + sculpt_stroke_modifiers_check(C, ss); - request_depth_update(paint_stroke_view_context(stroke)->rv3d); sculpt_cache_free(ss->cache); ss->cache = NULL; - sculpt_undo_push(C, sd); + + sculpt_undo_push_end(ss); + + BLI_pbvh_update(ss->tree, PBVH_UpdateOriginalBB, NULL); + + if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob); + + WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob); } } @@ -1604,7 +2064,8 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, wmEvent *even if(!sculpt_brush_stroke_init(C, op->reports)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, sculpt_stroke_test_start, + op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, + sculpt_stroke_test_start, sculpt_stroke_update_step, sculpt_stroke_done); @@ -1624,18 +2085,16 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) if(!sculpt_brush_stroke_init(C, op->reports)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, 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_done); sculpt_update_cache_invariants(sd, ss, C, op); - sculptmode_update_all_projverts(ss); paint_stroke_exec(C, op); sculpt_flush_update(C); sculpt_cache_free(ss->cache); - sculpt_undo_push(C, sd); - return OPERATOR_FINISHED; } @@ -1670,9 +2129,6 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) /* The initial 2D location of the mouse */ RNA_def_float_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX); - - /* The initial screen depth of the mouse */ - RNA_def_float(ot->srna, "depth", 0.0f, 0.0f, FLT_MAX, "depth", "", 0.0f, FLT_MAX); } /**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/ @@ -1686,9 +2142,9 @@ static int sculpt_set_persistent_base(bContext *C, wmOperator *op) MEM_freeN(ss->layer_disps); ss->layer_disps = NULL; - if(ss->mesh_co_orig) - MEM_freeN(ss->mesh_co_orig); - ss->mesh_co_orig = NULL; + if(ss->layer_co) + MEM_freeN(ss->layer_co); + ss->layer_co = NULL; } return OPERATOR_FINISHED; @@ -1709,15 +2165,30 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) /**** Toggle operator for turning sculpt mode on or off ****/ +static void sculpt_init_session(Scene *scene, Object *ob) +{ + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + + sculpt_update_mesh_elements(scene, ob, 0); + + if(ob->sculpt->refkb) + sculpt_key_to_mesh(ob->sculpt->refkb, ob); +} + static int sculpt_toggle_mode(bContext *C, wmOperator *op) { + 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(ob); if(ob->mode & OB_MODE_SCULPT) { if(sculpt_multires_active(ob)) multires_force_update(ob); + if(mmd && mmd->sculptlvl != mmd->lvl) + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + /* Leave sculptmode */ ob->mode &= ~OB_MODE_SCULPT; @@ -1725,8 +2196,10 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op) } else { /* Enter sculptmode */ - ob->mode |= OB_MODE_SCULPT; + + if(mmd && mmd->sculptlvl != mmd->lvl) + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); /* Create persistent sculpt mode data */ if(!ts->sculpt) @@ -1735,7 +2208,8 @@ static int sculpt_toggle_mode(bContext *C, wmOperator *op) /* Create sculpt mode session data */ if(ob->sculpt) free_sculptsession(&ob->sculpt); - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + + sculpt_init_session(scene, ob); paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT); @@ -1767,3 +2241,4 @@ void ED_operatortypes_sculpt() WM_operatortype_append(SCULPT_OT_sculptmode_toggle); WM_operatortype_append(SCULPT_OT_set_persistent_base); } + diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 15ccacc294a..0e1c0510902 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -35,7 +35,9 @@ struct bContext; struct Brush; +struct KeyBlock; struct Mesh; +struct MultiresModifierData; struct Object; struct Scene; struct Sculpt; @@ -47,6 +49,7 @@ 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 Object *ob); struct Brush *sculptmode_brush(void); //void do_symmetrical_brush_actions(struct Sculpt *sd, struct wmOperator *wm, struct BrushAction *a, short *, short *); @@ -55,6 +58,9 @@ char sculpt_modifiers_active(struct Object *ob); void sculpt(Sculpt *sd); int sculpt_poll(struct bContext *C); +void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap); +void sculpt_key_to_mesh(struct KeyBlock *kb, struct Object *ob); +void sculpt_mesh_to_key(struct Object *ob, struct KeyBlock *kb); /* Stroke */ struct SculptStroke *sculpt_stroke_new(const int max); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index a1f9a9fd250..93bedd0b866 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -310,6 +310,8 @@ static int get_file_icon(struct direntry *file) return ICON_FILE_SOUND; else if (file->flags & FTFONTFILE) return ICON_FILE_FONT; + else if (file->flags & BTXFILE) + return ICON_FILE_BLANK; else return ICON_FILE_BLANK; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 640f365b073..81aa63dcad9 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -871,6 +871,8 @@ void filelist_setfiletypes(struct FileList* filelist, short has_quicktime) || BLI_testextensie(file->relname, ".otf") || BLI_testextensie(file->relname, ".otc")) { file->flags |= FTFONTFILE; + } else if(BLI_testextensie(file->relname, ".btx")) { + file->flags |= BTXFILE; } else if (has_quicktime){ if( BLI_testextensie(file->relname, ".int") || BLI_testextensie(file->relname, ".inta") diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index d681a58b276..bf580545455 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -161,6 +161,8 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->filter |= RNA_boolean_get(op->ptr, "filter_text") ? TEXTFILE : 0; if(RNA_struct_find_property(op->ptr, "filter_folder")) params->filter |= RNA_boolean_get(op->ptr, "filter_folder") ? FOLDERFILE : 0; + if(RNA_struct_find_property(op->ptr, "filter_btx")) + params->filter |= RNA_boolean_get(op->ptr, "filter_btx") ? BTXFILE : 0; if (params->filter != 0) params->flag |= FILE_FILTER; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 31c12285293..edb91f2b0c9 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -108,6 +108,7 @@ #include "ED_mesh.h" #include "ED_particle.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "ED_types.h" #include "ED_util.h" @@ -2703,7 +2704,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, GPU_enable_material); + dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GPU_disable_material(); } @@ -2722,7 +2723,7 @@ static int wpaint__setSolidDrawOptions(void *userData, int index, int *drawSmoot return 1; } -static void draw_mesh_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, int dt, int flag) +static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, Base *base, int dt, int flag) { Object *ob= base->object; Mesh *me = ob->data; @@ -2795,7 +2796,7 @@ static void draw_mesh_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base } } else if(dt==OB_SOLID) { - if((v3d->flag&V3D_SELECT_OUTLINE) && (base->flag&SELECT) && !draw_wire) + if((v3d->flag&V3D_SELECT_OUTLINE) && (base->flag&SELECT) && !draw_wire && !ob->sculpt) draw_mesh_object_outline(v3d, ob, dm); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, me->flag & ME_TWOSIDED ); @@ -2803,7 +2804,23 @@ static void draw_mesh_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base glEnable(GL_LIGHTING); glFrontFace((ob->transflag&OB_NEG_SCALE)?GL_CW:GL_CCW); - dm->drawFacesSolid(dm, GPU_enable_material); + if(ob->sculpt) { + Paint *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) { + 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); + GPU_disable_material(); glFrontFace(GL_CCW); @@ -2814,7 +2831,8 @@ static void draw_mesh_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base } else { UI_ThemeColor(TH_WIRE); } - dm->drawLooseEdges(dm); + if(!ob->sculpt) + dm->drawLooseEdges(dm); } else if(dt==OB_SHADED) { int do_draw= 1; /* to resolve all G.f settings below... */ @@ -2932,7 +2950,7 @@ static void draw_mesh_fancy(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base } /* returns 1 if nothing was drawn, for detecting to draw an object center */ -static int draw_mesh_object(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, int dt, int flag) +static int draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, Base *base, int dt, int flag) { Object *ob= base->object; Object *obedit= scene->obedit; @@ -2967,10 +2985,6 @@ static int draw_mesh_object(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base if (obedit!=ob && finalDM) finalDM->release(finalDM); } -// else if(!em && (G.f & G_SCULPTMODE) &&(scene->sculptdata.flags & SCULPT_DRAW_FAST) && -// OBACT==ob && !sculpt_modifiers_active(ob)) { -// XXX sculptmode_draw_mesh(0); -// } else { /* don't create boundbox here with mesh_get_bb(), the derived system will make it, puts deformed bb's OK */ if(me->totface<=4 || boundbox_clip(rv3d, ob->obmat, (ob->bb)? ob->bb: me->bb)) { @@ -2982,7 +2996,7 @@ static int draw_mesh_object(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base (check_alpha)? &do_alpha_pass: NULL); } - draw_mesh_fancy(scene, v3d, rv3d, base, dt, flag); + draw_mesh_fancy(scene, ar, v3d, rv3d, base, dt, flag); GPU_end_object_materials(); @@ -5744,7 +5758,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, int flag) switch( ob->type) { case OB_MESH: - empty_object= draw_mesh_object(scene, v3d, rv3d, base, dt, flag); + empty_object= draw_mesh_object(scene, ar, v3d, rv3d, base, dt, flag); if(flag!=DRAW_CONSTCOLOR) dtx &= ~OB_DRAWWIRE; // mesh draws wire itself break; @@ -6333,10 +6347,8 @@ static void bbs_mesh_solid(Scene *scene, View3D *v3d, Object *ob) int ind; colors = MEM_mallocN(dm->getNumFaces(dm)*sizeof(MCol)*4,"bbs_mesh_solid"); for(i=0;i<dm->getNumFaces(dm);i++) { - if( index != 0 ) - ind = index[i]; - else - ind = i; + ind= ( index )? index[i]: i; + if (face_sel_mode==0 || !(me->mface[ind].flag&ME_HIDE)) { unsigned int fbindex = index_to_framebuffer(ind+1); for(j=0;j<4;j++) { @@ -6463,7 +6475,7 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r glEnable(GL_LIGHTING); if(dm) { - dm->drawFacesSolid(dm, GPU_enable_material); + dm->drawFacesSolid(dm, NULL, 0, GPU_enable_material); GPU_end_object_materials(); } else if(edm) diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 5a2040a44aa..19667beaaf9 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1643,37 +1643,6 @@ void view3d_update_depths(ARegion *ar, View3D *v3d) } } -/* Enable sculpting in wireframe mode by drawing sculpt object only to the depth buffer */ -static void draw_sculpt_depths(Scene *scene, ARegion *ar, View3D *v3d) -{ - Object *ob = OBACT; - - int dt= MIN2(v3d->drawtype, ob->dt); - if(v3d->zbuf==0 && dt>OB_WIRE) - dt= OB_WIRE; - if(dt == OB_WIRE) { - GLboolean depth_on; - int orig_vdt = v3d->drawtype; - int orig_zbuf = v3d->zbuf; - int orig_odt = ob->dt; - - glGetBooleanv(GL_DEPTH_TEST, &depth_on); - v3d->drawtype = ob->dt = OB_SOLID; - v3d->zbuf = 1; - - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glEnable(GL_DEPTH_TEST); - draw_object(scene, ar, v3d, BASACT, 0); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if(!depth_on) - glDisable(GL_DEPTH_TEST); - - v3d->drawtype = orig_vdt; - v3d->zbuf = orig_zbuf; - ob->dt = orig_odt; - } -} - void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (* func)(void *)) { RegionView3D *rv3d= ar->regiondata; @@ -1891,8 +1860,6 @@ static CustomDataMask get_viewedit_datamask(bScreen *screen, Scene *scene, Objec mask |= CD_MASK_MCOL; if(ob->mode & OB_MODE_WEIGHT_PAINT) mask |= CD_MASK_WEIGHT_MCOL; - if(ob->mode & OB_MODE_SCULPT) - mask |= CD_MASK_MDISPS; } return mask; @@ -2137,7 +2104,7 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) } // retopo= retopo_mesh_check() || retopo_curve_check(); - sculptparticle= (obact && obact->mode & (OB_MODE_SCULPT|OB_MODE_PARTICLE_EDIT)) && !scene->obedit; + sculptparticle= (obact && obact->mode & (OB_MODE_PARTICLE_EDIT)) && !scene->obedit; if(retopo) view3d_update_depths(ar, v3d); @@ -2150,8 +2117,6 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) } if(!retopo && sculptparticle && !(obact && (obact->dtx & OB_DRAWXRAY))) { - if(obact && obact->mode & OB_MODE_SCULPT) - draw_sculpt_depths(scene, ar, v3d); view3d_update_depths(ar, v3d); } @@ -2166,8 +2131,6 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) view3d_draw_xray(scene, ar, v3d, 1); // clears zbuffer if it is used! if(!retopo && sculptparticle && (obact && (OBACT->dtx & OB_DRAWXRAY))) { - if(obact && obact->mode & OB_MODE_SCULPT) - draw_sculpt_depths(scene, ar, v3d); view3d_update_depths(ar, v3d); } @@ -2189,8 +2152,6 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar) ED_region_pixelspace(ar); - /* Draw Sculpt Mode brush XXX (removed) */ - // retopo_paint_view_update(v3d); // retopo_draw_paint_lines(); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index fd20b534dd7..6a58e3c2e8d 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -278,7 +278,7 @@ static void calctrackballvec(rcti *rect, int mx, int my, float *vec) } -static void viewops_data(bContext *C, wmOperator *op, wmEvent *event) +static void viewops_data_create(bContext *C, wmOperator *op, wmEvent *event) { static float lastofs[3] = {0,0,0}; View3D *v3d = CTX_wm_view3d(C); @@ -359,6 +359,21 @@ static void viewops_data(bContext *C, wmOperator *op, wmEvent *event) if (rv3d->persmat[2][1] < 0.0f) vod->reverse= -1.0f; + rv3d->rflag |= RV3D_NAVIGATING; +} + +static void viewops_data_free(bContext *C, wmOperator *op) +{ + Paint *p = paint_get_active(CTX_data_scene(C)); + ViewOpsData *vod= op->customdata; + + vod->rv3d->rflag &= ~RV3D_NAVIGATING; + + if(p && (p->flags & PAINT_FAST_NAVIGATE)) + ED_region_tag_redraw(vod->ar); + + MEM_freeN(vod); + op->customdata= NULL; } /* ************************** viewrotate **********************************/ @@ -622,9 +637,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, wmEvent *event) } else if (event_code==VIEW_CONFIRM) { request_depth_update(CTX_wm_region_view3d(C)); - - MEM_freeN(vod); - op->customdata= NULL; + viewops_data_free(C, op); return OPERATOR_FINISHED; } @@ -641,7 +654,7 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, wmEvent *event) return OPERATOR_CANCELLED; /* makes op->customdata */ - viewops_data(C, op, event); + viewops_data_create(C, op, event); vod= op->customdata; /* switch from camera view when: */ @@ -762,8 +775,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event) else if (event_code==VIEW_CONFIRM) { request_depth_update(CTX_wm_region_view3d(C)); - MEM_freeN(vod); - op->customdata= NULL; + viewops_data_free(C, op); return OPERATOR_FINISHED; } @@ -774,7 +786,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, wmEvent *event) static int viewmove_invoke(bContext *C, wmOperator *op, wmEvent *event) { /* makes op->customdata */ - viewops_data(C, op, event); + viewops_data_create(C, op, event); /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -966,9 +978,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event) } else if (event_code==VIEW_CONFIRM) { request_depth_update(CTX_wm_region_view3d(C)); - - MEM_freeN(vod); - op->customdata= NULL; + viewops_data_free(C, op); return OPERATOR_FINISHED; } @@ -1029,7 +1039,7 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event) } else { /* makes op->customdata */ - viewops_data(C, op, event); + viewops_data_create(C, op, event); /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -1971,12 +1981,9 @@ void ED_view3d_local_clipping(RegionView3D *rv3d, float mat[][4]) static int view3d_clipping_exec(bContext *C, wmOperator *op) { RegionView3D *rv3d= CTX_wm_region_view3d(C); + ViewContext vc; + bglMats mats; rcti rect; - double mvmatrix[16]; - double projmatrix[16]; - double xs, ys, p[3]; - GLint viewport[4]; - short val; rect.xmin= RNA_int_get(op->ptr, "xmin"); rect.ymin= RNA_int_get(op->ptr, "ymin"); @@ -1989,36 +1996,9 @@ static int view3d_clipping_exec(bContext *C, wmOperator *op) /* note; otherwise opengl won't work */ view3d_operator_needs_opengl(C); - /* Get the matrices needed for gluUnProject */ - glGetIntegerv(GL_VIEWPORT, viewport); - glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix); - glGetDoublev(GL_PROJECTION_MATRIX, projmatrix); - - /* near zero floating point values can give issues with gluUnProject - in side view on some implementations */ - if(fabs(mvmatrix[0]) < 1e-6) mvmatrix[0]= 0.0; - if(fabs(mvmatrix[5]) < 1e-6) mvmatrix[5]= 0.0; - - /* Set up viewport so that gluUnProject will give correct values */ - viewport[0] = 0; - viewport[1] = 0; - - /* four clipping planes and bounding volume */ - /* first do the bounding volume */ - for(val=0; val<4; val++) { - - xs= (val==0||val==3)?rect.xmin:rect.xmax; - ys= (val==0||val==1)?rect.ymin:rect.ymax; - - gluUnProject(xs, ys, 0.0, mvmatrix, projmatrix, viewport, &p[0], &p[1], &p[2]); - VECCOPY(rv3d->clipbb->vec[val], p); - - gluUnProject(xs, ys, 1.0, mvmatrix, projmatrix, viewport, &p[0], &p[1], &p[2]); - VECCOPY(rv3d->clipbb->vec[4+val], p); - } - - /* then plane equations */ - calc_clipping_plane(rv3d->clip, rv3d->clipbb); + view3d_set_viewcontext(C, &vc); + view3d_get_transformation(vc.ar, vc.rv3d, vc.obact, &mats); + view3d_calculate_clipping(rv3d->clipbb, rv3d->clip, &mats, &rect); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index e7ebcbdb849..a1135bf6986 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -124,24 +124,24 @@ void view3d_get_view_aligned_coordinate(ViewContext *vc, float *fp, short mval[2 } } -void view3d_get_transformation(ViewContext *vc, Object *ob, bglMats *mats) +void view3d_get_transformation(ARegion *ar, RegionView3D *rv3d, Object *ob, bglMats *mats) { float cpy[4][4]; int i, j; - mul_m4_m4m4(cpy, ob->obmat, vc->rv3d->viewmat); + mul_m4_m4m4(cpy, ob->obmat, rv3d->viewmat); for(i = 0; i < 4; ++i) { for(j = 0; j < 4; ++j) { - mats->projection[i*4+j] = vc->rv3d->winmat[i][j]; + mats->projection[i*4+j] = rv3d->winmat[i][j]; mats->modelview[i*4+j] = cpy[i][j]; } } - mats->viewport[0] = vc->ar->winrct.xmin; - mats->viewport[1] = vc->ar->winrct.ymin; - mats->viewport[2] = vc->ar->winx; - mats->viewport[3] = vc->ar->winy; + mats->viewport[0] = ar->winrct.xmin; + mats->viewport[1] = ar->winrct.ymin; + mats->viewport[2] = ar->winx; + mats->viewport[3] = ar->winy; } /* ********************** view3d_select: selection manipulations ********************* */ diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 9cd9d12b975..a569eff1ebe 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -287,6 +287,8 @@ void smooth_view(bContext *C, Object *oldcamera, Object *camera, float *ofs, flo /* ensure it shows correct */ if(sms.to_camera) rv3d->persp= RV3D_PERSP; + + rv3d->rflag |= RV3D_NAVIGATING; /* keep track of running timer! */ if(rv3d->sms==NULL) @@ -349,6 +351,7 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *op, wmEvent *event) WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer); rv3d->smooth_timer= NULL; + rv3d->rflag &= ~RV3D_NAVIGATING; } else { int i; @@ -486,6 +489,44 @@ void VIEW3D_OT_setobjectascamera(wmOperatorType *ot) /* ********************************** */ +void view3d_calculate_clipping(BoundBox *bb, float planes[4][4], bglMats *mats, rcti *rect) +{ + double xs, ys, p[3]; + short val; + + /* near zero floating point values can give issues with gluUnProject + in side view on some implementations */ + if(fabs(mats->modelview[0]) < 1e-6) mats->modelview[0]= 0.0; + if(fabs(mats->modelview[5]) < 1e-6) mats->modelview[5]= 0.0; + + /* Set up viewport so that gluUnProject will give correct values */ + mats->viewport[0] = 0; + mats->viewport[1] = 0; + + /* four clipping planes and bounding volume */ + /* first do the bounding volume */ + for(val=0; val<4; val++) { + xs= (val==0||val==3)?rect->xmin:rect->xmax; + ys= (val==0||val==1)?rect->ymin:rect->ymax; + + gluUnProject(xs, ys, 0.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]); + VECCOPY(bb->vec[val], p); + + gluUnProject(xs, ys, 1.0, mats->modelview, mats->projection, mats->viewport, &p[0], &p[1], &p[2]); + VECCOPY(bb->vec[4+val], p); + } + + /* then plane equations */ + for(val=0; val<4; val++) { + + normal_tri_v3(planes[val], bb->vec[val], bb->vec[val==3?0:val+1], bb->vec[val+4]); + + planes[val][3]= - planes[val][0]*bb->vec[val][0] + - planes[val][1]*bb->vec[val][1] + - planes[val][2]*bb->vec[val][2]; + } +} + /* create intersection coordinates in view Z direction at mouse coordinates */ void viewline(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float ray_end[3]) { @@ -501,8 +542,8 @@ void viewline(ARegion *ar, View3D *v3d, float mval[2], float ray_start[3], float mul_m4_v4(rv3d->persinv, vec); mul_v3_fl(vec, 1.0f / vec[3]); - VECCOPY(ray_start, rv3d->viewinv[3]); - VECSUB(vec, vec, ray_start); + copy_v3_v3(ray_start, rv3d->viewinv[3]); + sub_v3_v3v3(vec, vec, ray_start); normalize_v3(vec); VECADDFAC(ray_start, rv3d->viewinv[3], vec, v3d->near); @@ -1920,7 +1961,7 @@ int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *event) fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer(); - fly->rv3d->rflag |= RV3D_FLYMODE; /* so we draw the corner margins */ + fly->rv3d->rflag |= RV3D_FLYMODE|RV3D_NAVIGATING; /* so we draw the corner margins */ /* detect weather to start with Z locking */ upvec[0]=1.0f; upvec[1]=0.0f; upvec[2]=0.0f; @@ -2019,7 +2060,7 @@ static int flyEnd(bContext *C, FlyInfo *fly) /*Done with correcting for the dist */ } - rv3d->rflag &= ~RV3D_FLYMODE; + rv3d->rflag &= ~(RV3D_FLYMODE|RV3D_NAVIGATING); //XXX2.5 BIF_view3d_previewrender_signal(fly->sa, PR_DBASE|PR_DISPRECT); /* not working at the moment not sure why */ diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index ae1e932bb81..fc2576eef5d 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -60,7 +60,7 @@ void ED_editors_exit(bContext *C) /* frees all editmode undos */ undo_editmode_clear(); - undo_imagepaint_clear(); + ED_undo_paint_free(); for(sce=G.main->scene.first; sce; sce= sce->id.next) { if(sce->obedit) { diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index d26f7a7a484..e20c88ba41f 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -124,7 +124,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) SpaceImage *sima= (SpaceImage *)sa->spacedata.first; if((obact && obact->mode & OB_MODE_TEXTURE_PAINT) || sima->flag & SI_DRAWTOOL) { - undo_imagepaint_step(step); + ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step); WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; @@ -146,7 +146,9 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) int do_glob_undo= 0; if(obact && obact->mode & OB_MODE_TEXTURE_PAINT) - undo_imagepaint_step(step); + ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step); + else if(obact && obact->mode & OB_MODE_SCULPT) + ED_undo_paint_step(C, UNDO_PAINT_MESH, step); else if(obact && obact->mode & OB_MODE_PARTICLE_EDIT) { if(step==1) PE_undo(CTX_data_scene(C)); |