diff options
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/object/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_remesh.c | 410 |
4 files changed, 410 insertions, 4 deletions
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index a5b6fa55aa9..c2c25e47908 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -18,6 +18,7 @@ set(INC ../include ../../blenkernel + ../../blenfont ../../blenlib ../../blentranslation ../../bmesh diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 559bb434f9d..d8ba270073e 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -291,6 +291,7 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); /* object_remesh.c */ void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); +void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot); void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot); /* object_transfer_data.c */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index cacf7b67777..fef046169a7 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -265,6 +265,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_hide_collection); WM_operatortype_append(OBJECT_OT_voxel_remesh); + WM_operatortype_append(OBJECT_OT_voxel_size_edit); + WM_operatortype_append(OBJECT_OT_quadriflow_remesh); } diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index a9f2319d926..08c44b87664 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -37,6 +37,9 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_userdef_types.h" + +#include "BLT_translation.h" #include "BKE_context.h" #include "BKE_customdata.h" @@ -61,17 +64,29 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_space_api.h" #include "ED_undo.h" +#include "ED_view3d.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" +#include "GPU_draw.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + #include "WM_api.h" #include "WM_message.h" #include "WM_toolsystem.h" #include "WM_types.h" +#include "UI_interface.h" + +#include "BLF_api.h" + #include "object_intern.h" // own include /* TODO(sebpa): unstable, can lead to unrecoverable errors. */ @@ -188,16 +203,403 @@ void OBJECT_OT_voxel_remesh(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +#define VOXEL_SIZE_EDIT_MAX_GRIDS_LINES 500 +#define VOXEL_SIZE_EDIT_MAX_STR_LEN 20 + +typedef struct VoxelSizeEditCustomData { + void *draw_handle; + Object *active_object; + + float init_mval[2]; + float slow_mval[2]; + + bool slow_mode; + + float init_voxel_size; + float slow_voxel_size; + float voxel_size; + + float preview_plane[4][3]; + + float text_mat[4][4]; +} VoxelSizeEditCustomData; + +static void voxel_size_parallel_lines_draw(uint pos3d, + const float initial_co[3], + const float end_co[3], + const float length_co[3], + const float spacing) +{ + const float total_len = len_v3v3(initial_co, end_co); + const int tot_lines = (int)(total_len / spacing); + const int tot_lines_half = (tot_lines / 2) + 1; + float spacing_dir[3], lines_start[3]; + float line_dir[3]; + sub_v3_v3v3(spacing_dir, end_co, initial_co); + normalize_v3(spacing_dir); + + sub_v3_v3v3(line_dir, length_co, initial_co); + + if (tot_lines > VOXEL_SIZE_EDIT_MAX_GRIDS_LINES || tot_lines <= 1) { + return; + } + + mid_v3_v3v3(lines_start, initial_co, end_co); + + immBegin(GPU_PRIM_LINES, (uint)tot_lines_half * 2); + for (int i = 0; i < tot_lines_half; i++) { + float line_start[3]; + float line_end[3]; + madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i); + add_v3_v3v3(line_end, line_start, line_dir); + immVertex3fv(pos3d, line_start); + immVertex3fv(pos3d, line_end); + } + immEnd(); + + mul_v3_fl(spacing_dir, -1.0f); + + immBegin(GPU_PRIM_LINES, (uint)(tot_lines_half - 1) * 2); + for (int i = 1; i < tot_lines_half; i++) { + float line_start[3]; + float line_end[3]; + madd_v3_v3v3fl(line_start, lines_start, spacing_dir, spacing * i); + add_v3_v3v3(line_end, line_start, line_dir); + immVertex3fv(pos3d, line_start); + immVertex3fv(pos3d, line_end); + } + immEnd(); +} + +static void voxel_size_edit_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + VoxelSizeEditCustomData *cd = arg; + + GPU_blend(true); + GPU_line_smooth(true); + + uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + GPU_matrix_push(); + GPU_matrix_mul(cd->active_object->obmat); + + /* Draw Rect */ + immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f); + GPU_line_width(3.0f); + + immBegin(GPU_PRIM_LINES, 8); + immVertex3fv(pos3d, cd->preview_plane[0]); + immVertex3fv(pos3d, cd->preview_plane[1]); + + immVertex3fv(pos3d, cd->preview_plane[1]); + immVertex3fv(pos3d, cd->preview_plane[2]); + + immVertex3fv(pos3d, cd->preview_plane[2]); + immVertex3fv(pos3d, cd->preview_plane[3]); + + immVertex3fv(pos3d, cd->preview_plane[3]); + immVertex3fv(pos3d, cd->preview_plane[0]); + immEnd(); + + /* Draw Grid */ + GPU_line_width(1.0f); + + const float total_len = len_v3v3(cd->preview_plane[0], cd->preview_plane[1]); + const int tot_lines = (int)(total_len / cd->voxel_size); + + /* Smoothstep to reduce the alpha of the grid as the line number increases. */ + const float a = VOXEL_SIZE_EDIT_MAX_GRIDS_LINES * 0.1f; + const float b = VOXEL_SIZE_EDIT_MAX_GRIDS_LINES; + const float x = clamp_f((tot_lines - a) / (b - a), 0.0f, 1.0); + const float alpha_factor = 1.0f - (x * x * (3.0f - 2.0f * x)); + + immUniformColor4f(0.9f, 0.9f, 0.9f, 0.75f * alpha_factor); + voxel_size_parallel_lines_draw( + pos3d, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[3], cd->voxel_size); + voxel_size_parallel_lines_draw( + pos3d, cd->preview_plane[1], cd->preview_plane[2], cd->preview_plane[0], cd->voxel_size); + + /* Draw text */ + uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + float strwidth, strheight; + short fstyle_points = fstyle->points; + char str[VOXEL_SIZE_EDIT_MAX_STR_LEN]; + short strdrawlen = 0; + + BLI_snprintf(str, VOXEL_SIZE_EDIT_MAX_STR_LEN, "%3.4f%%", cd->voxel_size); + strdrawlen = BLI_strlen_utf8(str); + + immUnbindProgram(); + + GPU_matrix_push(); + GPU_matrix_mul(cd->text_mat); + BLF_size(fontid, 10.0f * fstyle_points, U.dpi); + BLF_color3f(fontid, 1.0f, 1.0f, 1.0f); + BLF_width_and_height(fontid, str, strdrawlen, &strwidth, &strheight); + BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f); + BLF_draw(fontid, str, strdrawlen); + GPU_matrix_pop(); + + GPU_matrix_pop(); + + GPU_blend(false); + GPU_line_smooth(false); +} + +static void voxel_size_edit_cancel(bContext *C, wmOperator *op) +{ + ARegion *ar = CTX_wm_region(C); + VoxelSizeEditCustomData *cd = op->customdata; + + ED_region_draw_cb_exit(ar->type, cd->draw_handle); + + MEM_freeN(op->customdata); + + ED_workspace_status_text(C, NULL); +} + +static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + VoxelSizeEditCustomData *cd = op->customdata; + Object *active_object = cd->active_object; + Mesh *mesh = (Mesh *)active_object->data; + + /* Cancel modal operator */ + if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) || + (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + voxel_size_edit_cancel(C, op); + ED_region_tag_redraw(ar); + return OPERATOR_FINISHED; + } + + /* Finish modal operator */ + if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) || + (event->type == EVT_RETKEY && event->val == KM_PRESS) || + (event->type == EVT_PADENTER && event->val == KM_PRESS)) { + ED_region_draw_cb_exit(ar->type, cd->draw_handle); + mesh->remesh_voxel_size = cd->voxel_size; + MEM_freeN(op->customdata); + ED_region_tag_redraw(ar); + return OPERATOR_FINISHED; + } + + float mval[2] = {event->mval[0], event->mval[1]}; + + float d = cd->init_mval[0] - mval[0]; + + if (cd->slow_mode) { + d = cd->slow_mval[0] - mval[0]; + } + + if (event->ctrl) { + /* Linear mode, enables jumping to any voxel size. */ + d = d * 0.0005f; + } + else { + /* Multiply d by the initial voxel size to prevent incontrolable speeds when using low voxel + * sizes. */ + /* When the voxel size is slower, it needs more precision. */ + d = d * min_ff(pow2f(cd->init_voxel_size), 0.1f) * 0.05f; + } + if (cd->slow_mode) { + cd->voxel_size = cd->slow_voxel_size + d * 0.05f; + } + else { + cd->voxel_size = cd->init_voxel_size + d; + } + + if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) { + cd->slow_mode = true; + copy_v2_v2(cd->slow_mval, mval); + cd->slow_voxel_size = cd->voxel_size; + } + if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) { + cd->slow_mode = false; + cd->slow_voxel_size = 0.0f; + } + + cd->voxel_size = clamp_f(cd->voxel_size, 0.0001f, 1.0f); + + ED_region_tag_redraw(ar); + return OPERATOR_RUNNING_MODAL; +} + +static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + Object *active_object = CTX_data_active_object(C); + Mesh *mesh = (Mesh *)active_object->data; + + VoxelSizeEditCustomData *cd = MEM_callocN(sizeof(VoxelSizeEditCustomData), + "Voxel Size Edit OP Custom Data"); + + /* Initial operator Custom Data setup. */ + cd->draw_handle = ED_region_draw_cb_activate( + ar->type, voxel_size_edit_draw, cd, REGION_DRAW_POST_VIEW); + cd->active_object = active_object; + cd->init_mval[0] = event->mval[0]; + cd->init_mval[1] = event->mval[1]; + cd->init_voxel_size = mesh->remesh_voxel_size; + cd->voxel_size = mesh->remesh_voxel_size; + op->customdata = cd; + + /* Select the front facing face of the mesh boundig box. */ + BoundBox *bb = BKE_mesh_boundbox_get(cd->active_object); + + /* Indices of the Bounding Box faces. */ + int BB_faces[6][4] = { + {3, 0, 4, 7}, + {1, 2, 6, 5}, + {3, 2, 1, 0}, + {4, 5, 6, 7}, + {0, 1, 5, 4}, + {2, 3, 7, 6}, + }; + + copy_v3_v3(cd->preview_plane[0], bb->vec[BB_faces[0][0]]); + copy_v3_v3(cd->preview_plane[1], bb->vec[BB_faces[0][1]]); + copy_v3_v3(cd->preview_plane[2], bb->vec[BB_faces[0][2]]); + copy_v3_v3(cd->preview_plane[3], bb->vec[BB_faces[0][3]]); + + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + float mat[3][3]; + float current_normal[3]; + float view_normal[3] = {0.0f, 0.0f, 1.0f}; + + /* Calculate the view normal. */ + invert_m4_m4(active_object->imat, active_object->obmat); + copy_m3_m4(mat, rv3d->viewinv); + mul_m3_v3(mat, view_normal); + copy_m3_m4(mat, active_object->imat); + mul_m3_v3(mat, view_normal); + normalize_v3(view_normal); + + normal_tri_v3(current_normal, cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]); + + float min_dot = dot_v3v3(current_normal, view_normal); + float current_dot = 1; + + /* Check if there is a face that is more aligned towards the view. */ + for (int i = 0; i < 6; i++) { + normal_tri_v3( + current_normal, bb->vec[BB_faces[i][0]], bb->vec[BB_faces[i][1]], bb->vec[BB_faces[i][2]]); + current_dot = dot_v3v3(current_normal, view_normal); + + if (current_dot < min_dot) { + min_dot = current_dot; + copy_v3_v3(cd->preview_plane[0], bb->vec[BB_faces[i][0]]); + copy_v3_v3(cd->preview_plane[1], bb->vec[BB_faces[i][1]]); + copy_v3_v3(cd->preview_plane[2], bb->vec[BB_faces[i][2]]); + copy_v3_v3(cd->preview_plane[3], bb->vec[BB_faces[i][3]]); + } + } + + /* Matrix calculation to position the text in 3D space. */ + float text_pos[3]; + float scale_mat[4][4]; + + float d_a[3], d_b[3]; + float d_a_proj[2], d_b_proj[2]; + float preview_plane_proj[4][3]; + float y_axis_proj[2] = {0.0f, 1.0f}; + + mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]); + + /* Project the selected face in the previous step of the Bounding Box. */ + for (int i = 0; i < 4; i++) { + ED_view3d_project(ar, cd->preview_plane[i], preview_plane_proj[i]); + } + + /* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */ + sub_v3_v3v3(d_a, cd->preview_plane[1], cd->preview_plane[0]); + sub_v3_v3v3(d_b, cd->preview_plane[3], cd->preview_plane[0]); + normalize_v3(d_a); + normalize_v3(d_b); + + /* Project the X and Y axis. */ + sub_v2_v2v2(d_a_proj, preview_plane_proj[1], preview_plane_proj[0]); + sub_v2_v2v2(d_b_proj, preview_plane_proj[3], preview_plane_proj[0]); + normalize_v2(d_a_proj); + normalize_v2(d_b_proj); + + unit_m4(cd->text_mat); + + /* Select the axis that is aligned with the view Y axis to use it as the basis Y. */ + if (fabsf(dot_v2v2(d_a_proj, y_axis_proj)) > fabsf(dot_v2v2(d_b_proj, y_axis_proj))) { + copy_v3_v3(cd->text_mat[0], d_b); + copy_v3_v3(cd->text_mat[1], d_a); + + /* Flip the X and Y basis vectors to make sure they always point upwards and to the right. */ + if (d_b_proj[0] < 0.0f) { + mul_v3_fl(cd->text_mat[0], -1.0f); + } + if (d_a_proj[1] < 0.0f) { + mul_v3_fl(cd->text_mat[1], -1.0f); + } + } + else { + copy_v3_v3(cd->text_mat[0], d_a); + copy_v3_v3(cd->text_mat[1], d_b); + if (d_a_proj[0] < 0.0f) { + mul_v3_fl(cd->text_mat[0], -1.0f); + } + if (d_b_proj[1] < 0.0f) { + mul_v3_fl(cd->text_mat[1], -1.0f); + } + } + + /* Use the Bounding Box face normal as the basis Z. */ + normal_tri_v3(cd->text_mat[2], cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]); + + /* Write the text position into the matrix. */ + copy_v3_v3(cd->text_mat[3], text_pos); + + /* Scale the text. */ + unit_m4(scale_mat); + scale_m4_fl(scale_mat, 0.0008f); + mul_m4_m4_post(cd->text_mat, scale_mat); + + WM_event_add_modal_handler(C, op); + + ED_region_tag_redraw(ar); + + const char *status_str = TIP_( + "Move the mouse to change the voxel size. LBM: confirm size, ESC/RMB: cancel"); + ED_workspace_status_text(C, status_str); + + return OPERATOR_RUNNING_MODAL; +} + +void OBJECT_OT_voxel_size_edit(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Voxel Size"; + ot->description = "Modify the mesh voxel size interactively used in the voxel remesher"; + ot->idname = "OBJECT_OT_voxel_size_edit"; + + /* api callbacks */ + ot->poll = object_remesh_poll; + ot->invoke = voxel_size_edit_invoke; + ot->modal = voxel_size_edit_modal; + ot->cancel = voxel_size_edit_cancel; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/****************** quadriflow remesh operator *********************/ + +#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE 0.005f + enum { QUADRIFLOW_REMESH_RATIO = 1, QUADRIFLOW_REMESH_EDGE_LENGTH, QUADRIFLOW_REMESH_FACES, }; -/****************** quadriflow remesh operator *********************/ - -#define QUADRIFLOW_MIRROR_BISECT_TOLERANCE 0.005f - typedef enum eSymmetryAxes { SYMMETRY_AXES_X = (1 << 0), SYMMETRY_AXES_Y = (1 << 1), |