bool view3d_get_view_aligned_coordinate(ARegion *ar, float fp[3], const int mval[2], const bool do_fallback) { RegionView3D *rv3d = ar->regiondata; float dvec[3]; int mval_cpy[2]; eV3DProjStatus ret; ret = ED_view3d_project_int_global(ar, fp, mval_cpy, V3D_PROJ_TEST_NOP); if (ret == V3D_PROJ_RET_OK) { const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]), (float)(mval_cpy[1] - mval[1])}; const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL); ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); sub_v3_v3(fp, dvec); return true; } else { /* fallback to the view center */ if (do_fallback) { negate_v3_v3(fp, rv3d->ofs); return view3d_get_view_aligned_coordinate(ar, fp, mval, false); } else { return false; } } }
static int dupli_extrude_cursor(bContext *C, wmOperator *op, wmEvent *event) { ViewContext vc; EditVert *eve; float min[3], max[3]; int done= 0; short use_proj; em_setup_viewcontext(C, &vc); use_proj= (vc.scene->toolsettings->snap_flag & SCE_SNAP) && (vc.scene->toolsettings->snap_mode==SCE_SNAP_MODE_FACE); invert_m4_m4(vc.obedit->imat, vc.obedit->obmat); INIT_MINMAX(min, max); for(eve= vc.em->verts.first; eve; eve= eve->next) { if(eve->f & SELECT) { DO_MINMAX(eve->co, min, max); done= 1; } } /* call extrude? */ if(done) { const short rot_src= RNA_boolean_get(op->ptr, "rotate_source"); EditEdge *eed; float vec[3], cent[3], mat[3][3]; float nor[3]= {0.0, 0.0, 0.0}; /* 2D normal calc */ float mval_f[2]; mval_f[0]= (float)event->mval[0]; mval_f[1]= (float)event->mval[1]; done= 0; /* calculate the normal for selected edges */ for(eed= vc.em->edges.first; eed; eed= eed->next) { if(eed->f & SELECT) { float co1[3], co2[3]; mul_v3_m4v3(co1, vc.obedit->obmat, eed->v1->co); mul_v3_m4v3(co2, vc.obedit->obmat, eed->v2->co); project_float_noclip(vc.ar, co1, co1); project_float_noclip(vc.ar, co2, co2); /* 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= 1; } } 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 */ copy_v3_v3(view_vec, vc.rv3d->viewinv[2]); mul_mat3_m4_v3(vc.obedit->imat, view_vec); cross_v3_v3v3(cross, nor, view_vec); cross_v3_v3v3(nor, view_vec, cross); normalize_v3(nor); } /* center */ mid_v3_v3v3(cent, min, max); copy_v3_v3(min, cent); mul_m4_v3(vc.obedit->obmat, min); // view space view3d_get_view_aligned_coordinate(&vc, min, event->mval, TRUE); mul_m4_v3(vc.obedit->imat, min); // back in object space sub_v3_v3(min, cent); /* calculate rotation */ unit_m3(mat); if(done) { float dot; copy_v3_v3(vec, min); normalize_v3(vec); dot= dot_v3v3(vec, nor); if( fabs(dot)<0.999) { float cross[3], si, q1[4]; cross_v3_v3v3(cross, nor, vec); normalize_v3(cross); dot= 0.5f*saacos(dot); /* halve the rotation if its applied twice */ if(rot_src) dot *= 0.5f; si= (float)sin(dot); q1[0]= (float)cos(dot); q1[1]= cross[0]*si; q1[2]= cross[1]*si; q1[3]= cross[2]*si; quat_to_mat3( mat,q1); } } if(rot_src) { rotateflag(vc.em, SELECT, cent, mat); /* also project the source, for retopo workflow */ if(use_proj) EM_project_snap_verts(C, vc.ar, vc.obedit, vc.em); } extrudeflag(vc.obedit, vc.em, SELECT, nor, 0); rotateflag(vc.em, SELECT, cent, mat); translateflag(vc.em, SELECT, min); recalc_editnormals(vc.em); } else if(vc.em->selectmode & SCE_SELECT_VERTEX) { float imat[4][4]; const float *curs= give_cursor(vc.scene, vc.v3d); copy_v3_v3(min, curs); view3d_get_view_aligned_coordinate(&vc, min, event->mval, TRUE); eve= addvertlist(vc.em, 0, NULL); invert_m4_m4(imat, vc.obedit->obmat); mul_v3_m4v3(eve->co, imat, min); eve->f= SELECT; } if(use_proj) EM_project_snap_verts(C, vc.ar, vc.obedit, vc.em); WM_event_add_notifier(C, NC_GEOM|ND_DATA, vc.obedit->data); DAG_id_tag_update(vc.obedit->data, 0); return OPERATOR_FINISHED; }