Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
| Show First 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | static void filter_cache_init_task_cb(void *__restrict userdata, | ||||
| const TaskParallelTLS *__restrict UNUSED(tls)) | const TaskParallelTLS *__restrict UNUSED(tls)) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = userdata; | ||||
| PBVHNode *node = data->nodes[i]; | PBVHNode *node = data->nodes[i]; | ||||
| SCULPT_undo_push_node(data->ob, node, data->filter_undo_type); | SCULPT_undo_push_node(data->ob, node, data->filter_undo_type); | ||||
| } | } | ||||
| void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int undo_type) | void SCULPT_filter_cache_init(bContext *C, | ||||
| Object *ob, | |||||
| Sculpt *sd, | |||||
| const int undo_type, | |||||
| const int mval[2], | |||||
| float area_normal_radius) | |||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| PBVH *pbvh = ob->sculpt->pbvh; | PBVH *pbvh = ob->sculpt->pbvh; | ||||
| ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); | ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache"); | ||||
| ss->filter_cache->random_seed = rand(); | ss->filter_cache->random_seed = rand(); | ||||
| ▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | void SCULPT_filter_cache_init(bContext *C, | ||||
| copy_m4_m4(ss->filter_cache->obmat, ob->obmat); | copy_m4_m4(ss->filter_cache->obmat, ob->obmat); | ||||
| invert_m4_m4(ss->filter_cache->obmat_inv, ob->obmat); | invert_m4_m4(ss->filter_cache->obmat_inv, ob->obmat); | ||||
| Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); | ||||
| ViewContext vc; | ViewContext vc; | ||||
| ED_view3d_viewcontext_init(C, &vc, depsgraph); | ED_view3d_viewcontext_init(C, &vc, depsgraph); | ||||
| copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat); | copy_m4_m4(ss->filter_cache->viewmat, vc.rv3d->viewmat); | ||||
| copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv); | copy_m4_m4(ss->filter_cache->viewmat_inv, vc.rv3d->viewinv); | ||||
| Scene *scene = CTX_data_scene(C); | |||||
| UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; | |||||
| float co[3]; | |||||
| float mval_fl[2] = {(float)mval[0], (float)mval[1]}; | |||||
| if (SCULPT_stroke_get_location(C, co, mval_fl, false)) { | |||||
| PBVHNode **nodes; | |||||
| int totnode; | |||||
| /* Get radius from brush. */ | |||||
| Brush *brush = BKE_paint_brush(&sd->paint); | |||||
| float radius; | |||||
| if (brush) { | |||||
| if (BKE_brush_use_locked_size(scene, brush)) { | |||||
| radius = paint_calc_object_space_radius( | |||||
| &vc, co, (float)BKE_brush_size_get(scene, brush) * area_normal_radius); | |||||
| } | |||||
| else { | |||||
| radius = BKE_brush_unprojected_radius_get(scene, brush) * area_normal_radius; | |||||
| } | |||||
| } | |||||
| else { | |||||
| radius = paint_calc_object_space_radius(&vc, co, (float)ups->size * area_normal_radius); | |||||
| } | |||||
| SculptSearchSphereData search_data2 = { | |||||
| .original = true, | |||||
| .center = co, | |||||
| .radius_squared = radius * radius, | |||||
| .ignore_fully_ineffective = true, | |||||
| }; | |||||
| BKE_pbvh_search_gather(pbvh, SCULPT_search_sphere_cb, &search_data2, &nodes, &totnode); | |||||
| if (SCULPT_pbvh_calc_area_normal( | |||||
| brush, ob, nodes, totnode, true, ss->filter_cache->initial_normal)) { | |||||
| copy_v3_v3(ss->last_normal, ss->filter_cache->initial_normal); | |||||
| } | |||||
| else { | |||||
| copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); | |||||
| } | |||||
| MEM_SAFE_FREE(nodes); | |||||
| /* Update last stroke location */ | |||||
| mul_m4_v3(ob->obmat, co); | |||||
| add_v3_v3(ups->average_stroke_accum, co); | |||||
| ups->average_stroke_counter++; | |||||
| ups->last_stroke_valid = true; | |||||
| } | |||||
| else { | |||||
| /* Use last normal. */ | |||||
| copy_v3_v3(ss->filter_cache->initial_normal, ss->last_normal); | |||||
| } | |||||
| /* Update view normal */ | |||||
| float projection_mat[4][4]; | |||||
| float mat[3][3]; | |||||
| float viewDir[3] = {0.0f, 0.0f, 1.0f}; | |||||
| ED_view3d_ob_project_mat_get(vc.rv3d, ob, projection_mat); | |||||
| invert_m4_m4(ob->imat, ob->obmat); | |||||
| copy_m3_m4(mat, vc.rv3d->viewinv); | |||||
| mul_m3_v3(mat, viewDir); | |||||
| copy_m3_m4(mat, ob->imat); | |||||
| mul_m3_v3(mat, viewDir); | |||||
| normalize_v3_v3(ss->filter_cache->view_normal, viewDir); | |||||
| } | } | ||||
| void SCULPT_filter_cache_free(SculptSession *ss) | void SCULPT_filter_cache_free(SculptSession *ss) | ||||
| { | { | ||||
| if (ss->filter_cache->cloth_sim) { | if (ss->filter_cache->cloth_sim) { | ||||
| SCULPT_cloth_simulation_free(ss->filter_cache->cloth_sim); | SCULPT_cloth_simulation_free(ss->filter_cache->cloth_sim); | ||||
| } | } | ||||
| if (ss->filter_cache->automasking) { | if (ss->filter_cache->automasking) { | ||||
| ▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | static void mesh_filter_task_cb(void *__restrict userdata, | ||||
| SculptOrigVertData orig_data; | SculptOrigVertData orig_data; | ||||
| SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); | SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); | ||||
| /* When using the relax face sets meshes filter, | /* When using the relax face sets meshes filter, | ||||
| * each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */ | * each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */ | ||||
| /* This produces better results as the relax operation is no completely focused on the | /* This produces better results as the relax operation is no completely focused on the | ||||
| * boundaries. */ | * boundaries. */ | ||||
| const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0); | const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0); | ||||
| AutomaskingNodeData automask_data; | |||||
| SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node); | |||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { | ||||
| SCULPT_orig_vert_data_update(&orig_data, &vd); | SCULPT_orig_vert_data_update(&orig_data, &vd); | ||||
| SCULPT_automasking_node_update(ss, &automask_data, &vd); | |||||
| float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; | float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; | ||||
| float fade = vd.mask ? *vd.mask : 0.0f; | float fade = vd.mask ? *vd.mask : 0.0f; | ||||
| fade = 1.0f - fade; | fade = 1.0f - fade; | ||||
| fade *= data->filter_strength; | fade *= data->filter_strength; | ||||
| fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); | fade *= SCULPT_automasking_factor_get( | ||||
| ss->filter_cache->automasking, ss, vd.vertex, &automask_data); | |||||
| if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { | if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { | ||||
| /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its | /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its | ||||
| * laplacian_disp. This value is accessed from the vertex neighbors when deforming the | * laplacian_disp. This value is accessed from the vertex neighbors when deforming the | ||||
| * vertices, so it is needed for all vertices even if they are not going to be displaced. | * vertices, so it is needed for all vertices even if they are not going to be displaced. | ||||
| */ | */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | |||||
| static void mesh_filter_surface_smooth_displace_task_cb( | static void mesh_filter_surface_smooth_displace_task_cb( | ||||
| void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) | void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = userdata; | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| PBVHNode *node = data->nodes[i]; | PBVHNode *node = data->nodes[i]; | ||||
| PBVHVertexIter vd; | PBVHVertexIter vd; | ||||
| AutomaskingNodeData automask_data; | |||||
| SCULPT_automasking_node_begin( | |||||
| data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[i]); | |||||
| BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { | BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { | ||||
| SCULPT_automasking_node_update(ss, &automask_data, &vd); | |||||
| float fade = vd.mask ? *vd.mask : 0.0f; | float fade = vd.mask ? *vd.mask : 0.0f; | ||||
| fade = 1.0f - fade; | fade = 1.0f - fade; | ||||
| fade *= data->filter_strength; | fade *= data->filter_strength; | ||||
| fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); | fade *= SCULPT_automasking_factor_get( | ||||
| ss->filter_cache->automasking, ss, vd.vertex, &automask_data); | |||||
| if (fade == 0.0f) { | if (fade == 0.0f) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| SCULPT_surface_smooth_displace_step(ss, | SCULPT_surface_smooth_displace_step(ss, | ||||
| vd.co, | vd.co, | ||||
| ss->filter_cache->surface_smooth_laplacian_disp, | ss->filter_cache->surface_smooth_laplacian_disp, | ||||
| vd.vertex, | vd.vertex, | ||||
| ▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking; | const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking; | ||||
| if (deform_axis == 0) { | if (deform_axis == 0) { | ||||
| /* All axis are disabled, so the filter is not going to produce any deformation. */ | /* All axis are disabled, so the filter is not going to produce any deformation. */ | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (use_automasking) { | if (use_automasking) { | ||||
| /* Increment stroke id for automasking system. */ | |||||
| SCULPT_stroke_id_next(ob); | |||||
| /* Update the active face set manually as the paint cursor is not enabled when using the Mesh | /* Update the active face set manually as the paint cursor is not enabled when using the Mesh | ||||
| * Filter Tool. */ | * Filter Tool. */ | ||||
| float mval_fl[2] = {UNPACK2(event->mval)}; | float mval_fl[2] = {UNPACK2(event->mval)}; | ||||
| SculptCursorGeometryInfo sgi; | SculptCursorGeometryInfo sgi; | ||||
| SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); | SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); | ||||
| } | } | ||||
| SCULPT_vertex_random_access_ensure(ss); | SCULPT_vertex_random_access_ensure(ss); | ||||
| BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); | BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); | ||||
| if (needs_topology_info) { | if (needs_topology_info) { | ||||
| SCULPT_boundary_info_ensure(ob); | SCULPT_boundary_info_ensure(ob); | ||||
| } | } | ||||
| SCULPT_undo_push_begin(ob, op); | SCULPT_undo_push_begin(ob, op); | ||||
| SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); | SCULPT_filter_cache_init( | ||||
| C, ob, sd, SCULPT_UNDO_COORDS, event->mval, RNA_float_get(op->ptr, "area_normal_radius")); | |||||
| FilterCache *filter_cache = ss->filter_cache; | FilterCache *filter_cache = ss->filter_cache; | ||||
| filter_cache->active_face_set = SCULPT_FACE_SET_NONE; | filter_cache->active_face_set = SCULPT_FACE_SET_NONE; | ||||
| filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); | filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob); | ||||
| switch (filter_type) { | switch (filter_type) { | ||||
| case MESH_FILTER_SURFACE_SMOOTH: { | case MESH_FILTER_SURFACE_SMOOTH: { | ||||
| const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation"); | const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation"); | ||||
| Show All 30 Lines | static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); | SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); | ||||
| ss->filter_cache->orientation = orientation; | ss->filter_cache->orientation = orientation; | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| void SCULPT_mesh_filter_properties(struct wmOperatorType *ot) | |||||
| { | |||||
| RNA_def_float( | |||||
| ot->srna, | |||||
| "area_normal_radius", | |||||
| 0.25, | |||||
| 0.001, | |||||
| 5.0, | |||||
| "Normal Radius", | |||||
| "Radius used for calculating area normal on initial click,\nin percentage of brush radius", | |||||
| 0.01, | |||||
| 1.0); | |||||
| RNA_def_float( | |||||
| ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); | |||||
| } | |||||
| void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) | void SCULPT_OT_mesh_filter(struct wmOperatorType *ot) | ||||
| { | { | ||||
| /* Identifiers. */ | /* Identifiers. */ | ||||
| ot->name = "Filter Mesh"; | ot->name = "Filter Mesh"; | ||||
| ot->idname = "SCULPT_OT_mesh_filter"; | ot->idname = "SCULPT_OT_mesh_filter"; | ||||
| ot->description = "Applies a filter to modify the current mesh"; | ot->description = "Applies a filter to modify the current mesh"; | ||||
| /* API callbacks. */ | /* API callbacks. */ | ||||
| ot->invoke = sculpt_mesh_filter_invoke; | ot->invoke = sculpt_mesh_filter_invoke; | ||||
| ot->modal = sculpt_mesh_filter_modal; | ot->modal = sculpt_mesh_filter_modal; | ||||
| ot->poll = SCULPT_mode_poll; | ot->poll = SCULPT_mode_poll; | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* RNA. */ | /* RNA. */ | ||||
| SCULPT_mesh_filter_properties(ot); | |||||
| RNA_def_enum(ot->srna, | RNA_def_enum(ot->srna, | ||||
| "type", | "type", | ||||
| prop_mesh_filter_types, | prop_mesh_filter_types, | ||||
| MESH_FILTER_INFLATE, | MESH_FILTER_INFLATE, | ||||
| "Filter Type", | "Filter Type", | ||||
| "Operation that is going to be applied to the mesh"); | "Operation that is going to be applied to the mesh"); | ||||
| RNA_def_float( | |||||
| ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); | |||||
| RNA_def_enum_flag(ot->srna, | RNA_def_enum_flag(ot->srna, | ||||
| "deform_axis", | "deform_axis", | ||||
| prop_mesh_filter_deform_axis_items, | prop_mesh_filter_deform_axis_items, | ||||
| MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, | MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z, | ||||
| "Deform Axis", | "Deform Axis", | ||||
| "Apply the deformation in the selected axis"); | "Apply the deformation in the selected axis"); | ||||
| RNA_def_enum(ot->srna, | RNA_def_enum(ot->srna, | ||||
| "orientation", | "orientation", | ||||
| ▲ Show 20 Lines • Show All 54 Lines • Show Last 20 Lines | |||||