Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_undo.c
| Show First 20 Lines • Show All 419 Lines • ▼ Show 20 Lines | static void sculpt_undo_bmesh_restore_end(bContext *C, | ||||
| } | } | ||||
| else { | else { | ||||
| /* Disable dynamic topology sculpting */ | /* Disable dynamic topology sculpting */ | ||||
| sculpt_dynamic_topology_disable(C, NULL); | sculpt_dynamic_topology_disable(C, NULL); | ||||
| unode->applied = true; | unode->applied = true; | ||||
| } | } | ||||
| } | } | ||||
| static void sculpt_undo_geometry_restore(bContext *C, | |||||
| SculptUndoNode *unode, | |||||
| Object *ob, | |||||
| SculptSession *ss) | |||||
| { | |||||
| Mesh *me; | |||||
| sculpt_pbvh_clear(ob); | |||||
| me = ob->data; | |||||
| CustomData_free(&me->vdata, me->totvert); | |||||
brecht: I think it would be better to make a nomain copy of the Mesh datablock, and then restore it… | |||||
| CustomData_free(&me->edata, me->totedge); | |||||
| CustomData_free(&me->fdata, me->totface); | |||||
| CustomData_free(&me->ldata, me->totloop); | |||||
| CustomData_free(&me->pdata, me->totpoly); | |||||
| me->totvert = unode->geom_totvert; | |||||
| me->totedge = unode->geom_totedge; | |||||
| me->totloop = unode->geom_totloop; | |||||
| me->totpoly = unode->geom_totpoly; | |||||
| me->totface = 0; | |||||
| CustomData_copy( | |||||
| &unode->geom_vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, unode->geom_totvert); | |||||
| CustomData_copy( | |||||
| &unode->geom_edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, unode->geom_totedge); | |||||
| CustomData_copy( | |||||
| &unode->geom_ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, unode->geom_totloop); | |||||
| CustomData_copy( | |||||
| &unode->geom_pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, unode->geom_totpoly); | |||||
| BKE_mesh_update_customdata_pointers(me, false); | |||||
| } | |||||
| /* Handle all dynamic-topology updates | /* Handle all dynamic-topology updates | ||||
| * | * | ||||
| * Returns true if this was a dynamic-topology undo step, otherwise | * Returns true if this was a dynamic-topology undo step, otherwise | ||||
| * returns false to indicate the non-dyntopo code should run. */ | * returns false to indicate the non-dyntopo code should run. */ | ||||
| static int sculpt_undo_bmesh_restore(bContext *C, | static int sculpt_undo_bmesh_restore(bContext *C, | ||||
| SculptUndoNode *unode, | SculptUndoNode *unode, | ||||
| Object *ob, | Object *ob, | ||||
| SculptSession *ss) | SculptSession *ss) | ||||
| { | { | ||||
| switch (unode->type) { | switch (unode->type) { | ||||
| case SCULPT_UNDO_DYNTOPO_BEGIN: | case SCULPT_UNDO_DYNTOPO_BEGIN: | ||||
| sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); | sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); | ||||
| return true; | return true; | ||||
| case SCULPT_UNDO_DYNTOPO_END: | case SCULPT_UNDO_DYNTOPO_END: | ||||
| sculpt_undo_bmesh_restore_end(C, unode, ob, ss); | sculpt_undo_bmesh_restore_end(C, unode, ob, ss); | ||||
| return true; | return true; | ||||
| default: | default: | ||||
| if (ss->bm_log) { | if (ss->bm_log) { | ||||
| sculpt_undo_bmesh_restore_generic(C, unode, ob, ss); | sculpt_undo_bmesh_restore_generic(C, unode, ob, ss); | ||||
| return true; | return true; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| Show All 21 Lines | if (STREQ(unode->idname, ob->id.name)) { | ||||
| need_mask = true; | need_mask = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); | DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); | ||||
| if (lb->first) { | |||||
| unode = lb->first; | |||||
| if (unode->type == SCULPT_UNDO_GEOMETRY) { | |||||
| if (unode->applied) { | |||||
| sculpt_undo_geometry_restore(C, unode->next, ob, ss); | |||||
| unode->next->applied = true; | |||||
| unode->applied = false; | |||||
| } | |||||
| else { | |||||
| sculpt_undo_geometry_restore(C, unode, ob, ss); | |||||
| unode->next->applied = false; | |||||
| unode->applied = true; | |||||
| } | |||||
| BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); | |||||
| return; | |||||
| } | |||||
| } | |||||
| BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); | BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask); | ||||
| if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) { | if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) { | ||||
| return; | return; | ||||
| } | } | ||||
| for (unode = lb->first; unode; unode = unode->next) { | for (unode = lb->first; unode; unode = unode->next) { | ||||
| if (!STREQ(unode->idname, ob->id.name)) { | if (!STREQ(unode->idname, ob->id.name)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* check if undo data matches current data well enough to | /* check if undo data matches current data well enough to | ||||
| * continue */ | * continue */ | ||||
| if (unode->maxvert) { | if (unode->maxvert) { | ||||
| if (ss->totvert != unode->maxvert) { | if (ss->totvert != unode->maxvert) { | ||||
| Show All 27 Lines | switch (unode->type) { | ||||
| } | } | ||||
| break; | break; | ||||
| case SCULPT_UNDO_DYNTOPO_BEGIN: | case SCULPT_UNDO_DYNTOPO_BEGIN: | ||||
| case SCULPT_UNDO_DYNTOPO_END: | case SCULPT_UNDO_DYNTOPO_END: | ||||
| case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: | case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: | ||||
| BLI_assert(!"Dynamic topology should've already been handled"); | BLI_assert(!"Dynamic topology should've already been handled"); | ||||
| break; | break; | ||||
| case SCULPT_UNDO_GEOMETRY: | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| if (update || rebuild) { | if (update || rebuild) { | ||||
| bool tag_update = false; | bool tag_update = false; | ||||
| /* we update all nodes still, should be more clever, but also | /* we update all nodes still, should be more clever, but also | ||||
| * needs to work correct when exiting/entering sculpt mode and | * needs to work correct when exiting/entering sculpt mode and | ||||
| * the nodes get recreated, though in that case it could do all */ | * the nodes get recreated, though in that case it could do all */ | ||||
| ▲ Show 20 Lines • Show All 278 Lines • ▼ Show 20 Lines | static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) | ||||
| BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) | BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) | ||||
| { | { | ||||
| unode->mask[vd.i] = *vd.mask; | unode->mask[vd.i] = *vd.mask; | ||||
| } | } | ||||
| BKE_pbvh_vertex_iter_end; | BKE_pbvh_vertex_iter_end; | ||||
| } | } | ||||
| static SculptUndoNode *sculpt_undo_geometry_push(Object *ob, SculptUndoType type) | |||||
| { | |||||
| UndoSculpt *usculpt = sculpt_undo_get_nodes(); | |||||
| Mesh *me = ob->data; | |||||
| bool applied; | |||||
| SculptUndoNode *unode = usculpt->nodes.first; | |||||
| /* Store the original mesh in the first node, modifications in the second */ | |||||
| applied = unode != NULL; | |||||
| unode = MEM_callocN(sizeof(*unode), __func__); | |||||
| BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); | |||||
| unode->type = type; | |||||
| unode->applied = applied; | |||||
| CustomData_copy(&me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); | |||||
| CustomData_copy(&me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); | |||||
| CustomData_copy(&me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); | |||||
| CustomData_copy(&me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); | |||||
| unode->geom_totvert = me->totvert; | |||||
| unode->geom_totedge = me->totedge; | |||||
| unode->geom_totloop = me->totloop; | |||||
| unode->geom_totpoly = me->totpoly; | |||||
| BLI_addtail(&usculpt->nodes, unode); | |||||
| return unode; | |||||
| } | |||||
| static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) | static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) | ||||
| { | { | ||||
| UndoSculpt *usculpt = sculpt_undo_get_nodes(); | UndoSculpt *usculpt = sculpt_undo_get_nodes(); | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| SculptUndoNode *unode = usculpt->nodes.first; | SculptUndoNode *unode = usculpt->nodes.first; | ||||
| ▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | switch (type) { | ||||
| BM_log_face_modified(ss->bm_log, f); | BM_log_face_modified(ss->bm_log, f); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case SCULPT_UNDO_DYNTOPO_BEGIN: | case SCULPT_UNDO_DYNTOPO_BEGIN: | ||||
| case SCULPT_UNDO_DYNTOPO_END: | case SCULPT_UNDO_DYNTOPO_END: | ||||
| case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: | case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: | ||||
| case SCULPT_UNDO_GEOMETRY: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return unode; | return unode; | ||||
| } | } | ||||
| SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) | SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| SculptUndoNode *unode; | SculptUndoNode *unode; | ||||
| /* list is manipulated by multiple threads, so we lock */ | /* list is manipulated by multiple threads, so we lock */ | ||||
| BLI_thread_lock(LOCK_CUSTOM1); | BLI_thread_lock(LOCK_CUSTOM1); | ||||
| if (ss->bm || ELEM(type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { | if (ss->bm || ELEM(type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { | ||||
| /* Dynamic topology stores only one undo node per stroke, | /* Dynamic topology stores only one undo node per stroke, | ||||
| * regardless of the number of PBVH nodes modified */ | * regardless of the number of PBVH nodes modified */ | ||||
| unode = sculpt_undo_bmesh_push(ob, node, type); | unode = sculpt_undo_bmesh_push(ob, node, type); | ||||
| BLI_thread_unlock(LOCK_CUSTOM1); | BLI_thread_unlock(LOCK_CUSTOM1); | ||||
| return unode; | return unode; | ||||
| } | } | ||||
| else if (type == SCULPT_UNDO_GEOMETRY) { | |||||
| unode = sculpt_undo_geometry_push(ob, type); | |||||
| BLI_thread_unlock(LOCK_CUSTOM1); | |||||
| return unode; | |||||
| } | |||||
| else if ((unode = sculpt_undo_get_node(node))) { | else if ((unode = sculpt_undo_get_node(node))) { | ||||
| BLI_thread_unlock(LOCK_CUSTOM1); | BLI_thread_unlock(LOCK_CUSTOM1); | ||||
| return unode; | return unode; | ||||
| } | } | ||||
| unode = sculpt_undo_alloc_node(ob, node, type); | unode = sculpt_undo_alloc_node(ob, node, type); | ||||
| /* NOTE: If this ever becomes a bottleneck, make a lock inside of the node. | /* NOTE: If this ever becomes a bottleneck, make a lock inside of the node. | ||||
| ▲ Show 20 Lines • Show All 261 Lines • Show Last 20 Lines | |||||
I think it would be better to make a nomain copy of the Mesh datablock, and then restore it with BKE_mesh_nomain_to_mesh.
It's not clear to me that just restoring the data layers is safe.