/* adjust bone roll to align Z axis with vector * vec is in local space and is normalized */ float ED_armature_ebone_roll_to_vector(const EditBone *bone, const float align_axis[3], const bool axis_only) { float mat[3][3], nor[3]; float vec[3], align_axis_proj[3], roll = 0.0f; BLI_ASSERT_UNIT_V3(align_axis); sub_v3_v3v3(nor, bone->tail, bone->head); /* If tail == head or the bone is aligned with the axis... */ if (normalize_v3(nor) <= FLT_EPSILON || (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON))) { return roll; } vec_roll_to_mat3_normalized(nor, 0.0f, mat); /* project the new_up_axis along the normal */ project_v3_v3v3_normalized(vec, align_axis, nor); sub_v3_v3v3(align_axis_proj, align_axis, vec); if (axis_only) { if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI_2)) { negate_v3(align_axis_proj); } } roll = angle_v3v3(align_axis_proj, mat[2]); cross_v3_v3v3(vec, mat[2], align_axis_proj); if (dot_v3v3(vec, nor) < 0.0f) { return -roll; } return roll; }
/* adjust bone roll to align Z axis with vector * vec is in local space and is normalized */ float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only) { float mat[3][3], nor[3]; sub_v3_v3v3(nor, bone->tail, bone->head); vec_roll_to_mat3(nor, 0.0f, mat); /* check the bone isn't aligned with the axis */ if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) { float vec[3], align_axis_proj[3], roll; /* project the new_up_axis along the normal */ project_v3_v3v3(vec, align_axis, nor); sub_v3_v3v3(align_axis_proj, align_axis, vec); if (axis_only) { if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI / 2.0)) { negate_v3(align_axis_proj); } } roll = angle_v3v3(align_axis_proj, mat[2]); cross_v3_v3v3(vec, mat[2], align_axis_proj); if (dot_v3v3(vec, nor) < 0) { roll = -roll; } return roll; } return 0.0f; }
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) { float v1_proj[3], v2_proj[3], tproj[3]; float angle; sub_v3_v3v3(v1_proj, v1, v2); sub_v3_v3v3(v2_proj, v3, v2); /* project the vectors onto the axis */ project_v3_v3v3(tproj, v1_proj, axis); sub_v3_v3(v1_proj, tproj); project_v3_v3v3(tproj, v2_proj, axis); sub_v3_v3(v2_proj, tproj); angle = angle_v3v3(v1_proj, v2_proj); /* calculate the sign (reuse 'tproj') */ cross_v3_v3v3(tproj, v2_proj, v1_proj); if (dot_v3v3(tproj, axis) < 0.0f) { angle = ((float)(M_PI * 2.0)) - angle; } return angle; }
/** * angle between 2 vectors defined by 3 coords, about an axis. */ float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3]) { float v1_proj[3], v2_proj[3], tproj[3]; sub_v3_v3v3(v1_proj, v1, v2); sub_v3_v3v3(v2_proj, v3, v2); /* project the vectors onto the axis */ project_v3_v3v3(tproj, v1_proj, axis); sub_v3_v3(v1_proj, tproj); project_v3_v3v3(tproj, v2_proj, axis); sub_v3_v3(v2_proj, tproj); return angle_v3v3(v1_proj, v2_proj); }
static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh) { EditBone *ebone; float dir_act[3]; sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail); for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (EBONE_SELECTABLE(arm, ebone)) { float dir[3]; sub_v3_v3v3(dir, ebone->head, ebone->tail); if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) { ED_armature_edit_bone_select(ebone); } } } }
/* Note this modifies nos_new in-place. */ static void mix_normals( const float mix_factor, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, const float mix_limit, const short mix_mode, const int num_verts, MLoop *mloop, float (*nos_old)[3], float (*nos_new)[3], const int num_loops) { /* Mix with org normals... */ float *facs = NULL, *wfac; float (*no_new)[3], (*no_old)[3]; int i; if (dvert) { facs = MEM_malloc_arrayN((size_t)num_loops, sizeof(*facs), __func__); BKE_defvert_extract_vgroup_to_loopweights( dvert, defgrp_index, num_verts, mloop, num_loops, facs, use_invert_vgroup); } for (i = num_loops, no_new = nos_new, no_old = nos_old, wfac = facs; i--; no_new++, no_old++, wfac++) { const float fac = facs ? *wfac * mix_factor : mix_factor; switch (mix_mode) { case MOD_NORMALEDIT_MIX_ADD: add_v3_v3(*no_new, *no_old); normalize_v3(*no_new); break; case MOD_NORMALEDIT_MIX_SUB: sub_v3_v3(*no_new, *no_old); normalize_v3(*no_new); break; case MOD_NORMALEDIT_MIX_MUL: mul_v3_v3(*no_new, *no_old); normalize_v3(*no_new); break; case MOD_NORMALEDIT_MIX_COPY: break; } interp_v3_v3v3_slerp_safe( *no_new, *no_old, *no_new, (mix_limit < (float)M_PI) ? min_ff(fac, mix_limit / angle_v3v3(*no_new, *no_old)) : fac); } MEM_SAFE_FREE(facs); }
static void axisProjection(TransInfo *t, const float axis[3], const float in[3], float out[3]) { float norm[3], vec[3], factor, angle; float t_con_center[3]; if (is_zero_v3(in)) { return; } copy_v3_v3(t_con_center, t->center_global); /* checks for center being too close to the view center */ viewAxisCorrectCenter(t, t_con_center); angle = fabsf(angle_v3v3(axis, t->viewinv[2])); if (angle > (float)M_PI_2) { angle = (float)M_PI - angle; } angle = RAD2DEGF(angle); /* For when view is parallel to constraint... will cause NaNs otherwise * So we take vertical motion in 3D space and apply it to the * constraint axis. Nice for camera grab + MMB */ if (angle < 5.0f) { project_v3_v3v3(vec, in, t->viewinv[1]); factor = dot_v3v3(t->viewinv[1], vec) * 2.0f; /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */ if (factor < 0.0f) factor *= -factor; else factor *= factor; copy_v3_v3(out, axis); normalize_v3(out); mul_v3_fl(out, -factor); /* -factor makes move down going backwards */ } else { float v[3], i1[3], i2[3]; float v2[3], v4[3]; float norm_center[3]; float plane[3]; getViewVector(t, t_con_center, norm_center); cross_v3_v3v3(plane, norm_center, axis); project_v3_v3v3(vec, in, plane); sub_v3_v3v3(vec, in, vec); add_v3_v3v3(v, vec, t_con_center); getViewVector(t, v, norm); /* give arbitrary large value if projection is impossible */ factor = dot_v3v3(axis, norm); if (1.0f - fabsf(factor) < 0.0002f) { copy_v3_v3(out, axis); if (factor > 0) { mul_v3_fl(out, 1000000000.0f); } else { mul_v3_fl(out, -1000000000.0f); } } else { add_v3_v3v3(v2, t_con_center, axis); add_v3_v3v3(v4, v, norm); isect_line_line_v3(t_con_center, v2, v, v4, i1, i2); sub_v3_v3v3(v, i2, v); sub_v3_v3v3(out, i1, t_con_center); /* possible some values become nan when * viewpoint and object are both zero */ if (!finite(out[0])) out[0] = 0.0f; if (!finite(out[1])) out[1] = 0.0f; if (!finite(out[2])) out[2] = 0.0f; } } }
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, ModifierApplyFlag flag) { DerivedMesh *dm = derivedData; DerivedMesh *result; ScrewModifierData *ltmd = (ScrewModifierData *) md; const int useRenderParams = flag & MOD_APPLY_RENDER; int *origindex; int mpoly_index = 0; unsigned int step; unsigned int i, j; unsigned int i1, i2; unsigned int step_tot = useRenderParams ? ltmd->render_steps : ltmd->steps; const bool do_flip = ltmd->flag & MOD_SCREW_NORMAL_FLIP ? 1 : 0; const int quad_ord[4] = { do_flip ? 3 : 0, do_flip ? 2 : 1, do_flip ? 1 : 2, do_flip ? 0 : 3, }; const int quad_ord_ofs[4] = { do_flip ? 2 : 0, do_flip ? 1 : 1, do_flip ? 0 : 2, do_flip ? 3 : 3, }; unsigned int maxVerts = 0, maxEdges = 0, maxPolys = 0; const unsigned int totvert = (unsigned int)dm->getNumVerts(dm); const unsigned int totedge = (unsigned int)dm->getNumEdges(dm); const unsigned int totpoly = (unsigned int)dm->getNumPolys(dm); unsigned int *edge_poly_map = NULL; /* orig edge to orig poly */ unsigned int *vert_loop_map = NULL; /* orig vert to orig loop */ /* UV Coords */ const unsigned int mloopuv_layers_tot = (unsigned int)CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV); MLoopUV **mloopuv_layers = BLI_array_alloca(mloopuv_layers, mloopuv_layers_tot); float uv_u_scale; float uv_v_minmax[2] = {FLT_MAX, -FLT_MAX}; float uv_v_range_inv; float uv_axis_plane[4]; char axis_char = 'X'; bool close; float angle = ltmd->angle; float screw_ofs = ltmd->screw_ofs; float axis_vec[3] = {0.0f, 0.0f, 0.0f}; float tmp_vec1[3], tmp_vec2[3]; float mat3[3][3]; float mtx_tx[4][4]; /* transform the coords by an object relative to this objects transformation */ float mtx_tx_inv[4][4]; /* inverted */ float mtx_tmp_a[4][4]; unsigned int vc_tot_linked = 0; short other_axis_1, other_axis_2; const float *tmpf1, *tmpf2; unsigned int edge_offset; MPoly *mpoly_orig, *mpoly_new, *mp_new; MLoop *mloop_orig, *mloop_new, *ml_new; MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new; MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base; ScrewVertConnect *vc, *vc_tmp, *vert_connect = NULL; const char mpoly_flag = (ltmd->flag & MOD_SCREW_SMOOTH_SHADING) ? ME_SMOOTH : 0; /* don't do anything? */ if (!totvert) return CDDM_from_template(dm, 0, 0, 0, 0, 0); switch (ltmd->axis) { case 0: other_axis_1 = 1; other_axis_2 = 2; break; case 1: other_axis_1 = 0; other_axis_2 = 2; break; default: /* 2, use default to quiet warnings */ other_axis_1 = 0; other_axis_2 = 1; break; } axis_vec[ltmd->axis] = 1.0f; if (ltmd->ob_axis) { /* calc the matrix relative to the axis object */ invert_m4_m4(mtx_tmp_a, ob->obmat); copy_m4_m4(mtx_tx_inv, ltmd->ob_axis->obmat); mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv); /* calc the axis vec */ mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */ normalize_v3(axis_vec); /* screw */ if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) { /* find the offset along this axis relative to this objects matrix */ float totlen = len_v3(mtx_tx[3]); if (totlen != 0.0f) { float zero[3] = {0.0f, 0.0f, 0.0f}; float cp[3]; screw_ofs = closest_to_line_v3(cp, mtx_tx[3], zero, axis_vec); } else { screw_ofs = 0.0f; } } /* angle */ #if 0 /* cant incluide this, not predictable enough, though quite fun. */ if (ltmd->flag & MOD_SCREW_OBJECT_ANGLE) { float mtx3_tx[3][3]; copy_m3_m4(mtx3_tx, mtx_tx); float vec[3] = {0, 1, 0}; float cross1[3]; float cross2[3]; cross_v3_v3v3(cross1, vec, axis_vec); mul_v3_m3v3(cross2, mtx3_tx, cross1); { float c1[3]; float c2[3]; float axis_tmp[3]; cross_v3_v3v3(c1, cross2, axis_vec); cross_v3_v3v3(c2, axis_vec, c1); angle = angle_v3v3(cross1, c2); cross_v3_v3v3(axis_tmp, cross1, c2); normalize_v3(axis_tmp); if (len_v3v3(axis_tmp, axis_vec) > 1.0f) angle = -angle; } } #endif } else { /* exis char is used by i_rotate*/ axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */ /* useful to be able to use the axis vec in some cases still */ zero_v3(axis_vec); axis_vec[ltmd->axis] = 1.0f; } /* apply the multiplier */ angle *= (float)ltmd->iter; screw_ofs *= (float)ltmd->iter; uv_u_scale = 1.0f / (float)(step_tot); /* multiplying the steps is a bit tricky, this works best */ step_tot = ((step_tot + 1) * ltmd->iter) - (ltmd->iter - 1); /* will the screw be closed? * Note! smaller then FLT_EPSILON * 100 gives problems with float precision so its never closed. */ if (fabsf(screw_ofs) <= (FLT_EPSILON * 100.0f) && fabsf(fabsf(angle) - ((float)M_PI * 2.0f)) <= (FLT_EPSILON * 100.0f)) { close = 1; step_tot--; if (step_tot < 3) step_tot = 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * step_tot) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxPolys = totedge * step_tot; screw_ofs = 0.0f; } else { close = 0; if (step_tot < 3) step_tot = 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * (step_tot - 1)) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxPolys = totedge * (step_tot - 1); } if ((ltmd->flag & MOD_SCREW_UV_STRETCH_U) == 0) { uv_u_scale = (uv_u_scale / (float)ltmd->iter) * (angle / ((float)M_PI * 2.0f)); } result = CDDM_from_template(dm, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys); /* copy verts from mesh */ mvert_orig = dm->getVertArray(dm); medge_orig = dm->getEdgeArray(dm); mvert_new = result->getVertArray(result); mpoly_new = result->getPolyArray(result); mloop_new = result->getLoopArray(result); medge_new = result->getEdgeArray(result); if (!CustomData_has_layer(&result->polyData, CD_ORIGINDEX)) { CustomData_add_layer(&result->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys); } origindex = CustomData_get_layer(&result->polyData, CD_ORIGINDEX); DM_copy_vert_data(dm, result, 0, 0, (int)totvert); /* copy first otherwise this overwrites our own vertex normals */ if (mloopuv_layers_tot) { float zero_co[3] = {0}; plane_from_point_normal_v3(uv_axis_plane, zero_co, axis_vec); } if (mloopuv_layers_tot) { unsigned int uv_lay; for (uv_lay = 0; uv_lay < mloopuv_layers_tot; uv_lay++) { mloopuv_layers[uv_lay] = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, (int)uv_lay); } if (ltmd->flag & MOD_SCREW_UV_STRETCH_V) { for (i = 0, mv_orig = mvert_orig; i < totvert; i++, mv_orig++) { const float v = dist_squared_to_plane_v3(mv_orig->co, uv_axis_plane); uv_v_minmax[0] = min_ff(v, uv_v_minmax[0]); uv_v_minmax[1] = max_ff(v, uv_v_minmax[1]); } uv_v_minmax[0] = sqrtf_signed(uv_v_minmax[0]); uv_v_minmax[1] = sqrtf_signed(uv_v_minmax[1]); } uv_v_range_inv = uv_v_minmax[1] - uv_v_minmax[0]; uv_v_range_inv = uv_v_range_inv ? 1.0f / uv_v_range_inv : 0.0f; } /* Set the locations of the first set of verts */ mv_new = mvert_new; mv_orig = mvert_orig; /* Copy the first set of edges */ med_orig = medge_orig; med_new = medge_new; for (i = 0; i < totedge; i++, med_orig++, med_new++) { med_new->v1 = med_orig->v1; med_new->v2 = med_orig->v2; med_new->crease = med_orig->crease; med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; } /* build polygon -> edge map */ if (totpoly) { MPoly *mp_orig; mpoly_orig = dm->getPolyArray(dm); mloop_orig = dm->getLoopArray(dm); edge_poly_map = MEM_mallocN(sizeof(*edge_poly_map) * totedge, __func__); memset(edge_poly_map, 0xff, sizeof(*edge_poly_map) * totedge); vert_loop_map = MEM_mallocN(sizeof(*vert_loop_map) * totvert, __func__); memset(vert_loop_map, 0xff, sizeof(*vert_loop_map) * totvert); for (i = 0, mp_orig = mpoly_orig; i < totpoly; i++, mp_orig++) { unsigned int loopstart = (unsigned int)mp_orig->loopstart; unsigned int loopend = loopstart + (unsigned int)mp_orig->totloop; MLoop *ml_orig = &mloop_orig[loopstart]; unsigned int k; for (k = loopstart; k < loopend; k++, ml_orig++) { edge_poly_map[ml_orig->e] = i; vert_loop_map[ml_orig->v] = k; /* also order edges based on faces */ if (medge_new[ml_orig->e].v1 != ml_orig->v) { SWAP(unsigned int, medge_new[ml_orig->e].v1, medge_new[ml_orig->e].v2); } } } }
/* the arguments are the desired situation */ void ED_view3d_smooth_view_ex( /* avoid passing in the context */ wmWindowManager *wm, wmWindow *win, ScrArea *sa, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera, const float *ofs, const float *quat, const float *dist, const float *lens, const int smooth_viewtx) { RegionView3D *rv3d = ar->regiondata; struct SmoothView3DStore sms = {{0}}; bool ok = false; /* initialize sms */ view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d); view3d_smooth_view_state_backup(&sms.src, v3d, rv3d); /* if smoothview runs multiple times... */ if (rv3d->sms == NULL) { view3d_smooth_view_state_backup(&sms.org, v3d, rv3d); sms.org_view = rv3d->view; } else { sms.org = rv3d->sms->org; sms.org_view = rv3d->sms->org_view; } /* sms.to_camera = false; */ /* initizlized to zero anyway */ /* note on camera locking, this is a little confusing but works ok. * we may be changing the view 'as if' there is no active camera, but in fact * there is an active camera which is locked to the view. * * In the case where smooth view is moving _to_ a camera we don't want that * camera to be moved or changed, so only when the camera is not being set should * we allow camera option locking to initialize the view settings from the camera. */ if (camera == NULL && oldcamera == NULL) { ED_view3d_camera_lock_init(v3d, rv3d); } /* store the options we want to end with */ if (ofs) copy_v3_v3(sms.dst.ofs, ofs); if (quat) copy_qt_qt(sms.dst.quat, quat); if (dist) sms.dst.dist = *dist; if (lens) sms.dst.lens = *lens; if (camera) { sms.dst.dist = ED_view3d_offset_distance(camera->obmat, ofs, VIEW3D_DIST_FALLBACK); ED_view3d_from_object(camera, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens); sms.to_camera = true; /* restore view3d values in end */ } /* skip smooth viewing for render engine draw */ if (smooth_viewtx && v3d->drawtype != OB_RENDER) { bool changed = false; /* zero means no difference */ if (oldcamera != camera) changed = true; else if (sms.dst.dist != rv3d->dist) changed = true; else if (sms.dst.lens != v3d->lens) changed = true; else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) changed = true; else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) changed = true; /* The new view is different from the old one * so animate the view */ if (changed) { /* original values */ if (oldcamera) { sms.src.dist = ED_view3d_offset_distance(oldcamera->obmat, rv3d->ofs, 0.0f); /* this */ ED_view3d_from_object(oldcamera, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); } /* grid draw as floor */ if ((rv3d->viewlock & RV3D_LOCKED) == 0) { /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */ rv3d->view = RV3D_VIEW_USER; } sms.time_allowed = (double)smooth_viewtx / 1000.0; /* if this is view rotation only * we can decrease the time allowed by * the angle between quats * this means small rotations wont lag */ if (quat && !ofs && !dist) { float vec1[3] = {0, 0, 1}, vec2[3] = {0, 0, 1}; float q1[4], q2[4]; invert_qt_qt(q1, sms.dst.quat); invert_qt_qt(q2, sms.src.quat); mul_qt_v3(q1, vec1); mul_qt_v3(q2, vec2); /* scale the time allowed by the rotation */ sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */ } /* ensure it shows correct */ if (sms.to_camera) { /* use ortho if we move from an ortho view to an ortho camera */ rv3d->persp = (((rv3d->is_persp == false) && (camera->type == OB_CAMERA) && (((Camera *)camera->data)->type == CAM_ORTHO)) ? RV3D_ORTHO : RV3D_PERSP); } rv3d->rflag |= RV3D_NAVIGATING; /* not essential but in some cases the caller will tag the area for redraw, * and in that case we can get a flicker of the 'org' user view but we want to see 'src' */ view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); /* keep track of running timer! */ if (rv3d->sms == NULL) { rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); } *rv3d->sms = sms; if (rv3d->smooth_timer) { WM_event_remove_timer(wm, win, rv3d->smooth_timer); } /* TIMER1 is hardcoded in keymap */ rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); /* max 30 frs/sec */ ok = true; } } /* if we get here nothing happens */ if (ok == false) { if (sms.to_camera == false) { copy_v3_v3(rv3d->ofs, sms.dst.ofs); copy_qt_qt(rv3d->viewquat, sms.dst.quat); rv3d->dist = sms.dst.dist; v3d->lens = sms.dst.lens; ED_view3d_camera_lock_sync(v3d, rv3d); } if (rv3d->viewlock & RV3D_BOXVIEW) { view3d_boxview_copy(sa, ar); } ED_region_tag_redraw(ar); } }
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, int useRenderParams, int UNUSED(isFinalCalc)) { DerivedMesh *dm= derivedData; DerivedMesh *result; ScrewModifierData *ltmd= (ScrewModifierData*) md; int *origindex; int mface_index=0; int step; int i, j; int i1,i2; int step_tot= useRenderParams ? ltmd->render_steps : ltmd->steps; const int do_flip = ltmd->flag & MOD_SCREW_NORMAL_FLIP ? 1 : 0; int maxVerts=0, maxEdges=0, maxFaces=0; int totvert= dm->getNumVerts(dm); int totedge= dm->getNumEdges(dm); char axis_char= 'X', close; float angle= ltmd->angle; float screw_ofs= ltmd->screw_ofs; float axis_vec[3]= {0.0f, 0.0f, 0.0f}; float tmp_vec1[3], tmp_vec2[3]; float mat3[3][3]; float mtx_tx[4][4]; /* transform the coords by an object relative to this objects transformation */ float mtx_tx_inv[4][4]; /* inverted */ float mtx_tmp_a[4][4]; int vc_tot_linked= 0; short other_axis_1, other_axis_2; float *tmpf1, *tmpf2; MFace *mface_new, *mf_new; MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new; MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base; ScrewVertConnect *vc, *vc_tmp, *vert_connect= NULL; /* dont do anything? */ if (!totvert) return CDDM_from_template(dm, 0, 0, 0); switch(ltmd->axis) { case 0: other_axis_1=1; other_axis_2=2; break; case 1: other_axis_1=0; other_axis_2=2; break; default: /* 2, use default to quiet warnings */ other_axis_1=0; other_axis_2=1; break; } axis_vec[ltmd->axis]= 1.0f; if (ltmd->ob_axis) { /* calc the matrix relative to the axis object */ invert_m4_m4(mtx_tmp_a, ob->obmat); copy_m4_m4(mtx_tx_inv, ltmd->ob_axis->obmat); mul_m4_m4m4(mtx_tx, mtx_tx_inv, mtx_tmp_a); /* calc the axis vec */ mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */ normalize_v3(axis_vec); /* screw */ if(ltmd->flag & MOD_SCREW_OBJECT_OFFSET) { /* find the offset along this axis relative to this objects matrix */ float totlen = len_v3(mtx_tx[3]); if(totlen != 0.0f) { float zero[3]= {0.0f, 0.0f, 0.0f}; float cp[3]; screw_ofs= closest_to_line_v3(cp, mtx_tx[3], zero, axis_vec); } else { screw_ofs= 0.0f; } } /* angle */ #if 0 // cant incluide this, not predictable enough, though quite fun,. if(ltmd->flag & MOD_SCREW_OBJECT_ANGLE) { float mtx3_tx[3][3]; copy_m3_m4(mtx3_tx, mtx_tx); float vec[3] = {0,1,0}; float cross1[3]; float cross2[3]; cross_v3_v3v3(cross1, vec, axis_vec); mul_v3_m3v3(cross2, mtx3_tx, cross1); { float c1[3]; float c2[3]; float axis_tmp[3]; cross_v3_v3v3(c1, cross2, axis_vec); cross_v3_v3v3(c2, axis_vec, c1); angle= angle_v3v3(cross1, c2); cross_v3_v3v3(axis_tmp, cross1, c2); normalize_v3(axis_tmp); if(len_v3v3(axis_tmp, axis_vec) > 1.0f) angle= -angle; } } #endif } else { /* exis char is used by i_rotate*/ axis_char += ltmd->axis; /* 'X' + axis */ /* useful to be able to use the axis vec in some cases still */ zero_v3(axis_vec); axis_vec[ltmd->axis]= 1.0f; } /* apply the multiplier */ angle *= ltmd->iter; screw_ofs *= ltmd->iter; /* multiplying the steps is a bit tricky, this works best */ step_tot = ((step_tot + 1) * ltmd->iter) - (ltmd->iter - 1); /* will the screw be closed? * Note! smaller then FLT_EPSILON*100 gives problems with float precision so its never closed. */ if (fabsf(screw_ofs) <= (FLT_EPSILON*100.0f) && fabsf(fabsf(angle) - ((float)M_PI * 2.0f)) <= (FLT_EPSILON*100.0f)) { close= 1; step_tot--; if(step_tot < 3) step_tot= 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * step_tot) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxFaces = totedge * step_tot; screw_ofs= 0.0f; } else { close= 0; if(step_tot < 3) step_tot= 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * (step_tot-1)) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxFaces = totedge * (step_tot-1); } result= CDDM_from_template(dm, maxVerts, maxEdges, maxFaces); /* copy verts from mesh */ mvert_orig = dm->getVertArray(dm); medge_orig = dm->getEdgeArray(dm); mvert_new = result->getVertArray(result); mface_new = result->getFaceArray(result); medge_new = result->getEdgeArray(result); origindex= result->getFaceDataArray(result, CD_ORIGINDEX); DM_copy_vert_data(dm, result, 0, 0, totvert); /* copy first otherwise this overwrites our own vertex normals */ /* Set the locations of the first set of verts */ mv_new= mvert_new; mv_orig= mvert_orig; /* Copy the first set of edges */ med_orig= medge_orig; med_new= medge_new; for (i=0; i < totedge; i++, med_orig++, med_new++) { med_new->v1= med_orig->v1; med_new->v2= med_orig->v2; med_new->crease= med_orig->crease; med_new->flag= med_orig->flag & ~ME_LOOSEEDGE; } if(ltmd->flag & MOD_SCREW_NORMAL_CALC) { /* * Normal Calculation (for face flipping) * Sort edge verts for correct face flipping * NOT REALLY NEEDED but face flipping is nice. * * */ /* Notice! * * Since we are only ordering the edges here it can avoid mallocing the * extra space by abusing the vert array berfore its filled with new verts. * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3 * so its safe to use the second 2 thrids of MVert the array for vert_connect, * just make sure ScrewVertConnect struct is no more then twice as big as MVert, * at the moment there is no chance of that being a problem, * unless MVert becomes half its current size. * * once the edges are ordered, vert_connect is not needed and it can be used for verts * * This makes the modifier faster with one less alloc. */ vert_connect= MEM_mallocN(sizeof(ScrewVertConnect) * totvert, "ScrewVertConnect"); //vert_connect= (ScrewVertConnect *) &medge_new[totvert]; /* skip the first slice of verts */ vc= vert_connect; /* Copy Vert Locations */ /* - We can do this in a later loop - only do here if no normal calc */ if (!totedge) { for (i=0; i < totvert; i++, mv_orig++, mv_new++) { copy_v3_v3(mv_new->co, mv_orig->co); normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */ } } else { /*printf("\n\n\n\n\nStarting Modifier\n");*/ /* set edge users */ med_new= medge_new; mv_new= mvert_new; if (ltmd->ob_axis) { /*mtx_tx is initialized early on */ for (i=0; i < totvert; i++, mv_new++, mv_orig++, vc++) { vc->co[0]= mv_new->co[0]= mv_orig->co[0]; vc->co[1]= mv_new->co[1]= mv_orig->co[1]; vc->co[2]= mv_new->co[2]= mv_orig->co[2]; vc->flag= 0; vc->e[0]= vc->e[1]= NULL; vc->v[0]= vc->v[1]= -1; mul_m4_v3(mtx_tx, vc->co); /* length in 2d, dont sqrt because this is only for comparison */ vc->dist = vc->co[other_axis_1]*vc->co[other_axis_1] + vc->co[other_axis_2]*vc->co[other_axis_2]; /* printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);*/ } } else { for (i=0; i < totvert; i++, mv_new++, mv_orig++, vc++) { vc->co[0]= mv_new->co[0]= mv_orig->co[0]; vc->co[1]= mv_new->co[1]= mv_orig->co[1]; vc->co[2]= mv_new->co[2]= mv_orig->co[2]; vc->flag= 0; vc->e[0]= vc->e[1]= NULL; vc->v[0]= vc->v[1]= -1; /* length in 2d, dont sqrt because this is only for comparison */ vc->dist = vc->co[other_axis_1]*vc->co[other_axis_1] + vc->co[other_axis_2]*vc->co[other_axis_2]; /* printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);*/ } } /* this loop builds connectivity info for verts */ for (i=0; i<totedge; i++, med_new++) { vc= &vert_connect[med_new->v1]; if (vc->v[0] == -1) { /* unused */ vc->v[0]= med_new->v2; vc->e[0]= med_new; } else if (vc->v[1] == -1) { vc->v[1]= med_new->v2; vc->e[1]= med_new; } else { vc->v[0]= vc->v[1]= -2; /* erro value - dont use, 3 edges on vert */ } vc= &vert_connect[med_new->v2]; /* same as above but swap v1/2 */ if (vc->v[0] == -1) { /* unused */ vc->v[0]= med_new->v1; vc->e[0]= med_new; } else if (vc->v[1] == -1) { vc->v[1]= med_new->v1; vc->e[1]= med_new; } else { vc->v[0]= vc->v[1]= -2; /* erro value - dont use, 3 edges on vert */ } } /* find the first vert */ vc= vert_connect; for (i=0; i < totvert; i++, vc++) { /* Now do search for connected verts, order all edges and flip them * so resulting faces are flipped the right way */ vc_tot_linked= 0; /* count the number of linked verts for this loop */ if (vc->flag == 0) { int v_best=-1, ed_loop_closed=0; /* vert and vert new */ ScrewVertIter lt_iter; int ed_loop_flip= 0; /* compiler complains if not initialized, but it should be initialized below */ float fl= -1.0f; /*printf("Loop on connected vert: %i\n", i);*/ for(j=0; j<2; j++) { /*printf("\tSide: %i\n", j);*/ screwvert_iter_init(<_iter, vert_connect, i, j); if (j == 1) { screwvert_iter_step(<_iter); } while (lt_iter.v_poin) { /*printf("\t\tVERT: %i\n", lt_iter.v);*/ if (lt_iter.v_poin->flag) { /*printf("\t\t\tBreaking Found end\n");*/ //endpoints[0]= endpoints[1]= -1; ed_loop_closed= 1; /* circle */ break; } lt_iter.v_poin->flag= 1; vc_tot_linked++; /*printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist);*/ if (fl <= lt_iter.v_poin->dist) { fl= lt_iter.v_poin->dist; v_best= lt_iter.v; /*printf("\t\t\tVERT BEST: %i\n", v_best);*/ } screwvert_iter_step(<_iter); if (!lt_iter.v_poin) { /*printf("\t\t\tFound End Also Num %i\n", j);*/ /*endpoints[j]= lt_iter.v_other;*/ /* other is still valid */ break; } } } /* now we have a collection of used edges. flip their edges the right way*/ /*if (v_best != -1) - */ /*printf("Done Looking - vc_tot_linked: %i\n", vc_tot_linked);*/ if (vc_tot_linked>1) { float vf_1, vf_2, vf_best; vc_tmp= &vert_connect[v_best]; tmpf1= vert_connect[vc_tmp->v[0]].co; tmpf2= vert_connect[vc_tmp->v[1]].co; /* edge connects on each side! */ if ((vc_tmp->v[0] > -1) && (vc_tmp->v[1] > -1)) { /*printf("Verts on each side (%i %i)\n", vc_tmp->v[0], vc_tmp->v[1]);*/ /* find out which is higher */ vf_1= tmpf1[ltmd->axis]; vf_2= tmpf2[ltmd->axis]; vf_best= vc_tmp->co[ltmd->axis]; if (vf_1 < vf_best && vf_best < vf_2) { ed_loop_flip= 0; } else if (vf_1 > vf_best && vf_best > vf_2) { ed_loop_flip= 1; } else { /* not so simple to work out which edge is higher */ sub_v3_v3v3(tmp_vec1, tmpf1, vc_tmp->co); sub_v3_v3v3(tmp_vec2, tmpf2, vc_tmp->co); normalize_v3(tmp_vec1); normalize_v3(tmp_vec2); if (tmp_vec1[ltmd->axis] < tmp_vec2[ltmd->axis]) { ed_loop_flip= 1; } else { ed_loop_flip= 0; } } } else if (vc_tmp->v[0] >= 0) { /*vertex only connected on 1 side */ /*printf("Verts on ONE side (%i %i)\n", vc_tmp->v[0], vc_tmp->v[1]);*/ if (tmpf1[ltmd->axis] < vc_tmp->co[ltmd->axis]) { /* best is above */ ed_loop_flip= 1; } else { /* best is below or even... in even case we cant know whet to do. */ ed_loop_flip= 0; } }/* else { printf("No Connected ___\n"); }*/ /*printf("flip direction %i\n", ed_loop_flip);*/ /* switch the flip option if set * note: flip is now done at face level so copying vgroup slizes is easier */ /* if (do_flip) ed_loop_flip= !ed_loop_flip; */ if (angle < 0.0f) ed_loop_flip= !ed_loop_flip; /* if its closed, we only need 1 loop */ for(j=ed_loop_closed; j<2; j++) { /*printf("Ordering Side J %i\n", j);*/ screwvert_iter_init(<_iter, vert_connect, v_best, j); /*printf("\n\nStarting - Loop\n");*/ lt_iter.v_poin->flag= 1; /* so a non loop will traverse the other side */ /* If this is the vert off the best vert and * the best vert has 2 edges connected too it * then swap the flip direction */ if (j == 1 && (vc_tmp->v[0] > -1) && (vc_tmp->v[1] > -1)) ed_loop_flip= !ed_loop_flip; while (lt_iter.v_poin && lt_iter.v_poin->flag != 2) { /*printf("\tOrdering Vert V %i\n", lt_iter.v);*/ lt_iter.v_poin->flag= 2; if (lt_iter.e) { if (lt_iter.v == lt_iter.e->v1) { if (ed_loop_flip == 0) { /*printf("\t\t\tFlipping 0\n");*/ SWAP(int, lt_iter.e->v1, lt_iter.e->v2); }/* else { printf("\t\t\tFlipping Not 0\n"); }*/ } else if (lt_iter.v == lt_iter.e->v2) { if (ed_loop_flip == 1) { /*printf("\t\t\tFlipping 1\n");*/ SWAP(int, lt_iter.e->v1, lt_iter.e->v2); }/* else { printf("\t\t\tFlipping Not 1\n"); }*/ }/* else { printf("\t\tIncorrect edge topology"); }*/ }/* else { printf("\t\tNo Edge at this point\n"); }*/ screwvert_iter_step(<_iter); } }
/* the arguments are the desired situation */ void smooth_view(bContext *C, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera, float *ofs, float *quat, float *dist, float *lens) { wmWindowManager *wm= CTX_wm_manager(C); wmWindow *win= CTX_wm_window(C); ScrArea *sa= CTX_wm_area(C); RegionView3D *rv3d= ar->regiondata; struct SmoothViewStore sms= {0}; short ok= FALSE; /* initialize sms */ copy_v3_v3(sms.new_ofs, rv3d->ofs); copy_qt_qt(sms.new_quat, rv3d->viewquat); sms.new_dist= rv3d->dist; sms.new_lens= v3d->lens; sms.to_camera= 0; /* note on camera locking, this is a little confusing but works ok. * we may be changing the view 'as if' there is no active camera, but infact * there is an active camera which is locked to the view. * * In the case where smooth view is moving _to_ a camera we dont want that * camera to be moved or changed, so only when the camera is not being set should * we allow camera option locking to initialize the view settings from the camera. */ if(camera == NULL && oldcamera == NULL) { ED_view3d_camera_lock_init(v3d, rv3d); } /* store the options we want to end with */ if(ofs) copy_v3_v3(sms.new_ofs, ofs); if(quat) copy_qt_qt(sms.new_quat, quat); if(dist) sms.new_dist= *dist; if(lens) sms.new_lens= *lens; if (camera) { ED_view3d_from_object(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens); sms.to_camera= 1; /* restore view3d values in end */ } if (C && U.smooth_viewtx) { int changed = 0; /* zero means no difference */ if (oldcamera != camera) changed = 1; else if (sms.new_dist != rv3d->dist) changed = 1; else if (sms.new_lens != v3d->lens) changed = 1; else if (!equals_v3v3(sms.new_ofs, rv3d->ofs)) changed = 1; else if (!equals_v4v4(sms.new_quat, rv3d->viewquat)) changed = 1; /* The new view is different from the old one * so animate the view */ if (changed) { /* original values */ if (oldcamera) { sms.orig_dist= rv3d->dist; // below function does weird stuff with it... ED_view3d_from_object(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens); } else { copy_v3_v3(sms.orig_ofs, rv3d->ofs); copy_qt_qt(sms.orig_quat, rv3d->viewquat); sms.orig_dist= rv3d->dist; sms.orig_lens= v3d->lens; } /* grid draw as floor */ if((rv3d->viewlock & RV3D_LOCKED)==0) { /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */ sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view; rv3d->view= RV3D_VIEW_USER; } sms.time_allowed= (double)U.smooth_viewtx / 1000.0; /* if this is view rotation only * we can decrease the time allowed by * the angle between quats * this means small rotations wont lag */ if (quat && !ofs && !dist) { float vec1[3]={0,0,1}, vec2[3]= {0,0,1}; float q1[4], q2[4]; invert_qt_qt(q1, sms.new_quat); invert_qt_qt(q2, sms.orig_quat); mul_qt_v3(q1, vec1); mul_qt_v3(q2, vec2); /* scale the time allowed by the rotation */ sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */ } /* ensure it shows correct */ if(sms.to_camera) rv3d->persp= RV3D_PERSP; rv3d->rflag |= RV3D_NAVIGATING; /* keep track of running timer! */ if(rv3d->sms==NULL) rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d"); *rv3d->sms= sms; if(rv3d->smooth_timer) WM_event_remove_timer(wm, win, rv3d->smooth_timer); /* TIMER1 is hardcoded in keymap */ rv3d->smooth_timer= WM_event_add_timer(wm, win, TIMER1, 1.0/100.0); /* max 30 frs/sec */ ok= TRUE; } } /* if we get here nothing happens */ if(ok == FALSE) { if(sms.to_camera==0) { copy_v3_v3(rv3d->ofs, sms.new_ofs); copy_qt_qt(rv3d->viewquat, sms.new_quat); rv3d->dist = sms.new_dist; v3d->lens = sms.new_lens; } if(rv3d->viewlock & RV3D_BOXVIEW) view3d_boxview_copy(sa, ar); ED_region_tag_redraw(ar); } }