/** * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #ifndef WIN32 #include #else #include #endif #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_action_types.h" /* for some special action-editor settings */ #include "DNA_constraint_types.h" #include "DNA_ipo_types.h" /* some silly ipo flag */ #include "DNA_listBase.h" #include "DNA_meshdata_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" /* PET modes */ #include "DNA_screen_types.h" /* area dimensions */ #include "DNA_texture_types.h" #include "DNA_userdef_types.h" #include "DNA_view3d_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "RNA_access.h" //#include "BIF_editview.h" /* arrows_move_cursor */ #include "BIF_gl.h" #include "BIF_glutil.h" //#include "BIF_mywindow.h" //#include "BIF_resources.h" //#include "BIF_screen.h" //#include "BIF_space.h" /* undo */ //#include "BIF_toets.h" /* persptoetsen */ //#include "BIF_mywindow.h" /* warp_pointer */ //#include "BIF_toolbox.h" /* notice */ //#include "BIF_editmesh.h" //#include "BIF_editsima.h" //#include "BIF_editparticle.h" #include "BKE_action.h" #include "BKE_nla.h" //#include "BKE_bad_level_calls.h"/* popmenu and error */ #include "BKE_bmesh.h" #include "BKE_context.h" #include "BKE_constraint.h" #include "BKE_global.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_utildefines.h" #include "BKE_context.h" #include "BKE_unit.h" //#include "BSE_view.h" #include "ED_image.h" #include "ED_keyframing.h" #include "ED_screen.h" #include "ED_space_api.h" #include "ED_markers.h" #include "ED_util.h" #include "ED_view3d.h" #include "ED_mesh.h" #include "UI_view2d.h" #include "WM_types.h" #include "WM_api.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" #include "BLI_editVert.h" #include "BLI_ghash.h" #include "BLI_linklist.h" #include "PIL_time.h" /* sleep */ #include "UI_resources.h" //#include "blendef.h" // //#include "mydevice.h" #include "transform.h" /* ************************** SPACE DEPENDANT CODE **************************** */ void setTransformViewMatrices(TransInfo *t) { if(t->spacetype==SPACE_VIEW3D && t->ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = t->ar->regiondata; Mat4CpyMat4(t->viewmat, rv3d->viewmat); Mat4CpyMat4(t->viewinv, rv3d->viewinv); Mat4CpyMat4(t->persmat, rv3d->persmat); Mat4CpyMat4(t->persinv, rv3d->persinv); t->persp = rv3d->persp; } else { Mat4One(t->viewmat); Mat4One(t->viewinv); Mat4One(t->persmat); Mat4One(t->persinv); t->persp = V3D_ORTHO; } calculateCenter2D(t); } void convertViewVec(TransInfo *t, float *vec, short dx, short dy) { if (t->spacetype==SPACE_VIEW3D) { if (t->ar->regiontype == RGN_TYPE_WINDOW) { window_to_3d_delta(t->ar, vec, dx, dy); } } else if(t->spacetype==SPACE_IMAGE) { View2D *v2d = t->view; float divx, divy, aspx, aspy; ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); divx= v2d->mask.xmax-v2d->mask.xmin; divy= v2d->mask.ymax-v2d->mask.ymin; vec[0]= aspx*(v2d->cur.xmax-v2d->cur.xmin)*(dx)/divx; vec[1]= aspy*(v2d->cur.ymax-v2d->cur.ymin)*(dy)/divy; vec[2]= 0.0f; } else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { View2D *v2d = t->view; float divx, divy; divx= v2d->mask.xmax-v2d->mask.xmin; divy= v2d->mask.ymax-v2d->mask.ymin; vec[0]= (v2d->cur.xmax-v2d->cur.xmin)*(dx) / (divx); vec[1]= (v2d->cur.ymax-v2d->cur.ymin)*(dy) / (divy); vec[2]= 0.0f; } else if(t->spacetype==SPACE_NODE) { View2D *v2d = &t->ar->v2d; float divx, divy; divx= v2d->mask.xmax-v2d->mask.xmin; divy= v2d->mask.ymax-v2d->mask.ymin; vec[0]= (v2d->cur.xmax-v2d->cur.xmin)*(dx)/divx; vec[1]= (v2d->cur.ymax-v2d->cur.ymin)*(dy)/divy; vec[2]= 0.0f; } else if(t->spacetype==SPACE_SEQ) { View2D *v2d = &t->ar->v2d; float divx, divy; divx= v2d->mask.xmax-v2d->mask.xmin; divy= v2d->mask.ymax-v2d->mask.ymin; vec[0]= (v2d->cur.xmax-v2d->cur.xmin)*(dx)/divx; vec[1]= (v2d->cur.ymax-v2d->cur.ymin)*(dy)/divy; vec[2]= 0.0f; } } void projectIntView(TransInfo *t, float *vec, int *adr) { if (t->spacetype==SPACE_VIEW3D) { if(t->ar->regiontype == RGN_TYPE_WINDOW) project_int_noclip(t->ar, vec, adr); } else if(t->spacetype==SPACE_IMAGE) { float aspx, aspy, v[2]; ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); v[0]= vec[0]/aspx; v[1]= vec[1]/aspy; UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr+1); } else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { int out[2] = {0, 0}; UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], out, out+1); adr[0]= out[0]; adr[1]= out[1]; } else if(t->spacetype==SPACE_SEQ) { /* XXX not tested yet, but should work */ int out[2] = {0, 0}; UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], out, out+1); adr[0]= out[0]; adr[1]= out[1]; } } void projectFloatView(TransInfo *t, float *vec, float *adr) { if (t->spacetype==SPACE_VIEW3D) { if(t->ar->regiontype == RGN_TYPE_WINDOW) project_float_noclip(t->ar, vec, adr); } else if(t->spacetype==SPACE_IMAGE) { int a[2]; projectIntView(t, vec, a); adr[0]= a[0]; adr[1]= a[1]; } else if(ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { int a[2]; projectIntView(t, vec, a); adr[0]= a[0]; adr[1]= a[1]; } } void applyAspectRatio(TransInfo *t, float *vec) { SpaceImage *sima= t->sa->spacedata.first; if ((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) { float aspx, aspy; if((sima->flag & SI_COORDFLOATS)==0) { int width, height; ED_space_image_size(sima, &width, &height); vec[0] *= width; vec[1] *= height; } ED_space_image_uv_aspect(sima, &aspx, &aspy); vec[0] /= aspx; vec[1] /= aspy; } } void removeAspectRatio(TransInfo *t, float *vec) { SpaceImage *sima= t->sa->spacedata.first; if ((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) { float aspx, aspy; if((sima->flag & SI_COORDFLOATS)==0) { int width, height; ED_space_image_size(sima, &width, &height); vec[0] /= width; vec[1] /= height; } ED_space_image_uv_aspect(sima, &aspx, &aspy); vec[0] *= aspx; vec[1] *= aspy; } } static void viewRedrawForce(bContext *C, TransInfo *t) { if (t->spacetype == SPACE_VIEW3D) { /* Do we need more refined tags? */ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); /* for realtime animation record - send notifiers recognised by animation editors */ if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) WM_event_add_notifier(C, NC_OBJECT|ND_KEYS, NULL); } else if (t->spacetype == SPACE_ACTION) { //SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); } else if (t->spacetype == SPACE_IPO) { //SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first; WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL); } else if (t->spacetype == SPACE_NLA) { WM_event_add_notifier(C, NC_ANIMATION|ND_NLA_EDIT, NULL); } else if(t->spacetype == SPACE_NODE) { //ED_area_tag_redraw(t->sa); WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL); } else if(t->spacetype == SPACE_SEQ) { WM_event_add_notifier(C, NC_SCENE|ND_SEQUENCER, NULL); } else if (t->spacetype==SPACE_IMAGE) { // XXX how to deal with lock? #if 0 SpaceImage *sima= (SpaceImage*)t->sa->spacedata.first; if(sima->lock) force_draw_plus(SPACE_VIEW3D, 0); else force_draw(0); #endif WM_event_add_notifier(C, NC_GEOM|ND_DATA, t->obedit->data); } } static void viewRedrawPost(TransInfo *t) { ED_area_headerprint(t->sa, NULL); #if 0 // TRANSFORM_FIX_ME if(t->spacetype==SPACE_VIEW3D) { allqueue(REDRAWBUTSOBJECT, 0); allqueue(REDRAWVIEW3D, 0); } else if(t->spacetype==SPACE_IMAGE) { allqueue(REDRAWIMAGE, 0); allqueue(REDRAWVIEW3D, 0); } else if(ELEM3(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) { allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWNLA, 0); allqueue(REDRAWIPO, 0); allqueue(REDRAWTIME, 0); allqueue(REDRAWBUTSOBJECT, 0); } scrarea_queue_headredraw(curarea); #endif } /* ************************** TRANSFORMATIONS **************************** */ void BIF_selectOrientation() { #if 0 // TRANSFORM_FIX_ME short val; char *str_menu = BIF_menustringTransformOrientation("Orientation"); val= pupmenu(str_menu); MEM_freeN(str_menu); if(val >= 0) { G.vd->twmode = val; } #endif } static void view_editmove(unsigned short event) { #if 0 // TRANSFORM_FIX_ME int refresh = 0; /* Regular: Zoom in */ /* Shift: Scroll up */ /* Ctrl: Scroll right */ /* Alt-Shift: Rotate up */ /* Alt-Ctrl: Rotate right */ /* only work in 3D window for now * In the end, will have to send to event to a 2D window handler instead */ if (Trans.flag & T_2D_EDIT) return; switch(event) { case WHEELUPMOUSE: if( G.qual & LR_SHIFTKEY ) { if( G.qual & LR_ALTKEY ) { G.qual &= ~LR_SHIFTKEY; persptoetsen(PAD2); G.qual |= LR_SHIFTKEY; } else { persptoetsen(PAD2); } } else if( G.qual & LR_CTRLKEY ) { if( G.qual & LR_ALTKEY ) { G.qual &= ~LR_CTRLKEY; persptoetsen(PAD4); G.qual |= LR_CTRLKEY; } else { persptoetsen(PAD4); } } else if(U.uiflag & USER_WHEELZOOMDIR) persptoetsen(PADMINUS); else persptoetsen(PADPLUSKEY); refresh = 1; break; case WHEELDOWNMOUSE: if( G.qual & LR_SHIFTKEY ) { if( G.qual & LR_ALTKEY ) { G.qual &= ~LR_SHIFTKEY; persptoetsen(PAD8); G.qual |= LR_SHIFTKEY; } else { persptoetsen(PAD8); } } else if( G.qual & LR_CTRLKEY ) { if( G.qual & LR_ALTKEY ) { G.qual &= ~LR_CTRLKEY; persptoetsen(PAD6); G.qual |= LR_CTRLKEY; } else { persptoetsen(PAD6); } } else if(U.uiflag & USER_WHEELZOOMDIR) persptoetsen(PADPLUSKEY); else persptoetsen(PADMINUS); refresh = 1; break; } if (refresh) setTransformViewMatrices(&Trans); #endif } #if 0 static char *transform_to_undostr(TransInfo *t) { switch (t->mode) { case TFM_TRANSLATION: return "Translate"; case TFM_ROTATION: return "Rotate"; case TFM_RESIZE: return "Scale"; case TFM_TOSPHERE: return "To Sphere"; case TFM_SHEAR: return "Shear"; case TFM_WARP: return "Warp"; case TFM_SHRINKFATTEN: return "Shrink/Fatten"; case TFM_TILT: return "Tilt"; case TFM_TRACKBALL: return "Trackball"; case TFM_PUSHPULL: return "Push/Pull"; case TFM_BEVEL: return "Bevel"; case TFM_BWEIGHT: return "Bevel Weight"; case TFM_CREASE: return "Crease"; case TFM_BONESIZE: return "Bone Width"; case TFM_BONE_ENVELOPE: return "Bone Envelope"; case TFM_TIME_TRANSLATE: return "Translate Anim. Data"; case TFM_TIME_SCALE: return "Scale Anim. Data"; case TFM_TIME_SLIDE: return "Time Slide"; case TFM_BAKE_TIME: return "Key Time"; case TFM_MIRROR: return "Mirror"; } return "Transform"; } #endif /* ************************************************* */ /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ #define TFM_MODAL_CANCEL 1 #define TFM_MODAL_CONFIRM 2 #define TFM_MODAL_TRANSLATE 3 #define TFM_MODAL_ROTATE 4 #define TFM_MODAL_RESIZE 5 #define TFM_MODAL_SNAP_GEARS 6 #define TFM_MODAL_SNAP_GEARS_OFF 7 /* called in transform_ops.c, on each regeneration of keymaps */ void transform_modal_keymap(wmWindowManager *wm) { static EnumPropertyItem modal_items[] = { {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""}, {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""}, {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""}, {TFM_MODAL_SNAP_GEARS, "SNAP_GEARS", 0, "Snap On", ""}, {TFM_MODAL_SNAP_GEARS_OFF, "SNAP_GEARS_OFF", 0, "Snap Off", ""}, {0, NULL, 0, NULL, NULL}}; wmKeyMap *keymap= WM_modalkeymap_get(wm, "Transform Modal Map"); /* this function is called for each spacetype, only needs to add map once */ if(keymap) return; keymap= WM_modalkeymap_add(wm, "Transform Modal Map", modal_items); /* items for modal map */ WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL); WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, TFM_MODAL_CONFIRM); WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE); WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE); WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE); WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_GEARS); WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_GEARS_OFF); /* assign map to operators */ WM_modalkeymap_assign(keymap, "TFM_OT_transform"); WM_modalkeymap_assign(keymap, "TFM_OT_translate"); WM_modalkeymap_assign(keymap, "TFM_OT_rotate"); WM_modalkeymap_assign(keymap, "TFM_OT_tosphere"); WM_modalkeymap_assign(keymap, "TFM_OT_resize"); WM_modalkeymap_assign(keymap, "TFM_OT_shear"); WM_modalkeymap_assign(keymap, "TFM_OT_warp"); WM_modalkeymap_assign(keymap, "TFM_OT_shrink_fatten"); WM_modalkeymap_assign(keymap, "TFM_OT_tilt"); WM_modalkeymap_assign(keymap, "TFM_OT_trackball"); } void transformEvent(TransInfo *t, wmEvent *event) { float mati[3][3] = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; char cmode = constraintModeToChar(t); t->redraw |= handleMouseInput(t, &t->mouse, event); if (event->type == MOUSEMOVE) { t->mval[0] = event->x - t->ar->winrct.xmin; t->mval[1] = event->y - t->ar->winrct.ymin; t->redraw = 1; applyMouseInput(t, &t->mouse, t->mval, t->values); } /* handle modal keymap first */ if (event->type == EVT_MODAL_MAP) { switch (event->val) { case TFM_MODAL_CANCEL: t->state = TRANS_CANCEL; break; case TFM_MODAL_CONFIRM: t->state = TRANS_CONFIRM; break; case TFM_MODAL_TRANSLATE: /* only switch when... */ if( ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) { resetTransRestrictions(t); restoreTransObjects(t); initTranslation(t); initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case TFM_MODAL_ROTATE: /* only switch when... */ if( ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) { resetTransRestrictions(t); if (t->mode == TFM_ROTATION) { restoreTransObjects(t); initTrackball(t); } else { restoreTransObjects(t); initRotation(t); } initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case TFM_MODAL_RESIZE: /* only switch when... */ if( ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) { resetTransRestrictions(t); restoreTransObjects(t); initResize(t); initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case TFM_MODAL_SNAP_GEARS: t->modifiers |= MOD_SNAP_GEARS; t->redraw = 1; break; case TFM_MODAL_SNAP_GEARS_OFF: t->modifiers &= ~MOD_SNAP_GEARS; t->redraw = 1; break; } } /* else do non-mapped events */ else if (event->val==KM_PRESS) { switch (event->type){ case RIGHTMOUSE: t->state = TRANS_CANCEL; break; /* enforce redraw of transform when modifiers are used */ case LEFTCTRLKEY: case RIGHTCTRLKEY: t->modifiers |= MOD_SNAP_GEARS; t->redraw = 1; break; case LEFTSHIFTKEY: case RIGHTSHIFTKEY: t->modifiers |= MOD_CONSTRAINT_PLANE; t->redraw = 1; break; case SPACEKEY: if ((t->spacetype==SPACE_VIEW3D) && event->alt) { #if 0 // TRANSFORM_FIX_ME short mval[2]; getmouseco_sc(mval); BIF_selectOrientation(); calc_manipulator_stats(curarea); Mat3CpyMat4(t->spacemtx, G.vd->twmat); warp_pointer(mval[0], mval[1]); #endif } else { t->state = TRANS_CONFIRM; } break; case MIDDLEMOUSE: if ((t->flag & T_NO_CONSTRAINT)==0) { /* exception for switching to dolly, or trackball, in camera view */ if (t->flag & T_CAMERA) { if (t->mode==TFM_TRANSLATION) setLocalConstraint(t, (CON_AXIS2), "along local Z"); else if (t->mode==TFM_ROTATION) { restoreTransObjects(t); initTrackball(t); } } else { t->modifiers |= MOD_CONSTRAINT_SELECT; if (t->con.mode & CON_APPLY) { stopConstraint(t); } else { if (event->shift) { initSelectConstraint(t, t->spacemtx); } else { /* bit hackish... but it prevents mmb select to print the orientation from menu */ strcpy(t->spacename, "global"); initSelectConstraint(t, mati); } postSelectConstraint(t); } } t->redraw = 1; } break; case ESCKEY: t->state = TRANS_CANCEL; break; case PADENTER: case RETKEY: t->state = TRANS_CONFIRM; break; case GKEY: /* only switch when... */ if( ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) { resetTransRestrictions(t); restoreTransObjects(t); initTranslation(t); initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case SKEY: /* only switch when... */ if( ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) { resetTransRestrictions(t); restoreTransObjects(t); initResize(t); initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case RKEY: /* only switch when... */ if( ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) { resetTransRestrictions(t); if (t->mode == TFM_ROTATION) { restoreTransObjects(t); initTrackball(t); } else { restoreTransObjects(t); initRotation(t); } initSnapping(t, NULL); // need to reinit after mode change t->redraw = 1; } break; case CKEY: if (event->alt) { t->flag ^= T_PROP_CONNECTED; sort_trans_data_dist(t); calculatePropRatio(t); t->redraw= 1; } else { stopConstraint(t); t->redraw = 1; } break; case XKEY: if ((t->flag & T_NO_CONSTRAINT)==0) { if (cmode == 'X') { if (t->flag & T_2D_EDIT) { stopConstraint(t); } else { if (t->con.mode & CON_USER) { stopConstraint(t); } else { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setUserConstraint(t, (CON_AXIS0), "along %s X"); else if (t->modifiers & MOD_CONSTRAINT_PLANE) setUserConstraint(t, (CON_AXIS1|CON_AXIS2), "locking %s X"); } } } else { if (t->flag & T_2D_EDIT) { setConstraint(t, mati, (CON_AXIS0), "along X axis"); } else { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setConstraint(t, mati, (CON_AXIS0), "along global X"); else if (t->modifiers & MOD_CONSTRAINT_PLANE) setConstraint(t, mati, (CON_AXIS1|CON_AXIS2), "locking global X"); } } t->redraw = 1; } break; case YKEY: if ((t->flag & T_NO_CONSTRAINT)==0) { if (cmode == 'Y') { if (t->flag & T_2D_EDIT) { stopConstraint(t); } else { if (t->con.mode & CON_USER) { stopConstraint(t); } else { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setUserConstraint(t, (CON_AXIS1), "along %s Y"); else if (t->modifiers & MOD_CONSTRAINT_PLANE) setUserConstraint(t, (CON_AXIS0|CON_AXIS2), "locking %s Y"); } } } else { if (t->flag & T_2D_EDIT) { setConstraint(t, mati, (CON_AXIS1), "along Y axis"); } else { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setConstraint(t, mati, (CON_AXIS1), "along global Y"); else if (t->modifiers & MOD_CONSTRAINT_PLANE) setConstraint(t, mati, (CON_AXIS0|CON_AXIS2), "locking global Y"); } } t->redraw = 1; } break; case ZKEY: if ((t->flag & T_NO_CONSTRAINT)==0) { if (cmode == 'Z') { if (t->con.mode & CON_USER) { stopConstraint(t); } else { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setUserConstraint(t, (CON_AXIS2), "along %s Z"); else if ((t->modifiers & MOD_CONSTRAINT_PLANE) && ((t->flag & T_2D_EDIT)==0)) setUserConstraint(t, (CON_AXIS0|CON_AXIS1), "locking %s Z"); } } else if ((t->flag & T_2D_EDIT)==0) { if ((t->modifiers & MOD_CONSTRAINT_PLANE) == 0) setConstraint(t, mati, (CON_AXIS2), "along global Z"); else if (t->modifiers & MOD_CONSTRAINT_PLANE) setConstraint(t, mati, (CON_AXIS0|CON_AXIS1), "locking global Z"); } t->redraw = 1; } break; case OKEY: if (t->flag & T_PROP_EDIT && event->shift) { t->prop_mode = (t->prop_mode + 1) % 6; calculatePropRatio(t); t->redraw = 1; } break; case PADPLUSKEY: if(event->alt && t->flag & T_PROP_EDIT) { t->prop_size *= 1.1f; calculatePropRatio(t); } t->redraw= 1; break; case PAGEUPKEY: case WHEELDOWNMOUSE: if (t->flag & T_AUTOIK) { transform_autoik_update(t, 1); } else if(t->flag & T_PROP_EDIT) { t->prop_size*= 1.1f; calculatePropRatio(t); } else view_editmove(event->type); t->redraw= 1; break; case PADMINUS: if(event->alt && t->flag & T_PROP_EDIT) { t->prop_size*= 0.90909090f; calculatePropRatio(t); } t->redraw= 1; break; case PAGEDOWNKEY: case WHEELUPMOUSE: if (t->flag & T_AUTOIK) { transform_autoik_update(t, -1); } else if (t->flag & T_PROP_EDIT) { t->prop_size*= 0.90909090f; calculatePropRatio(t); } else view_editmove(event->type); t->redraw= 1; break; // case NDOFMOTION: // viewmoveNDOF(1); // break; } // Numerical input events t->redraw |= handleNumInput(&(t->num), event); // NDof input events switch(handleNDofInput(&(t->ndof), event)) { case NDOF_CONFIRM: if ((t->options & CTX_NDOF) == 0) { /* Confirm on normal transform only */ t->state = TRANS_CONFIRM; } break; case NDOF_CANCEL: if (t->options & CTX_NDOF) { /* Cancel on pure NDOF transform */ t->state = TRANS_CANCEL; } else { /* Otherwise, just redraw, NDof input was cancelled */ t->redraw = 1; } break; case NDOF_NOMOVE: if (t->options & CTX_NDOF) { /* Confirm on pure NDOF transform */ t->state = TRANS_CONFIRM; } break; case NDOF_REFRESH: t->redraw = 1; break; } // Snapping events t->redraw |= handleSnapping(t, event); //arrows_move_cursor(event->type); } else { switch (event->type){ case LEFTMOUSE: t->state = TRANS_CONFIRM; break; case LEFTSHIFTKEY: case RIGHTSHIFTKEY: t->modifiers &= ~MOD_CONSTRAINT_PLANE; t->redraw = 1; break; case LEFTCTRLKEY: case RIGHTCTRLKEY: t->modifiers &= ~MOD_SNAP_GEARS; /* no redraw on release modifier keys! this makes sure you can assign the 'grid' still after releasing modifer key */ //t->redraw = 1; break; case MIDDLEMOUSE: if ((t->flag & T_NO_CONSTRAINT)==0) { t->modifiers &= ~MOD_CONSTRAINT_SELECT; postSelectConstraint(t); t->redraw = 1; } break; // case LEFTMOUSE: // case RIGHTMOUSE: // if(WM_modal_tweak_exit(event, t->event_type)) //// if (t->options & CTX_TWEAK) // t->state = TRANS_CONFIRM; // break; } } // Per transform event, if present if (t->handleEvent) t->redraw |= t->handleEvent(t, event); } int calculateTransformCenter(bContext *C, wmEvent *event, int centerMode, float *vec) { TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data"); int success = 1; t->state = TRANS_RUNNING; t->options = CTX_NONE; t->mode = TFM_DUMMY; initTransInfo(C, t, NULL, event); // internal data, mouse, vectors createTransData(C, t); // make TransData structs from selection t->around = centerMode; // override userdefined mode if (t->total == 0) { success = 0; } else { success = 1; calculateCenter(t); // Copy center from constraint center. Transform center can be local VECCOPY(vec, t->con.center); } postTrans(t); /* aftertrans does insert ipos and action channels, and clears base flags, doesnt read transdata */ special_aftertrans_update(t); MEM_freeN(t); return success; } typedef enum { UP, DOWN, LEFT, RIGHT } ArrowDirection; static void drawArrow(ArrowDirection d, short offset, short length, short size) { switch(d) { case LEFT: offset = -offset; length = -length; size = -size; case RIGHT: glBegin(GL_LINES); glVertex2s( offset, 0); glVertex2s( offset + length, 0); glVertex2s( offset + length, 0); glVertex2s( offset + length - size, -size); glVertex2s( offset + length, 0); glVertex2s( offset + length - size, size); glEnd(); break; case DOWN: offset = -offset; length = -length; size = -size; case UP: glBegin(GL_LINES); glVertex2s( 0, offset); glVertex2s( 0, offset + length); glVertex2s( 0, offset + length); glVertex2s(-size, offset + length - size); glVertex2s( 0, offset + length); glVertex2s( size, offset + length - size); glEnd(); break; } } static void drawArrowHead(ArrowDirection d, short size) { switch(d) { case LEFT: size = -size; case RIGHT: glBegin(GL_LINES); glVertex2s( 0, 0); glVertex2s( -size, -size); glVertex2s( 0, 0); glVertex2s( -size, size); glEnd(); break; case DOWN: size = -size; case UP: glBegin(GL_LINES); glVertex2s( 0, 0); glVertex2s(-size, -size); glVertex2s( 0, 0); glVertex2s( size, -size); glEnd(); break; } } static void drawArc(float size, float angle_start, float angle_end, int segments) { float delta = (angle_end - angle_start) / segments; float angle; glBegin(GL_LINE_STRIP); for( angle = angle_start; angle < angle_end; angle += delta) { glVertex2f( cosf(angle) * size, sinf(angle) * size); } glVertex2f( cosf(angle_end) * size, sinf(angle_end) * size); glEnd(); } void drawHelpline(const struct bContext *C, TransInfo *t) { if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) { float vecrot[3], cent[2]; VECCOPY(vecrot, t->center); if(t->flag & T_EDIT) { Object *ob= t->obedit; if(ob) Mat4MulVecfl(ob->obmat, vecrot); } else if(t->flag & T_POSE) { Object *ob=t->poseobj; if(ob) Mat4MulVecfl(ob->obmat, vecrot); } projectFloatView(t, vecrot, cent); // no overflow in extreme cases glDisable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); ED_region_pixelspace(t->ar); switch(t->helpline) { case HLP_SPRING: UI_ThemeColor(TH_WIRE); setlinestyle(3); glBegin(GL_LINE_STRIP); glVertex2sv(t->mval); glVertex2fv(cent); glEnd(); glTranslatef(t->mval[0], t->mval[1], 0); glRotatef(-180 / M_PI * atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1]), 0, 0, 1); setlinestyle(0); glLineWidth(3.0); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); glLineWidth(1.0); break; case HLP_HARROW: UI_ThemeColor(TH_WIRE); glTranslatef(t->mval[0], t->mval[1], 0); glLineWidth(3.0); drawArrow(RIGHT, 5, 10, 5); drawArrow(LEFT, 5, 10, 5); glLineWidth(1.0); break; case HLP_VARROW: UI_ThemeColor(TH_WIRE); glTranslatef(t->mval[0], t->mval[1], 0); glLineWidth(3.0); glBegin(GL_LINES); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); glLineWidth(1.0); break; case HLP_ANGLE: { float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1]; float angle = atan2f(dy, dx); float dist = sqrtf(dx*dx + dy*dy); float delta_angle = MIN2(15 / dist, M_PI/4); float spacing_angle = MIN2(5 / dist, M_PI/12); UI_ThemeColor(TH_WIRE); setlinestyle(3); glBegin(GL_LINE_STRIP); glVertex2sv(t->mval); glVertex2fv(cent); glEnd(); glTranslatef(cent[0], cent[1], 0); setlinestyle(0); glLineWidth(3.0); drawArc(dist, angle - delta_angle, angle - spacing_angle, 10); drawArc(dist, angle + spacing_angle, angle + delta_angle, 10); glPushMatrix(); glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); glRotatef(180 / M_PI * (angle - delta_angle), 0, 0, 1); drawArrowHead(DOWN, 5); glPopMatrix(); glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); glRotatef(180 / M_PI * (angle + delta_angle), 0, 0, 1); drawArrowHead(UP, 5); glLineWidth(1.0); break; } case HLP_TRACKBALL: { char col[3], col2[3]; UI_GetThemeColor3ubv(TH_GRID, col); glTranslatef(t->mval[0], t->mval[1], 0); glLineWidth(3.0); UI_make_axis_color(col, col2, 'x'); glColor3ubv((GLubyte *)col2); drawArrow(RIGHT, 5, 10, 5); drawArrow(LEFT, 5, 10, 5); UI_make_axis_color(col, col2, 'y'); glColor3ubv((GLubyte *)col2); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); glLineWidth(1.0); break; } } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEnable(GL_DEPTH_TEST); } } void drawTransform(const struct bContext *C, struct ARegion *ar, void *arg) { TransInfo *t = arg; drawConstraint(C, t); drawPropCircle(C, t); drawSnapping(C, t); drawHelpline(C, t); } void saveTransform(bContext *C, TransInfo *t, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); int constraint_axis[3] = {0, 0, 0}; int proportional = 0; if (RNA_struct_find_property(op->ptr, "value")) { if (t->flag & T_AUTOVALUES) { RNA_float_set_array(op->ptr, "value", t->auto_values); } else { RNA_float_set_array(op->ptr, "value", t->values); } } /* XXX convert stupid flag to enum */ switch(t->flag & (T_PROP_EDIT|T_PROP_CONNECTED)) { case (T_PROP_EDIT|T_PROP_CONNECTED): proportional = 2; break; case T_PROP_EDIT: proportional = 1; break; default: proportional = 0; } if (RNA_struct_find_property(op->ptr, "proportional")) { RNA_enum_set(op->ptr, "proportional", proportional); RNA_enum_set(op->ptr, "proportional_editing_falloff", t->prop_mode); RNA_float_set(op->ptr, "proportional_size", t->prop_size); } if (RNA_struct_find_property(op->ptr, "mirror")) { RNA_boolean_set(op->ptr, "mirror", t->flag & T_MIRROR); } if (RNA_struct_find_property(op->ptr, "constraint_axis")) { RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation); if (t->con.mode & CON_APPLY) { if (t->con.mode & CON_AXIS0) { constraint_axis[0] = 1; } if (t->con.mode & CON_AXIS1) { constraint_axis[1] = 1; } if (t->con.mode & CON_AXIS2) { constraint_axis[2] = 1; } } RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis); } // XXX If modal, save settings back in scene if (t->flag & T_MODAL) { ts->prop_mode = t->prop_mode; /* only save back if it wasn't automatically disabled */ if ((t->options & CTX_NO_PET) == 0) { ts->proportional = proportional; } if(t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->view; v3d->twmode = t->current_orientation; } } } int initTransform(bContext *C, TransInfo *t, wmOperator *op, wmEvent *event, int mode) { int options = 0; /* added initialize, for external calls to set stuff in TransInfo, like undo string */ t->state = TRANS_RUNNING; t->options = options; t->mode = mode; if (!initTransInfo(C, t, op, event)) // internal data, mouse, vectors { return 0; } if(t->spacetype == SPACE_VIEW3D) { //calc_manipulator_stats(curarea); initTransformOrientation(C, t); t->draw_handle = ED_region_draw_cb_activate(t->ar->type, drawTransform, t, REGION_DRAW_POST); } else if(t->spacetype == SPACE_IMAGE) { Mat3One(t->spacemtx); t->draw_handle = ED_region_draw_cb_activate(t->ar->type, drawTransform, t, REGION_DRAW_POST); } else Mat3One(t->spacemtx); createTransData(C, t); // make TransData structs from selection if (t->total == 0) { postTrans(t); return 0; } initSnapping(t, op); // Initialize snapping data AFTER mode flags /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */ /* EVIL2: we gave as argument also texture space context bit... was cleared */ /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */ mode = t->mode; calculatePropRatio(t); calculateCenter(t); initMouseInput(t, &t->mouse, t->center2d, t->imval); switch (mode) { case TFM_TRANSLATION: initTranslation(t); break; case TFM_ROTATION: initRotation(t); break; case TFM_RESIZE: initResize(t); break; case TFM_TOSPHERE: initToSphere(t); break; case TFM_SHEAR: initShear(t); break; case TFM_WARP: initWarp(t); break; case TFM_SHRINKFATTEN: initShrinkFatten(t); break; case TFM_TILT: initTilt(t); break; case TFM_CURVE_SHRINKFATTEN: initCurveShrinkFatten(t); break; case TFM_TRACKBALL: initTrackball(t); break; case TFM_PUSHPULL: initPushPull(t); break; case TFM_CREASE: initCrease(t); break; case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */ bArmature *arm= t->poseobj->data; if(arm->drawtype==ARM_ENVELOPE) initBoneEnvelope(t); else initBoneSize(t); } break; case TFM_BONE_ENVELOPE: initBoneEnvelope(t); break; case TFM_EDGE_SLIDE: initEdgeSlide(t); break; case TFM_BONE_ROLL: initBoneRoll(t); break; case TFM_TIME_TRANSLATE: initTimeTranslate(t); break; case TFM_TIME_SLIDE: initTimeSlide(t); break; case TFM_TIME_SCALE: initTimeScale(t); break; case TFM_TIME_EXTEND: /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement) * depending on which editor this was called from */ if ELEM(t->spacetype, SPACE_IPO, SPACE_NLA) initTranslation(t); else initTimeTranslate(t); break; case TFM_BAKE_TIME: initBakeTime(t); break; case TFM_MIRROR: initMirror(t); break; case TFM_BEVEL: initBevel(t); break; case TFM_BWEIGHT: initBevelWeight(t); break; case TFM_ALIGN: initAlign(t); break; } /* overwrite initial values if operator supplied a non-null vector */ if (RNA_property_is_set(op->ptr, "value")) { float values[4]; RNA_float_get_array(op->ptr, "value", values); QUATCOPY(t->values, values); QUATCOPY(t->auto_values, values); t->flag |= T_AUTOVALUES; } /* Constraint init from operator */ if (RNA_struct_find_property(op->ptr, "constraint_axis") && RNA_property_is_set(op->ptr, "constraint_axis")) { int constraint_axis[3]; RNA_boolean_get_array(op->ptr, "constraint_axis", constraint_axis); if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) { t->con.mode |= CON_APPLY; if (constraint_axis[0]) { t->con.mode |= CON_AXIS0; } if (constraint_axis[1]) { t->con.mode |= CON_AXIS1; } if (constraint_axis[2]) { t->con.mode |= CON_AXIS2; } setUserConstraint(t, t->con.mode, "%s"); } } return 1; } void transformApply(bContext *C, TransInfo *t) { if (t->redraw) { if (t->modifiers & MOD_CONSTRAINT_SELECT) t->con.mode |= CON_SELECT; selectConstraint(t); if (t->transform) { t->transform(t, t->mval); // calls recalcData() viewRedrawForce(C, t); } t->redraw = 0; } /* If auto confirm is on, break after one pass */ if (t->options & CTX_AUTOCONFIRM) { t->state = TRANS_CONFIRM; } if (BKE_ptcache_get_continue_physics()) { // TRANSFORM_FIX_ME //do_screenhandlers(G.curscreen); t->redraw = 1; } } int transformEnd(bContext *C, TransInfo *t) { int exit_code = OPERATOR_RUNNING_MODAL; if (t->state != TRANS_RUNNING) { /* handle restoring objects */ if(t->state == TRANS_CANCEL) { exit_code = OPERATOR_CANCELLED; restoreTransObjects(t); // calls recalcData() } else { exit_code = OPERATOR_FINISHED; } /* free data */ postTrans(t); /* aftertrans does insert keyframes, and clears base flags, doesnt read transdata */ special_aftertrans_update(t); /* send events out for redraws */ viewRedrawPost(t); /* Undo as last, certainly after special_trans_update! */ if(t->state == TRANS_CANCEL) { // if(t->undostr) ED_undo_push(C, t->undostr); } else { // if(t->undostr) ED_undo_push(C, t->undostr); // else ED_undo_push(C, transform_to_undostr(t)); } t->undostr= NULL; viewRedrawForce(C, t); } return exit_code; } /* ************************** TRANSFORM LOCKS **************************** */ static void protectedTransBits(short protectflag, float *vec) { if(protectflag & OB_LOCK_LOCX) vec[0]= 0.0f; if(protectflag & OB_LOCK_LOCY) vec[1]= 0.0f; if(protectflag & OB_LOCK_LOCZ) vec[2]= 0.0f; } static void protectedSizeBits(short protectflag, float *size) { if(protectflag & OB_LOCK_SCALEX) size[0]= 1.0f; if(protectflag & OB_LOCK_SCALEY) size[1]= 1.0f; if(protectflag & OB_LOCK_SCALEZ) size[2]= 1.0f; } static void protectedRotateBits(short protectflag, float *eul, float *oldeul) { if(protectflag & OB_LOCK_ROTX) eul[0]= oldeul[0]; if(protectflag & OB_LOCK_ROTY) eul[1]= oldeul[1]; if(protectflag & OB_LOCK_ROTZ) eul[2]= oldeul[2]; } /* this function only does the delta rotation */ /* axis-angle is usually internally stored as quats... */ static void protectedAxisAngleBits(short protectflag, float *quat, float *oldquat) { /* check that protection flags are set */ if ((protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) == 0) return; if (protectflag & OB_LOCK_ROT4D) { /* axis-angle getting limited as 4D entities that they are... */ if (protectflag & OB_LOCK_ROTW) quat[0]= oldquat[0]; if (protectflag & OB_LOCK_ROTX) quat[1]= oldquat[1]; if (protectflag & OB_LOCK_ROTY) quat[2]= oldquat[2]; if (protectflag & OB_LOCK_ROTZ) quat[3]= oldquat[3]; } else { /* axis-angle get limited with euler... */ float eul[3], oldeul[3], quat1[4]; QUATCOPY(quat1, quat); AxisAngleToEulO(quat+1, quat[0], eul, EULER_ORDER_DEFAULT); AxisAngleToEulO(oldquat+1, oldquat[0], oldeul, EULER_ORDER_DEFAULT); if (protectflag & OB_LOCK_ROTX) eul[0]= oldeul[0]; if (protectflag & OB_LOCK_ROTY) eul[1]= oldeul[1]; if (protectflag & OB_LOCK_ROTZ) eul[2]= oldeul[2]; EulOToAxisAngle(eul, EULER_ORDER_DEFAULT, quat+1, quat); /* when converting to axis-angle, we need a special exception for the case when there is no axis */ if (IS_EQ(quat[1], quat[2]) && IS_EQ(quat[2], quat[3])) { /* for now, rotate around y-axis then (so that it simply becomes the roll) */ quat[2]= 1.0f; } } } /* this function only does the delta rotation */ static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat) { /* check that protection flags are set */ if ((protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) == 0) return; if (protectflag & OB_LOCK_ROT4D) { /* quaternions getting limited as 4D entities that they are... */ if (protectflag & OB_LOCK_ROTW) quat[0]= oldquat[0]; if (protectflag & OB_LOCK_ROTX) quat[1]= oldquat[1]; if (protectflag & OB_LOCK_ROTY) quat[2]= oldquat[2]; if (protectflag & OB_LOCK_ROTZ) quat[3]= oldquat[3]; } else { /* quaternions get limited with euler... (compatability mode) */ float eul[3], oldeul[3], quat1[4]; QUATCOPY(quat1, quat); QuatToEul(quat, eul); QuatToEul(oldquat, oldeul); if (protectflag & OB_LOCK_ROTX) eul[0]= oldeul[0]; if (protectflag & OB_LOCK_ROTY) eul[1]= oldeul[1]; if (protectflag & OB_LOCK_ROTZ) eul[2]= oldeul[2]; EulToQuat(eul, quat); /* quaternions flip w sign to accumulate rotations correctly */ if ( (quat1[0]<0.0f && quat[0]>0.0f) || (quat1[0]>0.0f && quat[0]<0.0f) ) { QuatMulf(quat, -1.0f); } } } /* ******************* TRANSFORM LIMITS ********************** */ static void constraintTransLim(TransInfo *t, TransData *td) { if (td->con) { bConstraintTypeInfo *cti= get_constraint_typeinfo(CONSTRAINT_TYPE_LOCLIMIT); bConstraintOb cob; bConstraint *con; /* Make a temporary bConstraintOb for using these limit constraints * - they only care that cob->matrix is correctly set ;-) * - current space should be local */ memset(&cob, 0, sizeof(bConstraintOb)); Mat4One(cob.matrix); VECCOPY(cob.matrix[3], td->loc); /* Evaluate valid constraints */ for (con= td->con; con; con= con->next) { float tmat[4][4]; /* only consider constraint if enabled */ if (con->flag & CONSTRAINT_DISABLE) continue; if (con->enforce == 0.0f) continue; /* only use it if it's tagged for this purpose (and the right type) */ if (con->type == CONSTRAINT_TYPE_LOCLIMIT) { bLocLimitConstraint *data= con->data; if ((data->flag2 & LIMIT_TRANSFORM)==0) continue; /* do space conversions */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->mtx, tmat); } else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) { /* skip... incompatable spacetype */ continue; } /* do constraint */ cti->evaluate_constraint(con, &cob, NULL); /* convert spaces again */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->smtx, tmat); } } } /* copy results from cob->matrix */ VECCOPY(td->loc, cob.matrix[3]); } } static void constraintRotLim(TransInfo *t, TransData *td) { if (td->con) { bConstraintTypeInfo *cti= get_constraint_typeinfo(CONSTRAINT_TYPE_ROTLIMIT); bConstraintOb cob; bConstraint *con; /* Make a temporary bConstraintOb for using these limit constraints * - they only care that cob->matrix is correctly set ;-) * - current space should be local */ memset(&cob, 0, sizeof(bConstraintOb)); if (td->rotOrder == ROT_MODE_QUAT) { /* quats */ if (td->ext) QuatToMat4(td->ext->quat, cob.matrix); else return; } else if (td->rotOrder == ROT_MODE_AXISANGLE) { /* axis angle */ if (td->ext) AxisAngleToMat4(&td->ext->quat[1], td->ext->quat[0], cob.matrix); else return; } else { /* eulers */ if (td->ext) EulOToMat4(td->ext->rot, td->rotOrder, cob.matrix); else return; } /* Evaluate valid constraints */ for (con= td->con; con; con= con->next) { /* only consider constraint if enabled */ if (con->flag & CONSTRAINT_DISABLE) continue; if (con->enforce == 0.0f) continue; /* we're only interested in Limit-Rotation constraints */ if (con->type == CONSTRAINT_TYPE_ROTLIMIT) { bRotLimitConstraint *data= con->data; float tmat[4][4]; /* only use it if it's tagged for this purpose */ if ((data->flag2 & LIMIT_TRANSFORM)==0) continue; /* do space conversions */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->mtx, tmat); } else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) { /* skip... incompatable spacetype */ continue; } /* do constraint */ cti->evaluate_constraint(con, &cob, NULL); /* convert spaces again */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->smtx, tmat); } } } /* copy results from cob->matrix */ if (td->rotOrder == ROT_MODE_QUAT) { /* quats */ Mat4ToQuat(cob.matrix, td->ext->quat); } else if (td->rotOrder == ROT_MODE_AXISANGLE) { /* axis angle */ Mat4ToAxisAngle(cob.matrix, &td->ext->quat[1], &td->ext->quat[0]); } else { /* eulers */ Mat4ToEulO(cob.matrix, td->ext->rot, td->rotOrder); } } } static void constraintSizeLim(TransInfo *t, TransData *td) { if (td->con && td->ext) { bConstraintTypeInfo *cti= get_constraint_typeinfo(CONSTRAINT_TYPE_SIZELIMIT); bConstraintOb cob; bConstraint *con; /* Make a temporary bConstraintOb for using these limit constraints * - they only care that cob->matrix is correctly set ;-) * - current space should be local */ memset(&cob, 0, sizeof(bConstraintOb)); if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) { /* scale val and reset size */ return; // TODO: fix this case } else { /* Reset val if SINGLESIZE but using a constraint */ if (td->flag & TD_SINGLESIZE) return; SizeToMat4(td->ext->size, cob.matrix); } /* Evaluate valid constraints */ for (con= td->con; con; con= con->next) { /* only consider constraint if enabled */ if (con->flag & CONSTRAINT_DISABLE) continue; if (con->enforce == 0.0f) continue; /* we're only interested in Limit-Scale constraints */ if (con->type == CONSTRAINT_TYPE_SIZELIMIT) { bSizeLimitConstraint *data= con->data; float tmat[4][4]; /* only use it if it's tagged for this purpose */ if ((data->flag2 & LIMIT_TRANSFORM)==0) continue; /* do space conversions */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->mtx, tmat); } else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) { /* skip... incompatable spacetype */ continue; } /* do constraint */ cti->evaluate_constraint(con, &cob, NULL); /* convert spaces again */ if (con->ownspace == CONSTRAINT_SPACE_WORLD) { /* just multiply by td->mtx (this should be ok) */ Mat4CpyMat4(tmat, cob.matrix); Mat4MulMat34(cob.matrix, td->smtx, tmat); } } } /* copy results from cob->matrix */ if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) { /* scale val and reset size */ return; // TODO: fix this case } else { /* Reset val if SINGLESIZE but using a constraint */ if (td->flag & TD_SINGLESIZE) return; Mat4ToSize(cob.matrix, td->ext->size); } } } /* ************************** WARP *************************** */ void initWarp(TransInfo *t) { float max[3], min[3]; int i; t->mode = TFM_WARP; t->transform = Warp; t->handleEvent = handleEventWarp; initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 5.0f; t->snap[2] = 1.0f; t->flag |= T_NO_CONSTRAINT; /* we need min/max in view space */ for(i = 0; i < t->total; i++) { float center[3]; VECCOPY(center, t->data[i].center); Mat3MulVecfl(t->data[i].mtx, center); Mat4MulVecfl(t->viewmat, center); VecSubf(center, center, t->viewmat[3]); if (i) MinMax3(min, max, center); else { VECCOPY(max, center); VECCOPY(min, center); } } t->center[0]= (min[0]+max[0])/2.0f; t->center[1]= (min[1]+max[1])/2.0f; t->center[2]= (min[2]+max[2])/2.0f; if (max[0] == min[0]) max[0] += 0.1; /* not optimal, but flipping is better than invalid garbage (i.e. division by zero!) */ t->val= (max[0]-min[0])/2.0f; /* t->val is X dimension projected boundbox */ } int handleEventWarp(TransInfo *t, wmEvent *event) { int status = 0; if (event->type == MIDDLEMOUSE && event->val==KM_PRESS) { // Use customData pointer to signal warp direction if (t->customData == 0) t->customData = (void*)1; else t->customData = 0; status = 1; } return status; } int Warp(TransInfo *t, short mval[2]) { TransData *td = t->data; float vec[3], circumfac, dist, phi0, co, si, *curs, cursor[3], gcursor[3]; int i; char str[50]; curs= give_cursor(t->scene, t->view); /* * gcursor is the one used for helpline. * It has to be in the same space as the drawing loop * (that means it needs to be in the object's space when in edit mode and * in global space in object mode) * * cursor is used for calculations. * It needs to be in view space, but we need to take object's offset * into account if in Edit mode. */ VECCOPY(cursor, curs); VECCOPY(gcursor, cursor); if (t->flag & T_EDIT) { VecSubf(cursor, cursor, t->obedit->obmat[3]); VecSubf(gcursor, gcursor, t->obedit->obmat[3]); Mat3MulVecfl(t->data->smtx, gcursor); } Mat4MulVecfl(t->viewmat, cursor); VecSubf(cursor, cursor, t->viewmat[3]); /* amount of degrees for warp */ circumfac = 360.0f * t->values[0]; if (t->customData) /* non-null value indicates reversed input */ { circumfac *= -1; } snapGrid(t, &circumfac); applyNumInput(&t->num, &circumfac); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Warp: %s", c); } else { /* default header print */ sprintf(str, "Warp: %.3f", circumfac); } circumfac*= (float)(-M_PI/360.0); for(i = 0; i < t->total; i++, td++) { float loc[3]; if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; /* translate point to center, rotate in such a way that outline==distance */ VECCOPY(vec, td->iloc); Mat3MulVecfl(td->mtx, vec); Mat4MulVecfl(t->viewmat, vec); VecSubf(vec, vec, t->viewmat[3]); dist= vec[0]-cursor[0]; /* t->val is X dimension projected boundbox */ phi0= (circumfac*dist/t->val); vec[1]= (vec[1]-cursor[1]); co= (float)cos(phi0); si= (float)sin(phi0); loc[0]= -si*vec[1]+cursor[0]; loc[1]= co*vec[1]+cursor[1]; loc[2]= vec[2]; Mat4MulVecfl(t->viewinv, loc); VecSubf(loc, loc, t->viewinv[3]); Mat3MulVecfl(td->smtx, loc); VecSubf(loc, loc, td->iloc); VecMulf(loc, td->factor); VecAddf(td->loc, td->iloc, loc); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** SHEAR *************************** */ void initShear(TransInfo *t) { t->mode = TFM_SHEAR; t->transform = Shear; t->handleEvent = handleEventShear; initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } int handleEventShear(TransInfo *t, wmEvent *event) { int status = 0; if (event->type == MIDDLEMOUSE && event->val==KM_PRESS) { // Use customData pointer to signal Shear direction if (t->customData == 0) { initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE); t->customData = (void*)1; } else { initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE); t->customData = 0; } status = 1; } return status; } int Shear(TransInfo *t, short mval[2]) { TransData *td = t->data; float vec[3]; float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3]; float value; int i; char str[50]; Mat3CpyMat4(persmat, t->viewmat); Mat3Inv(persinv, persmat); value = 0.05f * t->values[0]; snapGrid(t, &value); applyNumInput(&t->num, &value); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Shear: %s %s", c, t->proptext); } else { /* default header print */ sprintf(str, "Shear: %.3f %s", value, t->proptext); } Mat3One(smat); // Custom data signals shear direction if (t->customData == 0) smat[1][0] = value; else smat[0][1] = value; Mat3MulMat3(tmat, smat, persmat); Mat3MulMat3(totmat, persinv, tmat); for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (t->obedit) { float mat3[3][3]; Mat3MulMat3(mat3, totmat, td->mtx); Mat3MulMat3(tmat, td->smtx, mat3); } else { Mat3CpyMat3(tmat, totmat); } VecSubf(vec, td->center, t->center); Mat3MulVecfl(tmat, vec); VecAddf(vec, vec, t->center); VecSubf(vec, vec, td->center); VecMulf(vec, td->factor); VecAddf(td->loc, td->iloc, vec); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** RESIZE *************************** */ void initResize(TransInfo *t) { t->mode = TFM_RESIZE; t->transform = Resize; initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); t->flag |= T_NULL_ONE; t->num.flag |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if (!t->obedit) { t->flag |= T_NO_ZERO; t->num.flag |= NUM_NO_ZERO; } t->idx_max = 2; t->num.idx_max = 2; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; } static void headerResize(TransInfo *t, float vec[3], char *str) { char tvec[60]; if (hasNumInput(&t->num)) { outputNumInput(&(t->num), tvec); } else { sprintf(&tvec[0], "%.4f", vec[0]); sprintf(&tvec[20], "%.4f", vec[1]); sprintf(&tvec[40], "%.4f", vec[2]); } if (t->con.mode & CON_APPLY) { switch(t->num.idx_max) { case 0: sprintf(str, "Scale: %s%s %s", &tvec[0], t->con.text, t->proptext); break; case 1: sprintf(str, "Scale: %s : %s%s %s", &tvec[0], &tvec[20], t->con.text, t->proptext); break; case 2: sprintf(str, "Scale: %s : %s : %s%s %s", &tvec[0], &tvec[20], &tvec[40], t->con.text, t->proptext); } } else { if (t->flag & T_2D_EDIT) sprintf(str, "Scale X: %s Y: %s%s %s", &tvec[0], &tvec[20], t->con.text, t->proptext); else sprintf(str, "Scale X: %s Y: %s Z: %s%s %s", &tvec[0], &tvec[20], &tvec[40], t->con.text, t->proptext); } } #define SIGN(a) (a<-FLT_EPSILON?1:a>FLT_EPSILON?2:3) #define VECSIGNFLIP(a, b) ((SIGN(a[0]) & SIGN(b[0]))==0 || (SIGN(a[1]) & SIGN(b[1]))==0 || (SIGN(a[2]) & SIGN(b[2]))==0) /* smat is reference matrix, only scaled */ static void TransMat3ToSize( float mat[][3], float smat[][3], float *size) { float vec[3]; VecCopyf(vec, mat[0]); size[0]= Normalize(vec); VecCopyf(vec, mat[1]); size[1]= Normalize(vec); VecCopyf(vec, mat[2]); size[2]= Normalize(vec); /* first tried with dotproduct... but the sign flip is crucial */ if( VECSIGNFLIP(mat[0], smat[0]) ) size[0]= -size[0]; if( VECSIGNFLIP(mat[1], smat[1]) ) size[1]= -size[1]; if( VECSIGNFLIP(mat[2], smat[2]) ) size[2]= -size[2]; } static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], center[3]; float vec[3]; if (t->flag & T_EDIT) { Mat3MulMat3(smat, mat, td->mtx); Mat3MulMat3(tmat, td->smtx, smat); } else { Mat3CpyMat3(tmat, mat); } if (t->con.applySize) { t->con.applySize(t, td, tmat); } /* local constraint shouldn't alter center */ if (t->around == V3D_LOCAL) { if (t->flag & T_OBJECT) { VECCOPY(center, td->center); } else if (t->flag & T_EDIT) { if(t->around==V3D_LOCAL && (t->settings->selectmode & SCE_SELECT_FACE)) { VECCOPY(center, td->center); } else { VECCOPY(center, t->center); } } else { VECCOPY(center, t->center); } } else { VECCOPY(center, t->center); } if (td->ext) { float fsize[3]; if (t->flag & (T_OBJECT|T_TEXTURE|T_POSE)) { float obsizemat[3][3]; // Reorient the size mat to fit the oriented object. Mat3MulMat3(obsizemat, tmat, td->axismtx); //printmatrix3("obsizemat", obsizemat); TransMat3ToSize(obsizemat, td->axismtx, fsize); //printvecf("fsize", fsize); } else { Mat3ToSize(tmat, fsize); } protectedSizeBits(td->protectflag, fsize); if ((t->flag & T_V3D_ALIGN)==0) { // align mode doesn't resize objects itself if((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)){ /* scale val and reset size */ *td->val = td->ival * fsize[0] * td->factor; td->ext->size[0] = td->ext->isize[0]; td->ext->size[1] = td->ext->isize[1]; td->ext->size[2] = td->ext->isize[2]; } else { /* Reset val if SINGLESIZE but using a constraint */ if (td->flag & TD_SINGLESIZE) *td->val = td->ival; td->ext->size[0] = td->ext->isize[0] * (fsize[0]) * td->factor; td->ext->size[1] = td->ext->isize[1] * (fsize[1]) * td->factor; td->ext->size[2] = td->ext->isize[2] * (fsize[2]) * td->factor; } } constraintSizeLim(t, td); } /* For individual element center, Editmode need to use iloc */ if (t->flag & T_POINTS) VecSubf(vec, td->iloc, center); else VecSubf(vec, td->center, center); Mat3MulVecfl(tmat, vec); VecAddf(vec, vec, center); if (t->flag & T_POINTS) VecSubf(vec, vec, td->iloc); else VecSubf(vec, vec, td->center); VecMulf(vec, td->factor); if (t->flag & (T_OBJECT|T_POSE)) { Mat3MulVecfl(td->smtx, vec); } protectedTransBits(td->protectflag, vec); VecAddf(td->loc, td->iloc, vec); constraintTransLim(t, td); } int Resize(TransInfo *t, short mval[2]) { TransData *td; float size[3], mat[3][3]; float ratio; int i; char str[200]; /* for manipulator, center handle, the scaling can't be done relative to center */ if( (t->flag & T_USES_MANIPULATOR) && t->con.mode==0) { ratio = 1.0f - ((t->imval[0] - mval[0]) + (t->imval[1] - mval[1]))/100.0f; } else { ratio = t->values[0]; } size[0] = size[1] = size[2] = ratio; snapGrid(t, size); if (hasNumInput(&t->num)) { applyNumInput(&t->num, size); constraintNumInput(t, size); } applySnapping(t, size); if (t->flag & T_AUTOVALUES) { VECCOPY(size, t->auto_values); } VECCOPY(t->values, size); SizeToMat3(size, mat); if (t->con.applySize) { t->con.applySize(t, NULL, mat); } Mat3CpyMat3(t->mat, mat); // used in manipulator headerResize(t, size, str); for(i = 0, td=t->data; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; ElementResize(t, td, mat); } /* evil hack - redo resize if cliping needed */ if (t->flag & T_CLIP_UV && clipUVTransform(t, size, 1)) { SizeToMat3(size, mat); if (t->con.applySize) t->con.applySize(t, NULL, mat); for(i = 0, td=t->data; i < t->total; i++, td++) ElementResize(t, td, mat); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** TOSPHERE *************************** */ void initToSphere(TransInfo *t) { TransData *td = t->data; int i; t->mode = TFM_TOSPHERE; t->transform = ToSphere; initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->num.flag |= NUM_NULL_ONE | NUM_NO_NEGATIVE; t->flag |= T_NO_CONSTRAINT; // Calculate average radius for(i = 0 ; i < t->total; i++, td++) { t->val += VecLenf(t->center, td->iloc); } t->val /= (float)t->total; } int ToSphere(TransInfo *t, short mval[2]) { float vec[3]; float ratio, radius; int i; char str[64]; TransData *td = t->data; ratio = t->values[0]; snapGrid(t, &ratio); applyNumInput(&t->num, &ratio); if (ratio < 0) ratio = 0.0f; else if (ratio > 1) ratio = 1.0f; /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "To Sphere: %s %s", c, t->proptext); } else { /* default header print */ sprintf(str, "To Sphere: %.4f %s", ratio, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { float tratio; if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; VecSubf(vec, td->iloc, t->center); radius = Normalize(vec); tratio = ratio * td->factor; VecMulf(vec, radius * (1.0f - tratio) + t->val * tratio); VecAddf(td->loc, t->center, vec); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** ROTATION *************************** */ void initRotation(TransInfo *t) { t->mode = TFM_ROTATION; t->transform = Rotation; initMouseInputMode(t, &t->mouse, INPUT_ANGLE); t->ndof.axis = 16; /* Scale down and flip input for rotation */ t->ndof.factor[0] = -0.2f; t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = (float)((5.0/180)*M_PI); t->snap[2] = t->snap[1] * 0.2f; if (t->flag & T_2D_EDIT) t->flag |= T_NO_CONSTRAINT; } static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around) { float vec[3], totmat[3][3], smat[3][3]; float eul[3], fmat[3][3], quat[4]; float *center = t->center; /* local constraint shouldn't alter center */ if (around == V3D_LOCAL) { if (t->flag & (T_OBJECT|T_POSE)) { center = td->center; } else { /* !TODO! Make this if not rely on G */ if(around==V3D_LOCAL && (t->settings->selectmode & SCE_SELECT_FACE)) { center = td->center; } } } if (t->flag & T_POINTS) { Mat3MulMat3(totmat, mat, td->mtx); Mat3MulMat3(smat, td->smtx, totmat); VecSubf(vec, td->iloc, center); Mat3MulVecfl(smat, vec); VecAddf(td->loc, vec, center); VecSubf(vec,td->loc,td->iloc); protectedTransBits(td->protectflag, vec); VecAddf(td->loc, td->iloc, vec); if(td->flag & TD_USEQUAT) { Mat3MulSerie(fmat, td->mtx, mat, td->smtx, 0, 0, 0, 0, 0); Mat3ToQuat(fmat, quat); // Actual transform if(td->ext->quat){ QuatMul(td->ext->quat, quat, td->ext->iquat); /* is there a reason not to have this here? -jahka */ protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); } } } /** * HACK WARNING * * This is some VERY ugly special case to deal with pose mode. * * The problem is that mtx and smtx include each bone orientation. * * That is needed to rotate each bone properly, HOWEVER, to calculate * the translation component, we only need the actual armature object's * matrix (and inverse). That is not all though. Once the proper translation * has been computed, it has to be converted back into the bone's space. */ else if (t->flag & T_POSE) { float pmtx[3][3], imtx[3][3]; // Extract and invert armature object matrix Mat3CpyMat4(pmtx, t->poseobj->obmat); Mat3Inv(imtx, pmtx); if ((td->flag & TD_NO_LOC) == 0) { VecSubf(vec, td->center, center); Mat3MulVecfl(pmtx, vec); // To Global space Mat3MulVecfl(mat, vec); // Applying rotation Mat3MulVecfl(imtx, vec); // To Local space VecAddf(vec, vec, center); /* vec now is the location where the object has to be */ VecSubf(vec, vec, td->center); // Translation needed from the initial location Mat3MulVecfl(pmtx, vec); // To Global space Mat3MulVecfl(td->smtx, vec);// To Pose space protectedTransBits(td->protectflag, vec); VecAddf(td->loc, td->iloc, vec); constraintTransLim(t, td); } /* rotation */ if ((t->flag & T_V3D_ALIGN)==0) { // align mode doesn't rotate objects itself /* euler or quaternion/axis-angle? */ if (td->rotOrder == ROT_MODE_QUAT) { Mat3MulSerie(fmat, td->mtx, mat, td->smtx, 0, 0, 0, 0, 0); Mat3ToQuat(fmat, quat); // Actual transform QuatMul(td->ext->quat, quat, td->ext->iquat); /* this function works on end result */ protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); } else if (td->rotOrder == ROT_MODE_AXISANGLE) { /* calculate effect based on quats */ float iquat[4]; /* td->ext->(i)quat is in axis-angle form, not quats! */ AxisAngleToQuat(iquat, &td->ext->iquat[1], td->ext->iquat[0]); Mat3MulSerie(fmat, td->mtx, mat, td->smtx, 0, 0, 0, 0, 0); Mat3ToQuat(fmat, quat); // Actual transform QuatMul(td->ext->quat, quat, iquat); /* make temp copy (since stored in same place) */ QUATCOPY(quat, td->ext->quat); // this is just a 4d vector copying macro QuatToAxisAngle(quat, &td->ext->quat[1], &td->ext->quat[0]); /* this function works on end result */ protectedAxisAngleBits(td->protectflag, td->ext->quat, td->ext->iquat); } else { float eulmat[3][3]; Mat3MulMat3(totmat, mat, td->mtx); Mat3MulMat3(smat, td->smtx, totmat); /* calculate the total rotatation in eulers */ VECCOPY(eul, td->ext->irot); EulOToMat3(eul, td->rotOrder, eulmat); /* mat = transform, obmat = bone rotation */ Mat3MulMat3(fmat, smat, eulmat); Mat3ToCompatibleEulO(fmat, eul, td->ext->rot, td->rotOrder); /* and apply (to end result only) */ protectedRotateBits(td->protectflag, eul, td->ext->irot); VECCOPY(td->ext->rot, eul); } constraintRotLim(t, td); } } else { if ((td->flag & TD_NO_LOC) == 0) { /* translation */ VecSubf(vec, td->center, center); Mat3MulVecfl(mat, vec); VecAddf(vec, vec, center); /* vec now is the location where the object has to be */ VecSubf(vec, vec, td->center); Mat3MulVecfl(td->smtx, vec); protectedTransBits(td->protectflag, vec); VecAddf(td->loc, td->iloc, vec); } constraintTransLim(t, td); /* rotation */ if ((t->flag & T_V3D_ALIGN)==0) { // align mode doesn't rotate objects itself /* euler or quaternion? */ if ((td->rotOrder == ROT_MODE_QUAT) || (td->flag & TD_USEQUAT)) { Mat3MulSerie(fmat, td->mtx, mat, td->smtx, 0, 0, 0, 0, 0); Mat3ToQuat(fmat, quat); // Actual transform QuatMul(td->ext->quat, quat, td->ext->iquat); /* this function works on end result */ protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); } else if (td->rotOrder == ROT_MODE_AXISANGLE) { /* calculate effect based on quats */ float iquat[4]; /* td->ext->(i)quat is in axis-angle form, not quats! */ AxisAngleToQuat(iquat, &td->ext->iquat[1], td->ext->iquat[0]); Mat3MulSerie(fmat, td->mtx, mat, td->smtx, 0, 0, 0, 0, 0); Mat3ToQuat(fmat, quat); // Actual transform QuatMul(td->ext->quat, quat, iquat); /* make temp copy (since stored in same place) */ QUATCOPY(quat, td->ext->quat); // this is just a 4d vector copying macro QuatToAxisAngle(quat, &td->ext->quat[1], &td->ext->quat[0]); /* this function works on end result */ protectedAxisAngleBits(td->protectflag, td->ext->quat, td->ext->iquat); } else { float obmat[3][3]; Mat3MulMat3(totmat, mat, td->mtx); Mat3MulMat3(smat, td->smtx, totmat); /* calculate the total rotatation in eulers */ VecAddf(eul, td->ext->irot, td->ext->drot); /* we have to correct for delta rot */ EulOToMat3(eul, td->rotOrder, obmat); /* mat = transform, obmat = object rotation */ Mat3MulMat3(fmat, smat, obmat); Mat3ToCompatibleEulO(fmat, eul, td->ext->rot, td->rotOrder); /* correct back for delta rot */ VecSubf(eul, eul, td->ext->drot); /* and apply */ protectedRotateBits(td->protectflag, eul, td->ext->irot); VECCOPY(td->ext->rot, eul); } constraintRotLim(t, td); } } } static void applyRotation(TransInfo *t, float angle, float axis[3]) { TransData *td = t->data; float mat[3][3]; int i; VecRotToMat3(axis, angle, mat); for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (t->con.applyRot) { t->con.applyRot(t, td, axis, NULL); VecRotToMat3(axis, angle * td->factor, mat); } else if (t->flag & T_PROP_EDIT) { VecRotToMat3(axis, angle * td->factor, mat); } ElementRotation(t, td, mat, t->around); } } int Rotation(TransInfo *t, short mval[2]) { char str[64]; float final; float axis[3]; float mat[3][3]; VECCOPY(axis, t->viewinv[2]); VecMulf(axis, -1.0f); Normalize(axis); final = t->values[0]; applyNDofInput(&t->ndof, &final); snapGrid(t, &final); if (t->con.applyRot) { t->con.applyRot(t, NULL, axis, &final); } applySnapping(t, &final); if (hasNumInput(&t->num)) { char c[20]; applyNumInput(&t->num, &final); outputNumInput(&(t->num), c); sprintf(str, "Rot: %s %s %s", &c[0], t->con.text, t->proptext); /* Clamp between -180 and 180 */ while (final >= 180.0) final -= 360.0; while (final <= -180.0) final += 360.0; final *= (float)(M_PI / 180.0); } else { sprintf(str, "Rot: %.2f%s %s", 180.0*final/M_PI, t->con.text, t->proptext); } VecRotToMat3(axis, final, mat); // TRANSFORM_FIX_ME // t->values[0] = final; // used in manipulator // Mat3CpyMat3(t->mat, mat); // used in manipulator applyRotation(t, final, axis); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** TRACKBALL *************************** */ void initTrackball(TransInfo *t) { t->mode = TFM_TRACKBALL; t->transform = Trackball; initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL); t->ndof.axis = 40; /* Scale down input for rotation */ t->ndof.factor[0] = 0.2f; t->ndof.factor[1] = 0.2f; t->idx_max = 1; t->num.idx_max = 1; t->snap[0] = 0.0f; t->snap[1] = (float)((5.0/180)*M_PI); t->snap[2] = t->snap[1] * 0.2f; t->flag |= T_NO_CONSTRAINT; } static void applyTrackball(TransInfo *t, float axis1[3], float axis2[3], float angles[2]) { TransData *td = t->data; float mat[3][3], smat[3][3], totmat[3][3]; int i; VecRotToMat3(axis1, angles[0], smat); VecRotToMat3(axis2, angles[1], totmat); Mat3MulMat3(mat, smat, totmat); for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (t->flag & T_PROP_EDIT) { VecRotToMat3(axis1, td->factor * angles[0], smat); VecRotToMat3(axis2, td->factor * angles[1], totmat); Mat3MulMat3(mat, smat, totmat); } ElementRotation(t, td, mat, t->around); } } int Trackball(TransInfo *t, short mval[2]) { char str[128]; float axis1[3], axis2[3]; float mat[3][3], totmat[3][3], smat[3][3]; float phi[2]; VECCOPY(axis1, t->persinv[0]); VECCOPY(axis2, t->persinv[1]); Normalize(axis1); Normalize(axis2); phi[0] = t->values[0]; phi[1] = t->values[1]; applyNDofInput(&t->ndof, phi); snapGrid(t, phi); if (hasNumInput(&t->num)) { char c[40]; applyNumInput(&t->num, phi); outputNumInput(&(t->num), c); sprintf(str, "Trackball: %s %s %s", &c[0], &c[20], t->proptext); phi[0] *= (float)(M_PI / 180.0); phi[1] *= (float)(M_PI / 180.0); } else { sprintf(str, "Trackball: %.2f %.2f %s", 180.0*phi[0]/M_PI, 180.0*phi[1]/M_PI, t->proptext); } VecRotToMat3(axis1, phi[0], smat); VecRotToMat3(axis2, phi[1], totmat); Mat3MulMat3(mat, smat, totmat); // TRANSFORM_FIX_ME //Mat3CpyMat3(t->mat, mat); // used in manipulator applyTrackball(t, axis1, axis2, phi); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** TRANSLATION *************************** */ void initTranslation(TransInfo *t) { t->mode = TFM_TRANSLATION; t->transform = Translation; initMouseInputMode(t, &t->mouse, INPUT_VECTOR); t->idx_max = (t->flag & T_2D_EDIT)? 1: 2; t->num.flag = 0; t->num.idx_max = t->idx_max; t->ndof.axis = (t->flag & T_2D_EDIT)? 1|2: 1|2|4; if(t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->view; t->snap[0] = 0.0f; t->snap[1] = v3d->gridview * 1.0f; t->snap[2] = t->snap[1] * 0.1f; } else if(t->spacetype == SPACE_IMAGE) { t->snap[0] = 0.0f; t->snap[1] = 0.125f; t->snap[2] = 0.0625f; } else { t->snap[0] = 0.0f; t->snap[1] = t->snap[2] = 1.0f; } } static void headerTranslation(TransInfo *t, float vec[3], char *str) { char tvec[60]; char distvec[20]; char autoik[20]; float dist; if (hasNumInput(&t->num)) { outputNumInput(&(t->num), tvec); dist = VecLength(t->num.val); } else { float dvec[3]; VECCOPY(dvec, vec); applyAspectRatio(t, dvec); dist = VecLength(vec); if(t->scene->unit.system) { int i, do_split= t->scene->unit.flag & USER_UNIT_OPT_SPLIT ? 1:0; for(i=0; i<3; i++) bUnit_AsString(&tvec[i*20], 20, dvec[i]*t->scene->unit.scale_length, 4, t->scene->unit.system, B_UNIT_LENGTH, do_split, 1); } else { sprintf(&tvec[0], "%.4f", dvec[0]); sprintf(&tvec[20], "%.4f", dvec[1]); sprintf(&tvec[40], "%.4f", dvec[2]); } } if(t->scene->unit.system) bUnit_AsString(distvec, sizeof(distvec), dist*t->scene->unit.scale_length, 4, t->scene->unit.system, B_UNIT_LENGTH, t->scene->unit.flag & USER_UNIT_OPT_SPLIT, 0); else if( dist > 1e10 || dist < -1e10 ) /* prevent string buffer overflow */ sprintf(distvec, "%.4e", dist); else sprintf(distvec, "%.4f", dist); if(t->flag & T_AUTOIK) { short chainlen= t->settings->autoik_chainlen; if(chainlen) sprintf(autoik, "AutoIK-Len: %d", chainlen); else strcpy(autoik, ""); } else strcpy(autoik, ""); if (t->con.mode & CON_APPLY) { switch(t->num.idx_max) { case 0: sprintf(str, "D: %s (%s)%s %s %s", &tvec[0], distvec, t->con.text, t->proptext, &autoik[0]); break; case 1: sprintf(str, "D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[20], distvec, t->con.text, t->proptext, &autoik[0]); break; case 2: sprintf(str, "D: %s D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[20], &tvec[40], distvec, t->con.text, t->proptext, &autoik[0]); } } else { if(t->flag & T_2D_EDIT) sprintf(str, "Dx: %s Dy: %s (%s)%s %s", &tvec[0], &tvec[20], distvec, t->con.text, t->proptext); else sprintf(str, "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", &tvec[0], &tvec[20], &tvec[40], distvec, t->con.text, t->proptext, &autoik[0]); } } static void applyTranslation(TransInfo *t, float vec[3]) { TransData *td = t->data; float tvec[3]; int i; for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; /* handle snapping rotation before doing the translation */ if (usingSnappingNormal(t)) { if (validSnappingNormal(t)) { float *original_normal = td->axismtx[2]; float axis[3]; float quat[4]; float mat[3][3]; float angle; Crossf(axis, original_normal, t->tsnap.snapNormal); angle = saacos(Inpf(original_normal, t->tsnap.snapNormal)); AxisAngleToQuat(quat, axis, angle); QuatToMat3(quat, mat); ElementRotation(t, td, mat, V3D_LOCAL); } else { float mat[3][3]; Mat3One(mat); ElementRotation(t, td, mat, V3D_LOCAL); } } if (t->con.applyVec) { float pvec[3]; t->con.applyVec(t, td, vec, tvec, pvec); } else { VECCOPY(tvec, vec); } Mat3MulVecfl(td->smtx, tvec); VecMulf(tvec, td->factor); protectedTransBits(td->protectflag, tvec); VecAddf(td->loc, td->iloc, tvec); constraintTransLim(t, td); } } /* uses t->vec to store actual translation in */ int Translation(TransInfo *t, short mval[2]) { float tvec[3]; char str[250]; if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; applySnapping(t, t->values); t->con.applyVec(t, NULL, t->values, tvec, pvec); VECCOPY(t->values, tvec); headerTranslation(t, pvec, str); } else { applyNDofInput(&t->ndof, t->values); snapGrid(t, t->values); applyNumInput(&t->num, t->values); if (hasNumInput(&t->num)) { removeAspectRatio(t, t->values); } applySnapping(t, t->values); headerTranslation(t, t->values, str); } applyTranslation(t, t->values); /* evil hack - redo translation if clipping needed */ if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values, 0)) applyTranslation(t, t->values); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** SHRINK/FATTEN *************************** */ void initShrinkFatten(TransInfo *t) { // If not in mesh edit mode, fallback to Resize if (t->obedit==NULL || t->obedit->type != OB_MESH) { initResize(t); } else { t->mode = TFM_SHRINKFATTEN; t->transform = ShrinkFatten; initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 1.0f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } } int ShrinkFatten(TransInfo *t, short mval[2]) { float vec[3]; float distance; int i; char str[64]; TransData *td = t->data; distance = -t->values[0]; snapGrid(t, &distance); applyNumInput(&t->num, &distance); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Shrink/Fatten: %s %s", c, t->proptext); } else { /* default header print */ sprintf(str, "Shrink/Fatten: %.4f %s", distance, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; VECCOPY(vec, td->axismtx[2]); VecMulf(vec, distance); VecMulf(vec, td->factor); VecAddf(td->loc, td->iloc, vec); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** TILT *************************** */ void initTilt(TransInfo *t) { t->mode = TFM_TILT; t->transform = Tilt; initMouseInputMode(t, &t->mouse, INPUT_ANGLE); t->ndof.axis = 16; /* Scale down and flip input for rotation */ t->ndof.factor[0] = -0.2f; t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = (float)((5.0/180)*M_PI); t->snap[2] = t->snap[1] * 0.2f; t->flag |= T_NO_CONSTRAINT; } int Tilt(TransInfo *t, short mval[2]) { TransData *td = t->data; int i; char str[50]; float final; final = t->values[0]; applyNDofInput(&t->ndof, &final); snapGrid(t, &final); if (hasNumInput(&t->num)) { char c[20]; applyNumInput(&t->num, &final); outputNumInput(&(t->num), c); sprintf(str, "Tilt: %s %s", &c[0], t->proptext); final *= (float)(M_PI / 180.0); } else { sprintf(str, "Tilt: %.2f %s", 180.0*final/M_PI, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (td->val) { *td->val = td->ival + final * td->factor; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ******************** Curve Shrink/Fatten *************** */ void initCurveShrinkFatten(TransInfo *t) { t->mode = TFM_CURVE_SHRINKFATTEN; t->transform = CurveShrinkFatten; initMouseInputMode(t, &t->mouse, INPUT_SPRING); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } int CurveShrinkFatten(TransInfo *t, short mval[2]) { TransData *td = t->data; float ratio; int i; char str[50]; ratio = t->values[0]; snapGrid(t, &ratio); applyNumInput(&t->num, &ratio); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Shrink/Fatten: %s", c); } else { sprintf(str, "Shrink/Fatten: %3f", ratio); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if(td->val) { //*td->val= ratio; *td->val= td->ival*ratio; if (*td->val <= 0.0f) *td->val = 0.0001f; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** PUSH/PULL *************************** */ void initPushPull(TransInfo *t) { t->mode = TFM_PUSHPULL; t->transform = PushPull; initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE); t->ndof.axis = 4; /* Flip direction */ t->ndof.factor[0] = -1.0f; t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 1.0f; t->snap[2] = t->snap[1] * 0.1f; } int PushPull(TransInfo *t, short mval[2]) { float vec[3], axis[3]; float distance; int i; char str[128]; TransData *td = t->data; distance = t->values[0]; applyNDofInput(&t->ndof, &distance); snapGrid(t, &distance); applyNumInput(&t->num, &distance); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Push/Pull: %s%s %s", c, t->con.text, t->proptext); } else { /* default header print */ sprintf(str, "Push/Pull: %.4f%s %s", distance, t->con.text, t->proptext); } if (t->con.applyRot && t->con.mode & CON_APPLY) { t->con.applyRot(t, NULL, axis, NULL); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; VecSubf(vec, t->center, td->center); if (t->con.applyRot && t->con.mode & CON_APPLY) { t->con.applyRot(t, td, axis, NULL); if (isLockConstraint(t)) { float dvec[3]; Projf(dvec, vec, axis); VecSubf(vec, vec, dvec); } else { Projf(vec, vec, axis); } } Normalize(vec); VecMulf(vec, distance); VecMulf(vec, td->factor); VecAddf(td->loc, td->iloc, vec); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** BEVEL **************************** */ void initBevel(TransInfo *t) { t->transform = Bevel; t->handleEvent = handleEventBevel; initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE); t->mode = TFM_BEVEL; t->flag |= T_NO_CONSTRAINT; t->num.flag |= NUM_NO_NEGATIVE; t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; /* DON'T KNOW WHY THIS IS NEEDED */ if (G.editBMesh->imval[0] == 0 && G.editBMesh->imval[1] == 0) { /* save the initial mouse co */ G.editBMesh->imval[0] = t->imval[0]; G.editBMesh->imval[1] = t->imval[1]; } else { /* restore the mouse co from a previous call to initTransform() */ t->imval[0] = G.editBMesh->imval[0]; t->imval[1] = G.editBMesh->imval[1]; } } int handleEventBevel(TransInfo *t, wmEvent *event) { if (event->val==KM_PRESS) { if(!G.editBMesh) return 0; switch (event->type) { case MIDDLEMOUSE: G.editBMesh->options ^= BME_BEVEL_VERT; t->state = TRANS_CANCEL; return 1; //case PADPLUSKEY: // G.editBMesh->options ^= BME_BEVEL_RES; // G.editBMesh->res += 1; // if (G.editBMesh->res > 4) { // G.editBMesh->res = 4; // } // t->state = TRANS_CANCEL; // return 1; //case PADMINUS: // G.editBMesh->options ^= BME_BEVEL_RES; // G.editBMesh->res -= 1; // if (G.editBMesh->res < 0) { // G.editBMesh->res = 0; // } // t->state = TRANS_CANCEL; // return 1; default: return 0; } } return 0; } int Bevel(TransInfo *t, short mval[2]) { float distance,d; int i; char str[128]; char *mode; TransData *td = t->data; mode = (G.editBMesh->options & BME_BEVEL_VERT) ? "verts only" : "normal"; distance = t->values[0] / 4; /* 4 just seemed a nice value to me, nothing special */ distance = fabs(distance); snapGrid(t, &distance); applyNumInput(&t->num, &distance); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Bevel - Dist: %s, Mode: %s (MMB to toggle))", c, mode); } else { /* default header print */ sprintf(str, "Bevel - Dist: %.4f, Mode: %s (MMB to toggle))", distance, mode); } if (distance < 0) distance = -distance; for(i = 0 ; i < t->total; i++, td++) { if (td->axismtx[1][0] > 0 && distance > td->axismtx[1][0]) { d = td->axismtx[1][0]; } else { d = distance; } VECADDFAC(td->loc,td->center,td->axismtx[0],(*td->val)*d); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** BEVEL WEIGHT *************************** */ void initBevelWeight(TransInfo *t) { t->mode = TFM_BWEIGHT; t->transform = BevelWeight; initMouseInputMode(t, &t->mouse, INPUT_SPRING); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } int BevelWeight(TransInfo *t, short mval[2]) { TransData *td = t->data; float weight; int i; char str[50]; weight = t->values[0]; weight -= 1.0f; if (weight > 1.0f) weight = 1.0f; snapGrid(t, &weight); applyNumInput(&t->num, &weight); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); if (weight >= 0.0f) sprintf(str, "Bevel Weight: +%s %s", c, t->proptext); else sprintf(str, "Bevel Weight: %s %s", c, t->proptext); } else { /* default header print */ if (weight >= 0.0f) sprintf(str, "Bevel Weight: +%.3f %s", weight, t->proptext); else sprintf(str, "Bevel Weight: %.3f %s", weight, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->val) { *td->val = td->ival + weight * td->factor; if (*td->val < 0.0f) *td->val = 0.0f; if (*td->val > 1.0f) *td->val = 1.0f; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** CREASE *************************** */ void initCrease(TransInfo *t) { t->mode = TFM_CREASE; t->transform = Crease; initMouseInputMode(t, &t->mouse, INPUT_SPRING); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } int Crease(TransInfo *t, short mval[2]) { TransData *td = t->data; float crease; int i; char str[50]; crease = t->values[0]; crease -= 1.0f; if (crease > 1.0f) crease = 1.0f; snapGrid(t, &crease); applyNumInput(&t->num, &crease); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); if (crease >= 0.0f) sprintf(str, "Crease: +%s %s", c, t->proptext); else sprintf(str, "Crease: %s %s", c, t->proptext); } else { /* default header print */ if (crease >= 0.0f) sprintf(str, "Crease: +%.3f %s", crease, t->proptext); else sprintf(str, "Crease: %.3f %s", crease, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (td->val) { *td->val = td->ival + crease * td->factor; if (*td->val < 0.0f) *td->val = 0.0f; if (*td->val > 1.0f) *td->val = 1.0f; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ******************** EditBone (B-bone) width scaling *************** */ void initBoneSize(TransInfo *t) { t->mode = TFM_BONESIZE; t->transform = BoneSize; initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); t->idx_max = 2; t->num.idx_max = 2; t->num.flag |= NUM_NULL_ONE; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; } static void headerBoneSize(TransInfo *t, float vec[3], char *str) { char tvec[60]; if (hasNumInput(&t->num)) { outputNumInput(&(t->num), tvec); } else { sprintf(&tvec[0], "%.4f", vec[0]); sprintf(&tvec[20], "%.4f", vec[1]); sprintf(&tvec[40], "%.4f", vec[2]); } /* hmm... perhaps the y-axis values don't need to be shown? */ if (t->con.mode & CON_APPLY) { if (t->num.idx_max == 0) sprintf(str, "ScaleB: %s%s %s", &tvec[0], t->con.text, t->proptext); else sprintf(str, "ScaleB: %s : %s : %s%s %s", &tvec[0], &tvec[20], &tvec[40], t->con.text, t->proptext); } else { sprintf(str, "ScaleB X: %s Y: %s Z: %s%s %s", &tvec[0], &tvec[20], &tvec[40], t->con.text, t->proptext); } } static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], oldy; float sizemat[3][3]; Mat3MulMat3(smat, mat, td->mtx); Mat3MulMat3(tmat, td->smtx, smat); if (t->con.applySize) { t->con.applySize(t, td, tmat); } /* we've tucked the scale in loc */ oldy= td->iloc[1]; SizeToMat3(td->iloc, sizemat); Mat3MulMat3(tmat, tmat, sizemat); Mat3ToSize(tmat, td->loc); td->loc[1]= oldy; } int BoneSize(TransInfo *t, short mval[2]) { TransData *td = t->data; float size[3], mat[3][3]; float ratio; int i; char str[60]; // TRANSFORM_FIX_ME MOVE TO MOUSE INPUT /* for manipulator, center handle, the scaling can't be done relative to center */ if( (t->flag & T_USES_MANIPULATOR) && t->con.mode==0) { ratio = 1.0f - ((t->imval[0] - mval[0]) + (t->imval[1] - mval[1]))/100.0f; } else { ratio = t->values[0]; } size[0] = size[1] = size[2] = ratio; snapGrid(t, size); if (hasNumInput(&t->num)) { applyNumInput(&t->num, size); constraintNumInput(t, size); } SizeToMat3(size, mat); if (t->con.applySize) { t->con.applySize(t, NULL, mat); } Mat3CpyMat3(t->mat, mat); // used in manipulator headerBoneSize(t, size, str); for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; ElementBoneSize(t, td, mat); } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ******************** EditBone envelope *************** */ void initBoneEnvelope(TransInfo *t) { t->mode = TFM_BONE_ENVELOPE; t->transform = BoneEnvelope; initMouseInputMode(t, &t->mouse, INPUT_SPRING); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 0.1f; t->snap[2] = t->snap[1] * 0.1f; t->flag |= T_NO_CONSTRAINT; } int BoneEnvelope(TransInfo *t, short mval[2]) { TransData *td = t->data; float ratio; int i; char str[50]; ratio = t->values[0]; snapGrid(t, &ratio); applyNumInput(&t->num, &ratio); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); sprintf(str, "Envelope: %s", c); } else { sprintf(str, "Envelope: %3f", ratio); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (td->val) { /* if the old/original value was 0.0f, then just use ratio */ if (td->ival) *td->val= td->ival*ratio; else *td->val= ratio; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ******************** Edge Slide *************** */ static int createSlideVerts(TransInfo *t) { Mesh *me = t->obedit->data; EditMesh *em = me->edit_mesh; EditFace *efa; EditEdge *eed,*first=NULL,*last=NULL, *temp = NULL; EditVert *ev, *nearest = NULL; LinkNode *edgelist = NULL, *vertlist=NULL, *look; GHash *vertgh; TransDataSlideVert *tempsv; float perc = 0, percp = 0,vertdist; // XXX, projectMat[4][4]; float shiftlabda= 0.0f,len = 0.0f; int i, j, numsel, numadded=0, timesthrough = 0, vertsel=0, prop=1, cancel = 0,flip=0; int wasshift = 0; /* UV correction vars */ GHash **uvarray= NULL; SlideData *sld = MEM_callocN(sizeof(*sld), "sld"); int uvlay_tot= CustomData_number_of_layers(&em->fdata, CD_MTFACE); int uvlay_idx; TransDataSlideUv *slideuvs=NULL, *suv=NULL, *suv_last=NULL; RegionView3D *v3d = t->ar->regiondata; float projectMat[4][4]; float start[3] = {0.0f, 0.0f, 0.0f}, end[3] = {0.0f, 0.0f, 0.0f}; float vec[3]; //short mval[2], mvalo[2]; float labda = 0.0f, totvec=0.0; if (!v3d) { /*ok, let's try to survive this*/ Mat4One(projectMat); } else { view3d_get_object_project_mat(v3d, t->obedit, projectMat); } //mvalo[0] = -1; mvalo[1] = -1; numsel =0; // Get number of selected edges and clear some flags for(eed=em->edges.first;eed;eed=eed->next) { eed->f1 = 0; eed->f2 = 0; if(eed->f & SELECT) numsel++; } for(ev=em->verts.first;ev;ev=ev->next) { ev->f1 = 0; } //Make sure each edge only has 2 faces // make sure loop doesn't cross face for(efa=em->faces.first;efa;efa=efa->next) { int ct = 0; if(efa->e1->f & SELECT) { ct++; efa->e1->f1++; if(efa->e1->f1 > 2) { //BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e2->f & SELECT) { ct++; efa->e2->f1++; if(efa->e2->f1 > 2) { //BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e3->f & SELECT) { ct++; efa->e3->f1++; if(efa->e3->f1 > 2) { //BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e4 && efa->e4->f & SELECT) { ct++; efa->e4->f1++; if(efa->e4->f1 > 2) { //BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } // Make sure loop is not 2 edges of same face if(ct > 1) { //BKE_report(op->reports, RPT_ERROR, "Loop crosses itself"); return 0; } } // Get # of selected verts for(ev=em->verts.first;ev;ev=ev->next) { if(ev->f & SELECT) vertsel++; } // Test for multiple segments if(vertsel > numsel+1) { //BKE_report(op->reports, RPT_ERROR, "Please choose a single edge loop"); return 0; } // Get the edgeloop in order - mark f1 with SELECT once added for(eed=em->edges.first;eed;eed=eed->next) { if((eed->f & SELECT) && !(eed->f1 & SELECT)) { // If this is the first edge added, just put it in if(!edgelist) { BLI_linklist_prepend(&edgelist,eed); numadded++; first = eed; last = eed; eed->f1 = SELECT; } else { if(editedge_getSharedVert(eed, last)) { BLI_linklist_append(&edgelist,eed); eed->f1 = SELECT; numadded++; last = eed; } else if(editedge_getSharedVert(eed, first)) { BLI_linklist_prepend(&edgelist,eed); eed->f1 = SELECT; numadded++; first = eed; } } } if(eed->next == NULL && numadded != numsel) { eed=em->edges.first; timesthrough++; } // It looks like there was an unexpected case - Hopefully should not happen if(timesthrough >= numsel*2) { BLI_linklist_free(edgelist,NULL); //BKE_report(op->reports, RPT_ERROR, "Could not order loop"); return 0; } } // Put the verts in order in a linklist look = edgelist; while(look) { eed = look->link; if(!vertlist) { if(look->next) { temp = look->next->link; //This is the first entry takes care of extra vert if(eed->v1 != temp->v1 && eed->v1 != temp->v2) { BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } else { BLI_linklist_append(&vertlist,eed->v2); eed->v2->f1 = 1; } } else { //This is the case that we only have 1 edge BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } } // for all the entries if(eed->v1->f1 != 1) { BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } else if(eed->v2->f1 != 1) { BLI_linklist_append(&vertlist,eed->v2); eed->v2->f1 = 1; } look = look->next; } // populate the SlideVerts vertgh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); look = vertlist; while(look) { i=0; j=0; ev = look->link; tempsv = (struct TransDataSlideVert*)MEM_mallocN(sizeof(struct TransDataSlideVert),"SlideVert"); tempsv->up = NULL; tempsv->down = NULL; tempsv->origvert.co[0] = ev->co[0]; tempsv->origvert.co[1] = ev->co[1]; tempsv->origvert.co[2] = ev->co[2]; tempsv->origvert.no[0] = ev->no[0]; tempsv->origvert.no[1] = ev->no[1]; tempsv->origvert.no[2] = ev->no[2]; // i is total edges that vert is on // j is total selected edges that vert is on for(eed=em->edges.first;eed;eed=eed->next) { if(eed->v1 == ev || eed->v2 == ev) { i++; if(eed->f & SELECT) { j++; } } } // If the vert is in the middle of an edge loop, it touches 2 selected edges and 2 unselected edges if(i == 4 && j == 2) { for(eed=em->edges.first;eed;eed=eed->next) { if(editedge_containsVert(eed, ev)) { if(!(eed->f & SELECT)) { if(!tempsv->up) { tempsv->up = eed; } else if (!(tempsv->down)) { tempsv->down = eed; } } } } } // If it is on the end of the loop, it touches 1 selected and as least 2 more unselected if(i >= 3 && j == 1) { for(eed=em->edges.first;eed;eed=eed->next) { if(editedge_containsVert(eed, ev) && eed->f & SELECT) { for(efa = em->faces.first;efa;efa=efa->next) { if(editface_containsEdge(efa, eed)) { if(editedge_containsVert(efa->e1, ev) && efa->e1 != eed) { if(!tempsv->up) { tempsv->up = efa->e1; } else if (!(tempsv->down)) { tempsv->down = efa->e1; } } if(editedge_containsVert(efa->e2, ev) && efa->e2 != eed) { if(!tempsv->up) { tempsv->up = efa->e2; } else if (!(tempsv->down)) { tempsv->down = efa->e2; } } if(editedge_containsVert(efa->e3, ev) && efa->e3 != eed) { if(!tempsv->up) { tempsv->up = efa->e3; } else if (!(tempsv->down)) { tempsv->down = efa->e3; } } if(efa->e4) { if(editedge_containsVert(efa->e4, ev) && efa->e4 != eed) { if(!tempsv->up) { tempsv->up = efa->e4; } else if (!(tempsv->down)) { tempsv->down = efa->e4; } } } } } } } } if(i > 4 && j == 2) { BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(vertlist,NULL); BLI_linklist_free(edgelist,NULL); return 0; } BLI_ghash_insert(vertgh,ev,tempsv); look = look->next; } // make sure the UPs nad DOWNs are 'faceloops' // Also find the nearest slidevert to the cursor look = vertlist; nearest = NULL; vertdist = -1; while(look) { tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); if(!tempsv->up || !tempsv->down) { //BKE_report(op->reports, RPT_ERROR, "Missing rails"); BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(vertlist,NULL); BLI_linklist_free(edgelist,NULL); return 0; } if(me->drawflag & ME_DRAW_EDGELEN) { if(!(tempsv->up->f & SELECT)) { tempsv->up->f |= SELECT; tempsv->up->f2 |= 16; } else { tempsv->up->f2 |= ~16; } if(!(tempsv->down->f & SELECT)) { tempsv->down->f |= SELECT; tempsv->down->f2 |= 16; } else { tempsv->down->f2 |= ~16; } } if(look->next != NULL) { TransDataSlideVert *sv; ev = (EditVert*)look->next->link; sv = BLI_ghash_lookup(vertgh, ev); if(sv) { float co[3], co2[3], vec[3]; ev = (EditVert*)look->link; if(!sharesFace(em, tempsv->up,sv->up)) { EditEdge *swap; swap = sv->up; sv->up = sv->down; sv->down = swap; } if (v3d) { view3d_project_float(t->ar, tempsv->up->v1->co, co, projectMat); view3d_project_float(t->ar, tempsv->up->v2->co, co2, projectMat); } if (ev == tempsv->up->v1) { VecSubf(vec, co, co2); } else { VecSubf(vec, co2, co); } VecAddf(start, start, vec); if (v3d) { view3d_project_float(t->ar, tempsv->down->v1->co, co, projectMat); view3d_project_float(t->ar, tempsv->down->v2->co, co2, projectMat); } if (ev == tempsv->down->v1) { VecSubf(vec, co2, co); } else { VecSubf(vec, co, co2); } VecAddf(end, end, vec); totvec += 1.0f; nearest = (EditVert*)look->link; } } look = look->next; } VecAddf(start, start, end); VecMulf(start, 0.5*(1.0/totvec)); VECCOPY(vec, start); start[0] = t->mval[0]; start[1] = t->mval[1]; VecAddf(end, start, vec); sld->start[0] = (short) start[0]; sld->start[1] = (short) start[1]; sld->end[0] = (short) end[0]; sld->end[1] = (short) end[1]; if (uvlay_tot) { // XXX && (scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { int maxnum = 0; uvarray = MEM_callocN( uvlay_tot * sizeof(GHash *), "SlideUVs Array"); sld->totuv = uvlay_tot; suv_last = slideuvs = MEM_callocN( uvlay_tot * (numadded+1) * sizeof(TransDataSlideUv), "SlideUVs"); /* uvLayers * verts */ suv = NULL; for (uvlay_idx=0; uvlay_idxverts.first;ev;ev=ev->next) { ev->tmp.l = 0; } look = vertlist; while(look) { float *uv_new; tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); ev = look->link; suv = NULL; for(efa = em->faces.first;efa;efa=efa->next) { if (ev->tmp.l != -1) { /* test for self, in this case its invalid */ int k=-1; /* face corner */ /* Is this vert in the faces corner? */ if (efa->v1==ev) k=0; else if (efa->v2==ev) k=1; else if (efa->v3==ev) k=2; else if (efa->v4 && efa->v4==ev) k=3; if (k != -1) { MTFace *tf = CustomData_em_get_n(&em->fdata, efa->data, CD_MTFACE, uvlay_idx); EditVert *ev_up, *ev_down; uv_new = tf->uv[k]; if (ev->tmp.l) { if (fabs(suv->origuv[0]-uv_new[0]) > 0.0001 || fabs(suv->origuv[1]-uv_new[1])) { ev->tmp.l = -1; /* Tag as invalid */ BLI_linklist_free(suv->fuv_list,NULL); suv->fuv_list = NULL; BLI_ghash_remove(uvarray[uvlay_idx],ev, NULL, NULL); suv = NULL; break; } } else { ev->tmp.l = 1; suv = suv_last; suv->fuv_list = NULL; suv->uv_up = suv->uv_down = NULL; suv->origuv[0] = uv_new[0]; suv->origuv[1] = uv_new[1]; BLI_linklist_prepend(&suv->fuv_list, uv_new); BLI_ghash_insert(uvarray[uvlay_idx],ev,suv); suv_last++; /* advance to next slide UV */ maxnum++; } /* Now get the uvs along the up or down edge if we can */ if (suv) { if (!suv->uv_up) { ev_up = editedge_getOtherVert(tempsv->up,ev); if (efa->v1==ev_up) suv->uv_up = tf->uv[0]; else if (efa->v2==ev_up) suv->uv_up = tf->uv[1]; else if (efa->v3==ev_up) suv->uv_up = tf->uv[2]; else if (efa->v4 && efa->v4==ev_up) suv->uv_up = tf->uv[3]; } if (!suv->uv_down) { /* if the first face was apart of the up edge, it cant be apart of the down edge */ ev_down = editedge_getOtherVert(tempsv->down,ev); if (efa->v1==ev_down) suv->uv_down = tf->uv[0]; else if (efa->v2==ev_down) suv->uv_down = tf->uv[1]; else if (efa->v3==ev_down) suv->uv_down = tf->uv[2]; else if (efa->v4 && efa->v4==ev_down) suv->uv_down = tf->uv[3]; } /* Copy the pointers to the face UV's */ BLI_linklist_prepend(&suv->fuv_list, uv_new); } } } } look = look->next; } } /* end uv layer loop */ } /* end uvlay_tot */ sld->uvhash = uvarray; sld->slideuv = slideuvs; sld->vhash = vertgh; sld->nearest = nearest; sld->vertlist = vertlist; sld->edgelist = edgelist; sld->suv_last = suv_last; sld->uvlay_tot = uvlay_tot; // we should have enough info now to slide t->customData = sld; return 1; } void freeSlideVerts(TransInfo *t) { TransDataSlideUv *suv; SlideData *sld = t->customData; int uvlay_idx; //BLI_ghash_free(edgesgh, freeGHash, NULL); BLI_ghash_free(sld->vhash, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(sld->vertlist, NULL); BLI_linklist_free(sld->edgelist, NULL); if (sld->uvlay_tot) { for (uvlay_idx=0; uvlay_idxuvlay_tot; uvlay_idx++) { BLI_ghash_free(sld->uvhash[uvlay_idx], NULL, NULL); } suv = sld->suv_last-1; while (suv >= sld->slideuv) { if (suv->fuv_list) { BLI_linklist_free(suv->fuv_list,NULL); } suv--; } MEM_freeN(sld->slideuv); MEM_freeN(sld->uvhash); } MEM_freeN(sld); t->customData = NULL; } void initEdgeSlide(TransInfo *t) { SlideData *sld; t->mode = TFM_EDGE_SLIDE; t->transform = EdgeSlide; createSlideVerts(t); sld = t->customData; if (!sld) return; t->customFree = freeSlideVerts; initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO); setCustomPoints(t, &t->mouse, sld->end, sld->start); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = (float)((5.0/180)*M_PI); t->snap[2] = t->snap[1] * 0.2f; t->flag |= T_NO_CONSTRAINT; } int doEdgeSlide(TransInfo *t, float perc) { Mesh *me= t->obedit->data; EditMesh *em = me->edit_mesh; SlideData *sld = t->customData; EditEdge *first=NULL,*last=NULL, *temp = NULL; EditVert *ev, *nearest = sld->nearest; EditVert *centerVert, *upVert, *downVert; LinkNode *edgelist = sld->edgelist, *vertlist=sld->vertlist, *look; GHash *vertgh = sld->vhash; TransDataSlideVert *tempsv; float shiftlabda= 0.0f,len = 0.0f; int i = 0, numadded=0, timesthrough = 0, vertsel=0, prop=1, cancel = 0,flip=0; int wasshift = 0; /* UV correction vars */ GHash **uvarray= sld->uvhash; int uvlay_tot= CustomData_number_of_layers(&em->fdata, CD_MTFACE); int uvlay_idx; TransDataSlideUv *slideuvs=sld->slideuv, *suv=sld->slideuv, *suv_last=NULL; float uv_tmp[2]; LinkNode *fuv_link; float labda = 0.0f; len = 0.0f; tempsv = BLI_ghash_lookup(vertgh,nearest); centerVert = editedge_getSharedVert(tempsv->up, tempsv->down); upVert = editedge_getOtherVert(tempsv->up, centerVert); downVert = editedge_getOtherVert(tempsv->down, centerVert); len = MIN2(perc, VecLenf(upVert->co,downVert->co)); len = MAX2(len, 0); //Adjust Edgeloop if(prop) { look = vertlist; while(look) { EditVert *tempev; ev = look->link; tempsv = BLI_ghash_lookup(vertgh,ev); tempev = editedge_getOtherVert((perc>=0)?tempsv->up:tempsv->down, ev); VecLerpf(ev->co, tempsv->origvert.co, tempev->co, fabs(perc)); if (uvlay_tot) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { Vec2Lerpf(uv_tmp, suv->origuv, (perc>=0)?suv->uv_up:suv->uv_down, fabs(perc)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } look = look->next; } } else { //Non prop code look = vertlist; while(look) { float newlen; ev = look->link; tempsv = BLI_ghash_lookup(vertgh,ev); newlen = (len / VecLenf(editedge_getOtherVert(tempsv->up,ev)->co,editedge_getOtherVert(tempsv->down,ev)->co)); if(newlen > 1.0) {newlen = 1.0;} if(newlen < 0.0) {newlen = 0.0;} if(flip == 0) { VecLerpf(ev->co, editedge_getOtherVert(tempsv->down,ev)->co, editedge_getOtherVert(tempsv->up,ev)->co, fabs(newlen)); if (uvlay_tot) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { /* dont do anything if no UVs */ for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { Vec2Lerpf(uv_tmp, suv->uv_down, suv->uv_up, fabs(newlen)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } } else{ VecLerpf(ev->co, editedge_getOtherVert(tempsv->up,ev)->co, editedge_getOtherVert(tempsv->down,ev)->co, fabs(newlen)); if (uvlay_tot) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { /* dont do anything if no UVs */ for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { Vec2Lerpf(uv_tmp, suv->uv_up, suv->uv_down, fabs(newlen)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } } look = look->next; } } return 1; } int EdgeSlide(TransInfo *t, short mval[2]) { TransData *td = t->data; char str[50]; float final; final = t->values[0]; snapGrid(t, &final); if (hasNumInput(&t->num)) { char c[20]; applyNumInput(&t->num, &final); outputNumInput(&(t->num), c); sprintf(str, "Edge Slide Percent: %s", &c[0]); } else { sprintf(str, "Edge Slide Percent: %.2f", final); } CLAMP(final, -1.0f, 1.0f); /*do stuff here*/ if (t->customData) doEdgeSlide(t, final); else { strcpy(str, "Invalid Edge Selection"); t->state = TRANS_CANCEL; } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ******************** EditBone roll *************** */ void initBoneRoll(TransInfo *t) { t->mode = TFM_BONE_ROLL; t->transform = BoneRoll; initMouseInputMode(t, &t->mouse, INPUT_ANGLE); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = (float)((5.0/180)*M_PI); t->snap[2] = t->snap[1] * 0.2f; t->flag |= T_NO_CONSTRAINT; } int BoneRoll(TransInfo *t, short mval[2]) { TransData *td = t->data; int i; char str[50]; float final; final = t->values[0]; snapGrid(t, &final); if (hasNumInput(&t->num)) { char c[20]; applyNumInput(&t->num, &final); outputNumInput(&(t->num), c); sprintf(str, "Roll: %s", &c[0]); final *= (float)(M_PI / 180.0); } else { sprintf(str, "Roll: %.2f", 180.0*final/M_PI); } /* set roll values */ for (i = 0; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; *(td->val) = td->ival - final; } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** BAKE TIME ******************* */ void initBakeTime(TransInfo *t) { t->transform = BakeTime; initMouseInputMode(t, &t->mouse, INPUT_NONE); t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; t->snap[1] = 1.0f; t->snap[2] = t->snap[1] * 0.1f; } int BakeTime(TransInfo *t, short mval[2]) { TransData *td = t->data; float time; int i; char str[50]; float fac = 0.1f; if(t->mouse.precision) { /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ time= (float)(t->center2d[0] - t->mouse.precision_mval[0]) * fac; time+= 0.1f*((float)(t->center2d[0]*fac - mval[0]) -time); } else { time = (float)(t->center2d[0] - mval[0])*fac; } snapGrid(t, &time); applyNumInput(&t->num, &time); /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[20]; outputNumInput(&(t->num), c); if (time >= 0.0f) sprintf(str, "Time: +%s %s", c, t->proptext); else sprintf(str, "Time: %s %s", c, t->proptext); } else { /* default header print */ if (time >= 0.0f) sprintf(str, "Time: +%.3f %s", time, t->proptext); else sprintf(str, "Time: %.3f %s", time, t->proptext); } for(i = 0 ; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; if (td->val) { *td->val = td->ival + time * td->factor; if (td->ext->size && *td->val < *td->ext->size) *td->val = *td->ext->size; if (td->ext->quat && *td->val > *td->ext->quat) *td->val = *td->ext->quat; } } recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************** MIRROR *************************** */ void initMirror(TransInfo *t) { t->transform = Mirror; initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; if (!t->obedit) { t->flag |= T_NO_ZERO; } } int Mirror(TransInfo *t, short mval[2]) { TransData *td; float size[3], mat[3][3]; int i; char str[200]; /* * OPTIMISATION: * This still recalcs transformation on mouse move * while it should only recalc on constraint change * */ /* if an axis has been selected */ if (t->con.mode & CON_APPLY) { size[0] = size[1] = size[2] = -1; SizeToMat3(size, mat); if (t->con.applySize) { t->con.applySize(t, NULL, mat); } sprintf(str, "Mirror%s", t->con.text); for(i = 0, td=t->data; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; ElementResize(t, td, mat); } recalcData(t); ED_area_headerprint(t->sa, str); } else { size[0] = size[1] = size[2] = 1; SizeToMat3(size, mat); for(i = 0, td=t->data; i < t->total; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; ElementResize(t, td, mat); } recalcData(t); if(t->flag & T_2D_EDIT) ED_area_headerprint(t->sa, "Select a mirror axis (X, Y)"); else ED_area_headerprint(t->sa, "Select a mirror axis (X, Y, Z)"); } return 1; } /* ************************** ALIGN *************************** */ void initAlign(TransInfo *t) { t->flag |= T_NO_CONSTRAINT; t->transform = Align; initMouseInputMode(t, &t->mouse, INPUT_NONE); } int Align(TransInfo *t, short mval[2]) { TransData *td = t->data; float center[3]; int i; /* saving original center */ VECCOPY(center, t->center); for(i = 0 ; i < t->total; i++, td++) { float mat[3][3], invmat[3][3]; if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; /* around local centers */ if (t->flag & (T_OBJECT|T_POSE)) { VECCOPY(t->center, td->center); } else { if(t->settings->selectmode & SCE_SELECT_FACE) { VECCOPY(t->center, td->center); } } Mat3Inv(invmat, td->axismtx); Mat3MulMat3(mat, t->spacemtx, invmat); ElementRotation(t, td, mat, t->around); } /* restoring original center */ VECCOPY(t->center, center); recalcData(t); ED_area_headerprint(t->sa, "Align"); return 1; } /* ************************** ANIM EDITORS - TRANSFORM TOOLS *************************** */ /* ---------------- Special Helpers for Various Settings ------------- */ /* This function returns the snapping 'mode' for Animation Editors only * We cannot use the standard snapping due to NLA-strip scaling complexities. */ // XXX these modifier checks should be keymappable static short getAnimEdit_SnapMode(TransInfo *t) { short autosnap= SACTSNAP_OFF; /* currently, some of these are only for the action editor */ if (t->spacetype == SPACE_ACTION) { SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; if (saction) autosnap= saction->autosnap; } else if (t->spacetype == SPACE_IPO) { SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first; if (sipo) autosnap= sipo->autosnap; } else if (t->spacetype == SPACE_NLA) { SpaceNla *snla= (SpaceNla *)t->sa->spacedata.first; if (snla) autosnap= snla->autosnap; } else { // TRANSFORM_FIX_ME This needs to use proper defines for t->modifiers // // FIXME: this still toggles the modes... // if (ctrl) // autosnap= SACTSNAP_STEP; // else if (shift) // autosnap= SACTSNAP_FRAME; // else if (alt) // autosnap= SACTSNAP_MARKER; // else autosnap= SACTSNAP_OFF; } return autosnap; } /* This function is used for testing if an Animation Editor is displaying * its data in frames or seconds (and the data needing to be edited as such). * Returns 1 if in seconds, 0 if in frames */ static short getAnimEdit_DrawTime(TransInfo *t) { short drawtime; /* currently, some of these are only for the action editor */ if (t->spacetype == SPACE_ACTION) { SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; drawtime = (saction->flag & SACTION_DRAWTIME)? 1 : 0; } else if (t->spacetype == SPACE_NLA) { SpaceNla *snla= (SpaceNla *)t->sa->spacedata.first; drawtime = (snla->flag & SNLA_DRAWTIME)? 1 : 0; } else { drawtime = 0; } return drawtime; } /* This function is used by Animation Editor specific transform functions to do * the Snap Keyframe to Nearest Frame/Marker */ static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, AnimData *adt, short autosnap) { /* snap key to nearest frame? */ if (autosnap == SACTSNAP_FRAME) { const Scene *scene= t->scene; const short doTime= getAnimEdit_DrawTime(t); const double secf= FPS; double val; /* convert frame to nla-action time (if needed) */ if (adt) val= BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); else val= *(td->val); /* do the snapping to nearest frame/second */ if (doTime) val= (float)( floor((val/secf) + 0.5f) * secf ); else val= (float)( floor(val+0.5f) ); /* convert frame out of nla-action time */ if (adt) *(td->val)= BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); else *(td->val)= val; } /* snap key to nearest marker? */ else if (autosnap == SACTSNAP_MARKER) { float val; /* convert frame to nla-action time (if needed) */ if (adt) val= BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); else val= *(td->val); /* snap to nearest marker */ // TODO: need some more careful checks for where data comes from val= (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val); /* convert frame out of nla-action time */ if (adt) *(td->val)= BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); else *(td->val)= val; } } /* ----------------- Translation ----------------------- */ void initTimeTranslate(TransInfo *t) { t->mode = TFM_TIME_TRANSLATE; t->transform = TimeTranslate; initMouseInputMode(t, &t->mouse, INPUT_NONE); /* num-input has max of (n-1) */ t->idx_max = 0; t->num.flag = 0; t->num.idx_max = t->idx_max; /* initialise snap like for everything else */ t->snap[0] = 0.0f; t->snap[1] = t->snap[2] = 1.0f; } static void headerTimeTranslate(TransInfo *t, char *str) { char tvec[60]; /* if numeric input is active, use results from that, otherwise apply snapping to result */ if (hasNumInput(&t->num)) { outputNumInput(&(t->num), tvec); } else { const Scene *scene = t->scene; const short autosnap= getAnimEdit_SnapMode(t); const short doTime = getAnimEdit_DrawTime(t); const double secf= FPS; float val = t->values[0]; /* apply snapping + frame->seconds conversions */ if (autosnap == SACTSNAP_STEP) { if (doTime) val= floor(val/secf + 0.5f); else val= floor(val + 0.5f); } else { if (doTime) val= val / secf; } sprintf(&tvec[0], "%.4f", val); } sprintf(str, "DeltaX: %s", &tvec[0]); } static void applyTimeTranslate(TransInfo *t, float sval) { TransData *td = t->data; Scene *scene = t->scene; int i; const short doTime= getAnimEdit_DrawTime(t); const double secf= FPS; const short autosnap= getAnimEdit_SnapMode(t); float deltax, val; /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ for (i = 0 ; i < t->total; i++, td++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) */ AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; /* check if any need to apply nla-mapping */ if (adt) { deltax = t->values[0]; if (autosnap == SACTSNAP_STEP) { if (doTime) deltax= (float)( floor((deltax/secf) + 0.5f) * secf ); else deltax= (float)( floor(deltax + 0.5f) ); } val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP); val += deltax; *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); } else { deltax = val = t->values[0]; if (autosnap == SACTSNAP_STEP) { if (doTime) val= (float)( floor((deltax/secf) + 0.5f) * secf ); else val= (float)( floor(val + 0.5f) ); } *(td->val) = td->ival + val; } /* apply nearest snapping */ doAnimEdit_SnapFrame(t, td, adt, autosnap); } } int TimeTranslate(TransInfo *t, short mval[2]) { View2D *v2d = (View2D *)t->view; float cval[2], sval[2]; char str[200]; /* calculate translation amount from mouse movement - in 'time-grid space' */ UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]); UI_view2d_region_to_view(v2d, t->imval[0], t->imval[0], &sval[0], &sval[1]); /* we only need to calculate effect for time (applyTimeTranslate only needs that) */ t->values[0] = cval[0] - sval[0]; /* handle numeric-input stuff */ t->vec[0] = t->values[0]; applyNumInput(&t->num, &t->vec[0]); t->values[0] = t->vec[0]; headerTimeTranslate(t, str); applyTimeTranslate(t, sval[0]); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ----------------- Time Slide ----------------------- */ void initTimeSlide(TransInfo *t) { /* this tool is only really available in the Action Editor... */ if (t->spacetype == SPACE_ACTION) { SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; /* set flag for drawing stuff */ saction->flag |= SACTION_MOVING; } t->mode = TFM_TIME_SLIDE; t->transform = TimeSlide; t->flag |= T_FREE_CUSTOMDATA; initMouseInputMode(t, &t->mouse, INPUT_NONE); /* num-input has max of (n-1) */ t->idx_max = 0; t->num.flag = 0; t->num.idx_max = t->idx_max; /* initialise snap like for everything else */ t->snap[0] = 0.0f; t->snap[1] = t->snap[2] = 1.0f; } static void headerTimeSlide(TransInfo *t, float sval, char *str) { char tvec[60]; if (hasNumInput(&t->num)) { outputNumInput(&(t->num), tvec); } else { float minx= *((float *)(t->customData)); float maxx= *((float *)(t->customData) + 1); float cval= t->values[0]; float val; val= 2.0f*(cval-sval) / (maxx-minx); CLAMP(val, -1.0f, 1.0f); sprintf(&tvec[0], "%.4f", val); } sprintf(str, "TimeSlide: %s", &tvec[0]); } static void applyTimeSlide(TransInfo *t, float sval) { TransData *td = t->data; int i; float minx= *((float *)(t->customData)); float maxx= *((float *)(t->customData) + 1); /* set value for drawing black line */ if (t->spacetype == SPACE_ACTION) { SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first; float cvalf = t->values[0]; saction->timeslide= cvalf; } /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ for (i = 0 ; i < t->total; i++, td++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) */ AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; float cval = t->values[0]; /* apply NLA-mapping to necessary values */ if (adt) cval= BKE_nla_tweakedit_remap(adt, cval, NLATIME_CONVERT_UNMAP); /* only apply to data if in range */ if ((sval > minx) && (sval < maxx)) { float cvalc= CLAMPIS(cval, minx, maxx); float timefac; /* left half? */ if (td->ival < sval) { timefac= (sval - td->ival) / (sval - minx); *(td->val)= cvalc - timefac * (cvalc - minx); } else { timefac= (td->ival - sval) / (maxx - sval); *(td->val)= cvalc + timefac * (maxx - cvalc); } } } } int TimeSlide(TransInfo *t, short mval[2]) { View2D *v2d = (View2D *)t->view; float cval[2], sval[2]; float minx= *((float *)(t->customData)); float maxx= *((float *)(t->customData) + 1); char str[200]; /* calculate mouse co-ordinates */ UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]); UI_view2d_region_to_view(v2d, t->imval[0], t->imval[0], &sval[0], &sval[1]); /* t->values[0] stores cval[0], which is the current mouse-pointer location (in frames) */ t->values[0] = cval[0]; /* handle numeric-input stuff */ t->vec[0] = 2.0f*(cval[0]-sval[0]) / (maxx-minx); applyNumInput(&t->num, &t->vec[0]); t->values[0] = (maxx-minx) * t->vec[0] / 2.0 + sval[0]; headerTimeSlide(t, sval[0], str); applyTimeSlide(t, sval[0]); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ----------------- Scaling ----------------------- */ void initTimeScale(TransInfo *t) { t->mode = TFM_TIME_SCALE; t->transform = TimeScale; initMouseInputMode(t, &t->mouse, INPUT_NONE); t->helpline = HLP_SPRING; /* set manually because we don't use a predefined input */ t->flag |= T_NULL_ONE; t->num.flag |= NUM_NULL_ONE; /* num-input has max of (n-1) */ t->idx_max = 0; t->num.flag = 0; t->num.idx_max = t->idx_max; /* initialise snap like for everything else */ t->snap[0] = 0.0f; t->snap[1] = t->snap[2] = 1.0f; } static void headerTimeScale(TransInfo *t, char *str) { char tvec[60]; if (hasNumInput(&t->num)) outputNumInput(&(t->num), tvec); else sprintf(&tvec[0], "%.4f", t->values[0]); sprintf(str, "ScaleX: %s", &tvec[0]); } static void applyTimeScale(TransInfo *t) { Scene *scene = t->scene; TransData *td = t->data; int i; const short autosnap= getAnimEdit_SnapMode(t); const short doTime= getAnimEdit_DrawTime(t); const double secf= FPS; for (i = 0 ; i < t->total; i++, td++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) */ AnimData *adt= (t->spacetype != SPACE_NLA) ? td->extra : NULL; float startx= CFRA; float fac= t->values[0]; if (autosnap == SACTSNAP_STEP) { if (doTime) fac= (float)( floor(fac/secf + 0.5f) * secf ); else fac= (float)( floor(fac + 0.5f) ); } /* check if any need to apply nla-mapping */ if (adt) startx= BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP); /* now, calculate the new value */ *(td->val) = td->ival - startx; *(td->val) *= fac; *(td->val) += startx; /* apply nearest snapping */ doAnimEdit_SnapFrame(t, td, adt, autosnap); } } int TimeScale(TransInfo *t, short mval[2]) { float cval, sval; float deltax, startx; float width= 0.0f; char str[200]; sval= t->imval[0]; cval= mval[0]; /* calculate scaling factor */ startx= sval-(width/2+(t->ar->winx)/2); deltax= cval-(width/2+(t->ar->winx)/2); t->values[0] = deltax / startx; /* handle numeric-input stuff */ t->vec[0] = t->values[0]; applyNumInput(&t->num, &t->vec[0]); t->values[0] = t->vec[0]; headerTimeScale(t, str); applyTimeScale(t); recalcData(t); ED_area_headerprint(t->sa, str); return 1; } /* ************************************ */ void BIF_TransformSetUndo(char *str) { // TRANSFORM_FIX_ME //Trans.undostr= str; } void NDofTransform() { #if 0 // TRANSFORM_FIX_ME float fval[7]; float maxval = 50.0f; // also serves as threshold int axis = -1; int mode = 0; int i; getndof(fval); for(i = 0; i < 6; i++) { float val = fabs(fval[i]); if (val > maxval) { axis = i; maxval = val; } } switch(axis) { case -1: /* No proper axis found */ break; case 0: case 1: case 2: mode = TFM_TRANSLATION; break; case 4: mode = TFM_ROTATION; break; case 3: case 5: mode = TFM_TRACKBALL; break; default: printf("ndof: what we are doing here ?"); } if (mode != 0) { initTransform(mode, CTX_NDOF); Transform(); } #endif }