Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_undo.c
| Show All 38 Lines | |||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "DNA_workspace_types.h" | #include "DNA_workspace_types.h" | ||||
| #include "BKE_ccg.h" | #include "BKE_ccg.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_customdata.h" | |||||
| #include "BKE_multires.h" | #include "BKE_multires.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_key.h" | #include "BKE_key.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_mesh_runtime.h" | #include "BKE_mesh_runtime.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_subsurf.h" | #include "BKE_subsurf.h" | ||||
| #include "BKE_subdiv_ccg.h" | #include "BKE_subdiv_ccg.h" | ||||
| ▲ Show 20 Lines • Show All 365 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(SculptUndoNode *unode, Object *ob) | |||||
| { | |||||
| Mesh *me; | |||||
| sculpt_pbvh_clear(ob); | |||||
| me = ob->data; | |||||
| CustomData_free(&me->vdata, me->totvert); | |||||
| CustomData_free(&me->edata, me->totedge); | |||||
| CustomData_free(&me->fdata, me->totface); | |||||
| CustomData_free(&me->ldata, me->totloop); | |||||
brecht: I think it would be better to make a nomain copy of the Mesh datablock, and then restore it… | |||||
| 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(unode->next, ob); | |||||
| unode->next->applied = true; | |||||
| unode->applied = false; | |||||
| } | |||||
| else { | |||||
| sculpt_undo_geometry_restore(unode, ob); | |||||
| 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 71 Lines • ▼ Show 20 Lines | while (unode != NULL) { | ||||
| if (unode->mask) { | if (unode->mask) { | ||||
| MEM_freeN(unode->mask); | MEM_freeN(unode->mask); | ||||
| } | } | ||||
| if (unode->bm_entry) { | if (unode->bm_entry) { | ||||
| BM_log_entry_drop(unode->bm_entry); | BM_log_entry_drop(unode->bm_entry); | ||||
| } | } | ||||
| if (unode->bm_enter_totvert) { | if (unode->geom_totvert) { | ||||
| CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); | CustomData_free(&unode->geom_vdata, unode->geom_totvert); | ||||
| } | } | ||||
| if (unode->bm_enter_totedge) { | if (unode->geom_totedge) { | ||||
| CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); | CustomData_free(&unode->geom_edata, unode->geom_totedge); | ||||
| } | } | ||||
| if (unode->bm_enter_totloop) { | if (unode->geom_totloop) { | ||||
| CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); | CustomData_free(&unode->geom_ldata, unode->geom_totloop); | ||||
| } | } | ||||
| if (unode->bm_enter_totpoly) { | if (unode->geom_totpoly) { | ||||
| CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); | CustomData_free(&unode->geom_pdata, unode->geom_totpoly); | ||||
| } | } | ||||
| MEM_freeN(unode); | MEM_freeN(unode); | ||||
| unode = unode_next; | unode = unode_next; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | case SCULPT_UNDO_MASK: | ||||
| usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; | usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; | ||||
| 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"); | ||||
| case SCULPT_UNDO_GEOMETRY: | |||||
| break; | break; | ||||
| } | } | ||||
| BLI_addtail(&usculpt->nodes, unode); | BLI_addtail(&usculpt->nodes, unode); | ||||
| if (maxgrid) { | if (maxgrid) { | ||||
| /* multires */ | /* multires */ | ||||
| unode->maxgrid = maxgrid; | unode->maxgrid = maxgrid; | ||||
| ▲ Show 20 Lines • Show All 65 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 All 12 Lines | else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { | ||||
| Mesh *me = ob->data; | Mesh *me = ob->data; | ||||
| /* Store a copy of the mesh's current vertices, loops, and | /* Store a copy of the mesh's current vertices, loops, and | ||||
| * polys. A full copy like this is needed because entering | * polys. A full copy like this is needed because entering | ||||
| * dynamic-topology immediately does topological edits | * dynamic-topology immediately does topological edits | ||||
| * (converting polys to triangles) that the BMLog can't | * (converting polys to triangles) that the BMLog can't | ||||
| * fully restore from */ | * fully restore from */ | ||||
| CustomData_copy( | CustomData_copy( | ||||
| &me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); | &me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); | ||||
| CustomData_copy( | CustomData_copy( | ||||
| &me->edata, &unode->bm_enter_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); | &me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); | ||||
| CustomData_copy( | CustomData_copy( | ||||
| &me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); | &me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); | ||||
| CustomData_copy( | CustomData_copy( | ||||
| &me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); | &me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); | ||||
| unode->bm_enter_totvert = me->totvert; | unode->geom_totvert = me->totvert; | ||||
| unode->bm_enter_totedge = me->totedge; | unode->geom_totedge = me->totedge; | ||||
| unode->bm_enter_totloop = me->totloop; | unode->geom_totloop = me->totloop; | ||||
| unode->bm_enter_totpoly = me->totpoly; | unode->geom_totpoly = me->totpoly; | ||||
| unode->bm_entry = BM_log_entry_add(ss->bm_log); | unode->bm_entry = BM_log_entry_add(ss->bm_log); | ||||
| BM_log_all_added(ss->bm, ss->bm_log); | BM_log_all_added(ss->bm, ss->bm_log); | ||||
| } | } | ||||
| else { | else { | ||||
| unode->bm_entry = BM_log_entry_add(ss->bm_log); | unode->bm_entry = BM_log_entry_add(ss->bm_log); | ||||
| } | } | ||||
| Show All 27 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 All 23 Lines | case SCULPT_UNDO_HIDDEN: | ||||
| break; | break; | ||||
| case SCULPT_UNDO_MASK: | case SCULPT_UNDO_MASK: | ||||
| sculpt_undo_store_mask(ob, unode); | sculpt_undo_store_mask(ob, unode); | ||||
| 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"); | ||||
| case SCULPT_UNDO_GEOMETRY: | |||||
| break; | break; | ||||
| } | } | ||||
| /* store active shape key */ | /* store active shape key */ | ||||
| if (ss->kb) { | if (ss->kb) { | ||||
| BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); | BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); | ||||
| } | } | ||||
| else { | else { | ||||
| ▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static void sculpt_undosys_step_free(UndoStep *us_p) | static void sculpt_undosys_step_free(UndoStep *us_p) | ||||
| { | { | ||||
| SculptUndoStep *us = (SculptUndoStep *)us_p; | SculptUndoStep *us = (SculptUndoStep *)us_p; | ||||
| sculpt_undo_free_list(&us->data.nodes); | sculpt_undo_free_list(&us->data.nodes); | ||||
| } | } | ||||
| void ED_sculpt_undo_geometry_begin(struct Object *ob) | |||||
| { | |||||
| sculpt_undo_push_begin("voxel remesh"); | |||||
| sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); | |||||
| } | |||||
| void ED_sculpt_undo_geometry_end(struct Object *ob) | |||||
| { | |||||
| sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); | |||||
| sculpt_undo_push_end(); | |||||
| } | |||||
| /* Export for ED_undo_sys. */ | /* Export for ED_undo_sys. */ | ||||
| void ED_sculpt_undosys_type(UndoType *ut) | void ED_sculpt_undosys_type(UndoType *ut) | ||||
| { | { | ||||
| ut->name = "Sculpt"; | ut->name = "Sculpt"; | ||||
| ut->poll = sculpt_undosys_poll; | ut->poll = sculpt_undosys_poll; | ||||
| ut->step_encode_init = sculpt_undosys_step_encode_init; | ut->step_encode_init = sculpt_undosys_step_encode_init; | ||||
| ut->step_encode = sculpt_undosys_step_encode; | ut->step_encode = sculpt_undosys_step_encode; | ||||
| ut->step_decode = sculpt_undosys_step_decode; | ut->step_decode = sculpt_undosys_step_decode; | ||||
| Show All 27 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.