Sculpt: WIP brush behavior changes
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Fri, 6 Nov 2009 16:46:35 +0000 (16:46 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Fri, 6 Nov 2009 16:46:35 +0000 (16:46 +0000)
* Draw/Inflate/Layer now keep working on the original mesh coordinates and
  normals from when the stroke started. This helps avoid the mesh blowing
  up, but can still be better. The old behavior is still available as
  "Accumulate" in the UI.
* This requires some more memory usage for the BVH, would like to find a
  way to avoid that.
* Smooth falloff is now the default.
* Spacing is now enabled by default, with a value of 7.5.
* Anchored now stores normals per node to save some memory.

release/scripts/ui/space_view3d_toolbar.py
source/blender/blenkernel/intern/brush.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenlib/BLI_pbvh.h
source/blender/blenlib/intern/pbvh.c
source/blender/editors/sculpt_paint/paint_undo.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/makesdna/DNA_brush_types.h
source/blender/makesrna/intern/rna_brush.c

index 0ad694e..7d1f905 100644 (file)
@@ -523,6 +523,9 @@ class VIEW3D_PT_tools_brush(PaintPanel):
                 if brush.sculpt_tool in ('DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'):
                     col.row().itemR(brush, "direction", expand=True)
 
+                if brush.sculpt_tool in ('DRAW', 'INFLATE', 'LAYER'):
+                    col.itemR(brush, "use_accumulate")
+
                 if brush.sculpt_tool == 'LAYER':
                     col.itemR(brush, "use_persistent")
                     col.itemO("sculpt.set_persistent_base")
index 8a4ffca..b415e7b 100644 (file)
@@ -75,13 +75,14 @@ Brush *add_brush(const char *name)
        brush->rgb[2]= 1.0f;
        brush->alpha= 0.2f;
        brush->size= 25;
-       brush->spacing= 10.0f;
+       brush->spacing= 7.5f;
        brush->smooth_stroke_radius= 75;
        brush->smooth_stroke_factor= 0.9;
        brush->rate= 0.1f;
        brush->jitter= 0.0f;
        brush->clone.alpha= 0.5;
        brush->sculpt_tool = SCULPT_TOOL_DRAW;
+       brush->flag |= BRUSH_SPACE;
 
        brush_curve_preset(brush, BRUSH_PRESET_SMOOTH);
 
index 7cc46d3..7acf77c 100644 (file)
@@ -420,11 +420,13 @@ static void draw_partial_cb(PBVHNode *node, void *data)
    Returns true if the AABB is at least partially within the frustum
    (ok, not a real frustum), false otherwise.
 */
-int planes_contain_AABB(PBVHNode *node, float bb_min[3], float bb_max[3], void *data)
+int planes_contain_AABB(PBVHNode *node, void *data)
 {
        float (*planes)[4] = data;
        int i, axis;
-       float vmin[3], vmax[3];
+       float vmin[3], vmax[3], bb_min[3], bb_max[3];
+
+       BLI_pbvh_node_get_BB(node, bb_min, bb_max);
 
        for(i = 0; i < 4; ++i) { 
                for(axis = 0; axis < 3; ++axis) {
index ba9de46..360a993 100644 (file)
@@ -37,8 +37,7 @@ typedef struct PBVHNode PBVHNode;
 /* Callbacks */
 
 /* returns 1 if the search should continue from this node, 0 otherwise */
-typedef int (*BLI_pbvh_SearchCallback)(PBVHNode *node,
-       float bb_min[3], float bb_max[3], void *data);
+typedef int (*BLI_pbvh_SearchCallback)(PBVHNode *node, void *data);
 
 typedef void (*BLI_pbvh_HitCallback)(PBVHNode *node, void *data);
 
@@ -69,7 +68,7 @@ void BLI_pbvh_search_gather(PBVH *bvh,
    hit first */
 
 void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
-                     float ray_start[3], float ray_normal[3]);
+                     float ray_start[3], float ray_normal[3], int original);
 
 /* Node Access */
 
@@ -78,20 +77,25 @@ typedef enum {
 
        PBVH_UpdateNormals = 2,
        PBVH_UpdateBB = 4,
+       PBVH_UpdateOriginalBB = 4,
        PBVH_UpdateDrawBuffers = 8,
        PBVH_UpdateRedraw = 16
 } PBVHNodeFlags;
 
 void BLI_pbvh_node_mark_update(PBVHNode *node);
 
-void BLI_pbvh_node_get_verts(PBVHNode *node, int **vert_indices, int *totvert);
-void BLI_pbvh_node_get_faces(PBVHNode *node, int **face_indices, int *totface);
+void BLI_pbvh_node_get_verts(PBVHNode *node, int **vert_indices,
+       int *totvert, int *allverts);
+void BLI_pbvh_node_get_faces(PBVHNode *node, int **face_indices,
+       int **face_vert_indices, int *totface);
 void *BLI_pbvh_node_get_draw_buffers(PBVHNode *node);
+void BLI_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
+void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
 
 /* Update Normals/Bounding Box/Draw Buffers/Redraw and clear flags */
 
 void BLI_pbvh_update(PBVH *bvh, int flags, float (*face_nors)[3]);
-void BLI_pbvh_redraw_bounding_box(PBVH *bvh, float bb_min[3], float bb_max[3]);
+void BLI_pbvh_redraw_BB(PBVH *bvh, float bb_min[3], float bb_max[3]);
 
 #endif /* BLI_PBVH_H */
 
index 2a1f148..4cf278d 100644 (file)
@@ -82,12 +82,14 @@ struct PBVHNode {
 
        /* Voxel bounds */
        BB vb;
+       BB orig_vb;
 
        /* For internal nodes */
        int children_offset;
 
        /* Pointer into bvh face_indices */
        int *face_indices;
+       int *face_vert_indices;
 
        unsigned short totface;
        unsigned short uniq_verts, face_verts;
@@ -266,7 +268,7 @@ static void grow_nodes(PBVH *bvh, int totnode)
 
 /* Add a vertex to the map, with a positive value for unique vertices and
    a negative value for additional vertices */
-static void map_insert_vert(PBVH *bvh, GHash *map,
+static int map_insert_vert(PBVH *bvh, GHash *map,
                            unsigned short *face_verts,
                            unsigned short *uniq_verts, int vertex)
 {
@@ -284,7 +286,10 @@ static void map_insert_vert(PBVH *bvh, GHash *map,
                }
                
                BLI_ghash_insert(map, key, value);
+               return GET_INT_FROM_POINTER(value);
        }
+       else
+               return GET_INT_FROM_POINTER(BLI_ghash_lookup(map, key));
 }
 
 /* Find vertices used by the faces in this node and update the draw buffers */
@@ -299,13 +304,17 @@ static void build_leaf_node(PBVH *bvh, PBVHNode *node)
        node->uniq_verts = node->face_verts = 0;
        totface= node->totface;
 
+       node->face_vert_indices = MEM_callocN(sizeof(int) *
+                                        4*totface, "bvh node face vert indices");
+
        for(i = 0; i < totface; ++i) {
                MFace *f = bvh->faces + node->face_indices[i];
                int sides = f->v4 ? 4 : 3;
 
                for(j = 0; j < sides; ++j) {
-                       map_insert_vert(bvh, map, &node->face_verts,
-                                       &node->uniq_verts, (&f->v1)[j]);
+                       node->face_vert_indices[i*4 + j]= 
+                               map_insert_vert(bvh, map, &node->face_verts,
+                                               &node->uniq_verts, (&f->v1)[j]);
                }
        }
 
@@ -327,6 +336,10 @@ static void build_leaf_node(PBVH *bvh, PBVHNode *node)
                        GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(iter));
        }
 
+       for(i = 0; i < totface*4; ++i)
+               if(node->face_vert_indices[i] < 0)
+                       node->face_vert_indices[i]= -node->face_vert_indices[i] + node->uniq_verts - 1;
+
        node->draw_buffers =
                GPU_build_buffers(map, bvh->verts, bvh->faces,
                                  node->face_indices,
@@ -370,6 +383,7 @@ void build_sub(PBVH *bvh, int node_index, BB *cb, BBC *prim_bbc,
                }
                
                build_leaf_node(bvh, bvh->nodes + node_index);
+               bvh->nodes[node_index].orig_vb= bvh->nodes[node_index].vb;
 
                /* Done with this subtree */
                return;
@@ -394,6 +408,8 @@ void build_sub(PBVH *bvh, int node_index, BB *cb, BBC *prim_bbc,
                                  (BB*)(prim_bbc + bvh->face_indices[i]));
        }
 
+       bvh->nodes[node_index].orig_vb= bvh->nodes[node_index].vb;
+
        end = partition_indices(bvh->face_indices, offset, offset + count - 1,
                                axis,
                                (cb->bmax[axis] + cb->bmin[axis]) * 0.5f,
@@ -480,6 +496,7 @@ void BLI_pbvh_free(PBVH *bvh)
                if(bvh->nodes[i].flag & PBVH_Leaf) {
                        GPU_free_buffers(bvh->nodes[i].draw_buffers);
                        MEM_freeN(bvh->nodes[i].vert_indices);
+                       MEM_freeN(bvh->nodes[i].face_vert_indices);
                }
        }
 
@@ -562,7 +579,7 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter)
                /* check search callback */
                search_data= iter->search_data;
 
-               if(iter->scb && !iter->scb(node, node->vb.bmin, node->vb.bmax, search_data))
+               if(iter->scb && !iter->scb(node, search_data))
                        continue; /* don't traverse, outside of search zone */
 
                if(node->flag & PBVH_Leaf) {
@@ -634,11 +651,12 @@ void BLI_pbvh_search_callback(PBVH *bvh,
        pbvh_iter_end(&iter);
 }
 
-static int update_search_cb(PBVHNode *node,
-       float bb_min[3], float bb_max[3], void *data_v)
+static int update_search_cb(PBVHNode *node, void *data_v)
 {
+       int flag= GET_INT_FROM_POINTER(data_v);
+
        if(node->flag & PBVH_Leaf)
-               return (node->flag & (PBVH_UpdateNormals|PBVH_UpdateBB|PBVH_UpdateDrawBuffers|PBVH_UpdateRedraw));
+               return (node->flag & flag);
        
        return 1;
 }
@@ -670,7 +688,8 @@ static void pbvh_update_normals(PBVH *bvh, PBVHNode **nodes,
                if((node->flag & PBVH_UpdateNormals)) {
                        int i, j, totface, *faces;
 
-                       BLI_pbvh_node_get_faces(node, &faces, &totface);
+                       faces= node->face_indices;
+                       totface= node->totface;
 
                        for(i = 0; i < totface; ++i) {
                                MFace *f= bvh->faces + faces[i];
@@ -713,7 +732,8 @@ static void pbvh_update_normals(PBVH *bvh, PBVHNode **nodes,
                if(node->flag & PBVH_UpdateNormals) {
                        int i, *verts, totvert;
 
-                       BLI_pbvh_node_get_verts(node, &verts, &totvert);
+                       verts= node->vert_indices;
+                       totvert= node->uniq_verts;
 
                        for(i = 0; i < totvert; ++i) {
                                const int v = verts[i];
@@ -754,6 +774,9 @@ static void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes,
                        /* don't clear flag yet, leave it for flushing later */
                        update_node_vb(bvh, node);
 
+               if((flag & PBVH_UpdateOriginalBB) && (node->flag & PBVH_UpdateOriginalBB))
+                       node->orig_vb= node->vb;
+
                if((flag & PBVH_UpdateRedraw) && (node->flag & PBVH_UpdateRedraw))
                        node->flag &= ~PBVH_UpdateRedraw;
        }
@@ -780,22 +803,32 @@ static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode)
        }
 }
 
-static int pbvh_flush_bb(PBVH *bvh, PBVHNode *node)
+static int pbvh_flush_bb(PBVH *bvh, PBVHNode *node, int flag)
 {
        int update= 0;
 
        /* difficult to multithread well, we just do single threaded recursive */
        if(node->flag & PBVH_Leaf) {
-               update= (node->flag & PBVH_UpdateBB);
-               node->flag &= ~PBVH_UpdateBB;
+               if(flag & PBVH_UpdateBB) {
+                       update |= (node->flag & PBVH_UpdateBB);
+                       node->flag &= ~PBVH_UpdateBB;
+               }
+
+               if(flag & PBVH_UpdateOriginalBB) {
+                       update |= (node->flag & PBVH_UpdateOriginalBB);
+                       node->flag &= ~PBVH_UpdateOriginalBB;
+               }
+
                return update;
        }
        else {
-               update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset);
-               update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset + 1);
+               update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset, flag);
+               update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset + 1, flag);
 
-               if(update)
+               if(update & PBVH_UpdateBB)
                        update_node_vb(bvh, node);
+               if(update & PBVH_UpdateOriginalBB)
+                       node->orig_vb= node->vb;
        }
 
        return update;
@@ -806,24 +839,25 @@ void BLI_pbvh_update(PBVH *bvh, int flag, float (*face_nors)[3])
        PBVHNode **nodes;
        int totnode;
 
-       BLI_pbvh_search_gather(bvh, update_search_cb, NULL, &nodes, &totnode);
+       BLI_pbvh_search_gather(bvh, update_search_cb, SET_INT_IN_POINTER(flag),
+               &nodes, &totnode);
 
        if(flag & PBVH_UpdateNormals)
                pbvh_update_normals(bvh, nodes, totnode, face_nors);
 
-       if(flag & (PBVH_UpdateBB|PBVH_UpdateRedraw))
+       if(flag & (PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw))
                pbvh_update_BB_redraw(bvh, nodes, totnode, flag);
 
        if(flag & PBVH_UpdateDrawBuffers)
                pbvh_update_draw_buffers(bvh, nodes, totnode);
 
-       if(flag & PBVH_UpdateBB)
-               pbvh_flush_bb(bvh, bvh->nodes);
+       if(flag & (PBVH_UpdateBB|PBVH_UpdateOriginalBB))
+               pbvh_flush_bb(bvh, bvh->nodes, flag);
 
        if(nodes) MEM_freeN(nodes);
 }
 
-void BLI_pbvh_redraw_bounding_box(PBVH *bvh, float bb_min[3], float bb_max[3])
+void BLI_pbvh_redraw_BB(PBVH *bvh, float bb_min[3], float bb_max[3])
 {
        PBVHIter iter;
        PBVHNode *node;
@@ -847,19 +881,21 @@ void BLI_pbvh_redraw_bounding_box(PBVH *bvh, float bb_min[3], float bb_max[3])
 
 void BLI_pbvh_node_mark_update(PBVHNode *node)
 {
-       node->flag |= PBVH_UpdateNormals|PBVH_UpdateBB|PBVH_UpdateDrawBuffers|PBVH_UpdateRedraw;
+       node->flag |= PBVH_UpdateNormals|PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateDrawBuffers|PBVH_UpdateRedraw;
 }
 
-void BLI_pbvh_node_get_verts(PBVHNode *node, int **vert_indices, int *totvert)
+void BLI_pbvh_node_get_verts(PBVHNode *node, int **vert_indices, int *totvert, int *allvert)
 {
-       *vert_indices= node->vert_indices;
-       *totvert= node->uniq_verts;
+       if(vert_indices) *vert_indices= node->vert_indices;
+       if(totvert) *totvert= node->uniq_verts;
+       if(allvert) *allvert= node->uniq_verts + node->face_verts;
 }
 
-void BLI_pbvh_node_get_faces(PBVHNode *node, int **face_indices, int *totface)
+void BLI_pbvh_node_get_faces(PBVHNode *node, int **face_indices, int **face_vert_indices, int *totface)
 {
-       *face_indices= node->face_indices;
-       *totface= node->totface;
+       if(face_indices) *face_indices= node->face_indices;
+       if(face_vert_indices) *face_vert_indices= node->face_vert_indices;
+       if(totface) *totface= node->totface;
 }
 
 void *BLI_pbvh_node_get_draw_buffers(PBVHNode *node)
@@ -867,6 +903,18 @@ void *BLI_pbvh_node_get_draw_buffers(PBVHNode *node)
        return node->draw_buffers;
 }
 
+void BLI_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3])
+{
+       VecCopyf(bb_min, node->vb.bmin);
+       VecCopyf(bb_max, node->vb.bmax);
+}
+
+void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3])
+{
+       VecCopyf(bb_min, node->orig_vb.bmin);
+       VecCopyf(bb_max, node->orig_vb.bmax);
+}
+
 /********************************* Raycast ***********************************/
 
 typedef struct {
@@ -874,15 +922,21 @@ typedef struct {
        float start[3];
        int sign[3];
        float inv_dir[3];
+       int original;
 } RaycastData;
 
 /* Adapted from here: http://www.gamedev.net/community/forums/topic.asp?topic_id=459973 */
-static int ray_aabb_intersect(PBVHNode *node, float bb_min[3], float bb_max[3], void *data_v)
+static int ray_aabb_intersect(PBVHNode *node, void *data_v)
 {
        RaycastData *ray = data_v;
-       float bbox[2][3];
+       float bb_min[3], bb_max[3], bbox[2][3];
        float tmin, tmax, tymin, tymax, tzmin, tzmax;
 
+       if(ray->original)
+               BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
+       else
+               BLI_pbvh_node_get_BB(node, bb_min, bb_max);
+
        VecCopyf(bbox[0], bb_min);
        VecCopyf(bbox[1], bb_max);
 
@@ -918,7 +972,7 @@ static int ray_aabb_intersect(PBVHNode *node, float bb_min[3], float bb_max[3],
 }
 
 void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
-                     float ray_start[3], float ray_normal[3])
+                     float ray_start[3], float ray_normal[3], int original)
 {
        RaycastData rcd;
 
@@ -929,6 +983,7 @@ void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
        rcd.sign[0] = rcd.inv_dir[0] < 0;
        rcd.sign[1] = rcd.inv_dir[1] < 0;
        rcd.sign[2] = rcd.inv_dir[2] < 0;
+       rcd.original = original;
 
        BLI_pbvh_search_callback(bvh, ray_aabb_intersect, &rcd, cb, data);
 }
index 9bc6cac..05f2b56 100644 (file)
@@ -191,10 +191,14 @@ void undo_paint_push_begin(int type, char *name, UndoRestoreCb restore, UndoFree
 
 ListBase *undo_paint_push_get_list(int type)
 {
-       if(type == UNDO_PAINT_IMAGE)
-               return &ImageUndoStack.current->elems;
-       else if(type == UNDO_PAINT_MESH)
-               return &MeshUndoStack.current->elems;
+       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;
 }
index fd61455..d307b08 100644 (file)
  *
  */
 
-/* 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,
@@ -155,7 +146,6 @@ typedef struct StrokeCache {
        ViewContext *vc;
        Brush *brush;
 
-       short (*orig_norms)[3]; /* Copy of the mesh vertices' normals */
        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;
@@ -167,6 +157,7 @@ typedef struct StrokeCache {
        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;
 
 /* ===== OPENGL =====
@@ -206,7 +197,7 @@ int sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d,
 
        view3d_get_object_project_mat(rv3d, ob, pmat);
 
-       BLI_pbvh_redraw_bounding_box(ob->sculpt->tree, bb_min, bb_max);
+       BLI_pbvh_redraw_BB(ob->sculpt->tree, bb_min, bb_max);
 
        rect->xmin = rect->ymin = INT_MAX;
        rect->xmax = rect->ymax = INT_MIN;
@@ -280,6 +271,7 @@ typedef struct SculptUndoNode {
        void *node;                                     /* only during push, not valid afterwards! */
 
        float (*co)[3];
+       short (*no)[3];
        int *index;
        int totvert;
 } SculptUndoNode;
@@ -329,7 +321,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
                   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, NULL);
+               BLI_pbvh_update(ss->tree, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL);
 
                /* not really convinced this is correct .. */
                if((mmd=sculpt_multires_active(ob))) {
@@ -350,28 +342,43 @@ static void sculpt_undo_free(ListBase *lb)
        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);
        }
 }
 
-static float (*sculpt_undo_push_node(SculptSession *ss, PBVHNode *node))[3]
+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 i, totvert, *verts;
+       int i, totvert, allvert, *verts;
 
-       BLI_pbvh_node_get_verts(node, &verts, &totvert);
+       BLI_pbvh_node_get_verts(node, &verts, &totvert, &allvert);
 
        /* list is manipulated by multiple threads, so we lock */
        BLI_lock_thread(LOCK_CUSTOM1);
 
-       for(unode=lb->first; unode; unode=unode->next) {
-               if(unode->node == node && strcmp(unode->idname, ob->id.name)==0) {
-                       BLI_unlock_thread(LOCK_CUSTOM1);
-                       return unode->co;
-               }
+       if((unode= sculpt_undo_get_node(ss, node))) {
+               BLI_unlock_thread(LOCK_CUSTOM1);
+               return unode;
        }
 
        unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode");
@@ -381,19 +388,44 @@ static float (*sculpt_undo_push_node(SculptSession *ss, PBVHNode *node))[3]
        unode->totvert= totvert;
        unode->maxvert= ss->totvert;
        /* we will use this while sculpting, is mapalloc slow to access then? */
-       unode->co= MEM_mapallocN(sizeof(float)*3*totvert, "SculptUndoNode.co");
-       unode->index= MEM_mapallocN(sizeof(int)*totvert, "SculptUndoNode.index");
-       undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(int))*totvert);
+       unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co");
+       unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no");
+       unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index");
+       undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert);
        BLI_addtail(lb, unode);
 
        BLI_unlock_thread(LOCK_CUSTOM1);
 
        /* copy threaded, hopefully this is the performance critical part */
-       memcpy(unode->index, verts, sizeof(int)*totvert);
-       for(i=0; i<totvert; i++)
+       memcpy(unode->index, verts, sizeof(int)*allvert);
+       for(i=0; i<allvert; i++) {
                VECCOPY(unode->co[i], ss->mvert[verts[i]].co)
+               VECCOPY(unode->no[i], ss->mvert[verts[i]].no)
+       }
        
-       return unode->co;
+       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);
 }
 
 /************************ Looping Over Verts in a BVH Node *******************/
@@ -422,7 +454,7 @@ static void sculpt_node_verts_init(Sculpt *sd, SculptSession *ss,
        vd->mvert= ss->mvert;
        vd->origvert= origvert;
        vd->i= -1;
-       BLI_pbvh_node_get_verts(node, &vd->verts, &vd->totvert);
+       BLI_pbvh_node_get_verts(node, &vd->verts, &vd->totvert, NULL);
 }
 
 static int sculpt_node_verts_next(SculptVertexData *vd)
@@ -628,14 +660,16 @@ typedef struct {
 } SculptSearchSphereData;
 
 /* Test AABB against sphere */
-static int sculpt_search_sphere_cb(PBVHNode *node,
-       float bb_min[3], float bb_max[3], void *data_v)
+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];
+       float t[3], bb_min[3], bb_max[3];
        int i;
 
+       //BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
+       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];
@@ -682,7 +716,6 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], cons
 /* 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);
        StrokeCache *cache = ss->cache;
        const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */
        float out[3] = {0.0f, 0.0f, 0.0f};
@@ -696,24 +729,30 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3]
        #pragma omp parallel for private(n) schedule(static)
        for(n=0; n<totnode; n++) {
                SculptVertexData vd;
+               SculptUndoNode *unode;
                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_node_verts_init(sd, ss, nodes[n], NULL, &vd);
 
-               if(brush->flag & BRUSH_ANCHORED) {
+               if(unode && ss->cache->original) {
                        while(sculpt_node_verts_next(&vd))
-                               add_norm_if(out_dir, nout, nout_flip, cache->orig_norms[vd.index]);
+                               add_norm_if(out_dir, nout, nout_flip, unode->no[vd.i]);
                }
                else {
                        while(sculpt_node_verts_next(&vd))
                                add_norm_if(out_dir, nout, nout_flip, ss->mvert[vd.index].no);
                }
 
-               /* we sum per node and add together later for threads */
-               #pragma omp critical
-               VecAddf(out, out, nout);
-               VecAddf(out_flip, out_flip, nout_flip);
+               {
+                       /* we sum per node and add together later for threads */
+                       #pragma omp critical
+                       VecAddf(out, out, nout);
+                       VecAddf(out_flip, out_flip, nout_flip);
+               }
        }
 
        if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
@@ -875,7 +914,7 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
 {
        Brush *brush = paint_brush(&sd->paint);
        float bstrength= ss->cache->bstrength;
-       float grab_delta[3], (*origco)[3];
+       float grab_delta[3];
        int n;
        
        VecCopyf(grab_delta, ss->cache->grab_delta_symmetry);
@@ -883,8 +922,11 @@ static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int t
        #pragma omp parallel for private(n) schedule(static)
        for(n=0; n<totnode; n++) {
                SculptVertexData vd;
+               SculptUndoNode *unode;
+               float (*origco)[3];
                
-               origco= sculpt_undo_push_node(ss, nodes[n]);
+               unode= sculpt_undo_push_node(ss, nodes[n]);
+               origco= unode->co;
                sculpt_node_verts_init(sd, ss, nodes[n], origco, &vd);
 
                while(sculpt_node_verts_next(&vd)) {
@@ -921,9 +963,11 @@ static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
        #pragma omp parallel for private(n) schedule(static)
        for(n=0; n<totnode; n++) {
                SculptVertexData vd;
+               SculptUndoNode *unode;
                float (*origco)[3];
                
-               origco= sculpt_undo_push_node(ss, nodes[n]);
+               unode= sculpt_undo_push_node(ss, nodes[n]);
+               origco= unode->co;
                sculpt_node_verts_init(sd, ss, nodes[n], NULL, &vd);
 
                while(sculpt_node_verts_next(&vd)) {
@@ -1422,8 +1466,6 @@ static float unproject_brush_radius(ViewContext *vc, float center[3], float offs
 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)
@@ -1479,21 +1521,20 @@ static void sculpt_update_cache_invariants(Sculpt *sd, SculptSession *ss, bConte
 
        /* Make copies of the mesh vertex locations and normals for some tools */
        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)
                                VecCopyf(cache->face_norms[i], fn);
                }
+
+               cache->original = 1;
        }
 
+       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;
 }
@@ -1601,15 +1642,47 @@ typedef struct {
        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;
        MVert *vert = srd->ss->mvert;
-       int i, totface, *faces;
+       int i, totface, *faces, *face_verts;
+
+       if(srd->original && srd->ss->cache) {
+               SculptUndoNode *unode;
 
-       BLI_pbvh_node_get_faces(node, &faces, &totface);
+               unode= sculpt_undo_get_node(srd->ss, node);
+
+               if(unode) {
+                       /* intersect with coordinates from before we started stroke */
+                       BLI_pbvh_node_get_faces(node, &faces, &face_verts, &totface);
+
+                       for(i = 0; i < totface; ++i) {
+                               MFace *f = srd->ss->mface + faces[i];
+                               /*if(face_verts[i*4 + 0] >= unode->totvert) abort();
+                               if(face_verts[i*4 + 1] >= unode->totvert) abort();
+                               if(face_verts[i*4 + 2] >= unode->totvert) abort();
+                               if(f->v4 && face_verts[i*4 + 3] >= unode->totvert) abort();*/
+
+                               if(ray_face_intersection(srd->ray_start, srd->ray_normal,
+                                                        unode->co[face_verts[i*4+0]],
+                                                        unode->co[face_verts[i*4+1]],
+                                                        unode->co[face_verts[i*4+2]],
+                                                        f->v4? unode->co[face_verts[i*4+3]]: NULL,
+                                                        &srd->dist)) {
+                                       srd->hit = faces[i];
+                               }
+                       }
+
+                       return;
+               }
+       }
+
+       /* intersect with current coordinates */
+       BLI_pbvh_node_get_faces(node, &faces, NULL, &totface);
 
        for(i = 0; i < totface; ++i) {
                MFace *f = srd->ss->mface + faces[i];
@@ -1631,6 +1704,8 @@ void sculpt_raycast_cb(PBVHNode *node, void *data_v)
 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 mval[2] = {mouse[0] - vc->ar->winrct.xmin,
                         mouse[1] - vc->ar->winrct.ymin};
@@ -1643,8 +1718,9 @@ int sculpt_stroke_get_location(bContext *C, struct PaintStroke *stroke, float ou
        srd.ray_normal = ray_normal;
        srd.dist = FLT_MAX;
        srd.hit = -1;
-       BLI_pbvh_raycast(vc->obact->sculpt->tree, sculpt_raycast_cb, &srd,
-                    ray_start, ray_normal);
+       srd.original = (cache)? cache->original: 0;
+       BLI_pbvh_raycast(ss->tree, sculpt_raycast_cb, &srd,
+                    ray_start, ray_normal, srd.original);
        
        VecCopyf(out, ray_normal);
        VecMulf(out, srd.dist);
@@ -1713,7 +1789,7 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
        int i;
 
        /* Restore the mesh before continuing with anchored stroke */
-       if((brush->flag & BRUSH_ANCHORED) && cache->orig_norms) {
+       if(brush->flag & BRUSH_ANCHORED) {
                ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
                SculptUndoNode *unode;
 
@@ -1721,17 +1797,14 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
 
                for(unode = lb->first; unode; unode = unode->next) {
                        float (*co)[3]= unode->co;
+                       short (*no)[3]= unode->no;
                        int *index= unode->index;
                        int totvert= unode->totvert;
 
-                       for(i = 0; i < totvert; ++i)
+                       for(i = 0; i < totvert; ++i) {
                                VECCOPY(ss->mvert[index[i]].co, co[i]);
-               }
-
-               for(i = 0; i < ss->totvert; ++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];
+                               VECCOPY(ss->mvert[index[i]].no, no[i]);
+                       }
                }
 
                if(ss->face_normals) {
@@ -1796,8 +1869,7 @@ static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent
 
                sculpt_update_cache_invariants(sd, ss, C, op);
 
-               undo_paint_push_begin(UNDO_PAINT_MESH, sculpt_tool_name(sd),
-                       sculpt_undo_restore, sculpt_undo_free);
+               sculpt_undo_push_begin(ss, sculpt_tool_name(sd));
 
                return 1;
        }
@@ -1824,13 +1896,12 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke)
 
        /* Finished */
        if(ss->cache) {
-               // Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
-
                sculpt_cache_free(ss->cache);
                ss->cache = NULL;
 
-               undo_paint_push_end(UNDO_PAINT_MESH);
-               // XXX ED_undo_push(C, sculpt_tool_name(sd));
+               sculpt_undo_push_end(ss);
+
+               BLI_pbvh_update(ss->tree, PBVH_UpdateOriginalBB, NULL);
        }
 }
 
@@ -1868,8 +1939,6 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
        sculpt_flush_update(C);
        sculpt_cache_free(ss->cache);
 
-       // XXX ED_undo_push(C, sculpt_tool_name(sd));
-
        return OPERATOR_FINISHED;
 }
 
index 1bbccd2..c8fc14c 100644 (file)
@@ -79,12 +79,13 @@ typedef struct Brush {
 #define BRUSH_JITTER_PRESSURE  16 /* was BRUSH_RAD_PRESSURE */
 #define BRUSH_SPACING_PRESSURE 32
 #define BRUSH_FIXED_TEX                        64
-#define BRUSH_RAKE             128
-#define BRUSH_ANCHORED         256
-#define BRUSH_DIR_IN           512
-#define BRUSH_SPACE            1024
-#define BRUSH_SMOOTH_STROKE    2048
-#define BRUSH_PERSISTENT       4096
+#define BRUSH_RAKE                             128
+#define BRUSH_ANCHORED                 256
+#define BRUSH_DIR_IN                   512
+#define BRUSH_SPACE                            1024
+#define BRUSH_SMOOTH_STROKE            2048
+#define BRUSH_PERSISTENT               4096
+#define BRUSH_ACCUMULATE               8192
 
 /* Brush.sculpt_tool */
 #define SCULPT_TOOL_DRAW    1
index e9744c7..714526e 100644 (file)
@@ -273,6 +273,11 @@ static void rna_def_brush(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_PERSISTENT);
        RNA_def_property_ui_text(prop, "Persistent", "Sculpts on a persistent layer of the mesh.");
        RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+       prop= RNA_def_property(srna, "use_accumulate", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ACCUMULATE);
+       RNA_def_property_ui_text(prop, "Accumulate", "Accumulate stroke dabs on top of each other.");
+       RNA_def_property_update(prop, 0, "rna_Brush_update");
        
        /* not exposed in the interface yet
        prop= RNA_def_property(srna, "fixed_tex", PROP_BOOLEAN, PROP_NONE);