Multi-Objects: MESH_OT_dupli_extrude_cursor

Supporting the ctrl+click operator for multiple objects.
When no vertex is selected we only add a new vertex for the active object.

I'm using all the selected vertices to calculate the center of transformation
regardless of the object they come from. This way we have the same effect if the
meshes are joined together or kept as individual objects.

Thanks Campbell Barton for reviewing the patch.
This commit is contained in:
Dalai Felinto 2018-08-20 20:55:56 -03:00
parent 6e7da7616b
commit 68a719f1ea
Notes: blender-bot 2023-02-14 06:00:46 +01:00
Referenced by issue #54643, Multi-Object-Mode: EditMesh Tools
1 changed files with 169 additions and 124 deletions

View File

@ -982,161 +982,206 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
BMIter iter;
float center[3];
uint verts_len;
bool use_proj;
em_setup_viewcontext(C, &vc);
const Object *object_active = vc.obact;
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
(vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
const bool use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
(vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
/* First calculate the center of transformation. */
zero_v3(center);
verts_len = 0;
BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
add_v3_v3(center, v1->co);
verts_len += 1;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(vc.view_layer, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ED_view3d_viewcontext_init_object(&vc, obedit);
const int local_verts_len = vc.em->bm->totvertsel;
if (vc.em->bm->totvertsel == 0) {
continue;
}
float local_center[3];
zero_v3(local_center);
BM_ITER_MESH(v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
add_v3_v3(local_center, v1->co);
}
}
mul_v3_fl(local_center, 1.0f / (float)local_verts_len);
mul_m4_v3(vc.obedit->obmat, local_center);
mul_v3_fl(local_center, (float)local_verts_len);
add_v3_v3(center, local_center);
verts_len += local_verts_len;
}
/* call extrude? */
if (verts_len != 0) {
const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
BMEdge *eed;
float mat[3][3];
float vec[3], ofs[3];
float nor[3] = {0.0, 0.0, 0.0};
/* 2D normal calc */
const float mval_f[2] = {(float)event->mval[0],
(float)event->mval[1]};
mul_v3_fl(center, 1.0f / (float)verts_len);
}
/* check for edges that are half selected, use for rotation */
bool done = false;
BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
float co1[2], co2[2];
/* Then we process the meshes. */
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ED_view3d_viewcontext_init_object(&vc, obedit);
if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
(ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
{
/* 2D rotate by 90d while adding.
* (x, y) = (y, -x)
*
* accumulate the screenspace normal in 2D,
* with screenspace edge length weighting the result. */
if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
nor[0] += (co1[1] - co2[1]);
nor[1] += -(co1[0] - co2[0]);
}
else {
nor[0] += (co2[1] - co1[1]);
nor[1] += -(co2[0] - co1[0]);
}
done = true;
}
if (verts_len != 0) {
if (vc.em->bm->totvertsel == 0) {
continue;
}
}
if (done) {
float view_vec[3], cross[3];
/* convert the 2D nomal into 3D */
mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
/* correct the normal to be aligned on the view plane */
mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
cross_v3_v3v3(cross, nor, view_vec);
cross_v3_v3v3(nor, view_vec, cross);
normalize_v3(nor);
else if (obedit != object_active) {
continue;
}
/* center */
copy_v3_v3(ofs, center);
invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
mul_m4_v3(vc.obedit->obmat, ofs); /* view space */
ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
mul_m4_v3(vc.obedit->imat, ofs); // back in object space
float local_center[3];
mul_v3_m4v3(local_center, vc.obedit->imat, center);
sub_v3_v3(ofs, center);
/* call extrude? */
if (verts_len != 0) {
const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
BMEdge *eed;
float mat[3][3];
float vec[3], ofs[3];
float nor[3] = { 0.0, 0.0, 0.0 };
/* calculate rotation */
unit_m3(mat);
if (done) {
float angle;
/* 2D normal calc */
const float mval_f[2] = { (float)event->mval[0],
(float)event->mval[1] };
normalize_v3_v3(vec, ofs);
/* check for edges that are half selected, use for rotation */
bool done = false;
BM_ITER_MESH(eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
float co1[2], co2[2];
angle = angle_normalized_v3v3(vec, nor);
if (angle != 0.0f) {
float axis[3];
cross_v3_v3v3(axis, nor, vec);
/* halve the rotation if its applied twice */
if (rot_src) {
angle *= 0.5f;
if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
(ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
{
/* 2D rotate by 90d while adding.
* (x, y) = (y, -x)
*
* accumulate the screenspace normal in 2D,
* with screenspace edge length weighting the result. */
if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
nor[0] += (co1[1] - co2[1]);
nor[1] += -(co1[0] - co2[0]);
}
else {
nor[0] += (co2[1] - co1[1]);
nor[1] += -(co2[0] - co1[0]);
}
done = true;
}
}
axis_angle_to_mat3(mat, axis, angle);
}
}
if (rot_src) {
if (done) {
float view_vec[3], cross[3];
/* convert the 2D normal into 3D */
mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
/* correct the normal to be aligned on the view plane */
mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
cross_v3_v3v3(cross, nor, view_vec);
cross_v3_v3v3(nor, view_vec, cross);
normalize_v3(nor);
}
/* center */
copy_v3_v3(ofs, local_center);
mul_m4_v3(vc.obedit->obmat, ofs); /* view space */
ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
mul_m4_v3(vc.obedit->imat, ofs); // back in object space
sub_v3_v3(ofs, local_center);
/* calculate rotation */
unit_m3(mat);
if (done) {
float angle;
normalize_v3_v3(vec, ofs);
angle = angle_normalized_v3v3(vec, nor);
if (angle != 0.0f) {
float axis[3];
cross_v3_v3v3(axis, nor, vec);
/* halve the rotation if its applied twice */
if (rot_src) {
angle *= 0.5f;
}
axis_angle_to_mat3(mat, axis, angle);
}
}
if (rot_src) {
EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
BM_ELEM_SELECT, local_center, mat);
/* also project the source, for retopo workflow */
if (use_proj) {
EMBM_project_snap_verts(C, vc.ar, vc.em);
}
}
edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, true, true);
EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
BM_ELEM_SELECT, center, mat);
BM_ELEM_SELECT, local_center, mat);
EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
BM_ELEM_SELECT, ofs);
}
else {
/* This only runs for the active object. */
const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d)->location;
BMOperator bmop;
BMOIter oiter;
/* also project the source, for retopo workflow */
if (use_proj)
EMBM_project_snap_verts(C, vc.ar, vc.em);
copy_v3_v3(local_center, cursor);
ED_view3d_win_to_3d_int(vc.v3d, vc.ar, local_center, event->mval, local_center);
mul_m4_v3(vc.obedit->imat, local_center); // back in object space
EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", local_center);
BMO_op_exec(vc.em->bm, &bmop);
BMO_ITER(v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
BM_vert_select_set(vc.em->bm, v1, true);
}
if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
continue;
}
}
edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, true, true);
EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
BM_ELEM_SELECT, center, mat);
EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
BM_ELEM_SELECT, ofs);
if (use_proj) {
EMBM_project_snap_verts(C, vc.ar, vc.em);
}
/* This normally happens when pushing undo but modal operators
* like this one don't push undo data until after modal mode is
* done. */
EDBM_mesh_normals_update(vc.em);
EDBM_update_generic(vc.em, true, true);
}
else {
const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d)->location;
BMOperator bmop;
BMOIter oiter;
copy_v3_v3(center, cursor);
ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
mul_m4_v3(vc.obedit->imat, center); // back in object space
EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", center);
BMO_op_exec(vc.em->bm, &bmop);
BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
BM_vert_select_set(vc.em->bm, v1, true);
}
if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
return OPERATOR_CANCELLED;
}
}
if (use_proj)
EMBM_project_snap_verts(C, vc.ar, vc.em);
/* This normally happens when pushing undo but modal operators
* like this one don't push undo data until after modal mode is
* done. */
EDBM_mesh_normals_update(vc.em);
EDBM_update_generic(vc.em, true, true);
MEM_freeN(objects);
return OPERATOR_FINISHED;
}