diff options
author | Martin Poirier <theeth@yahoo.com> | 2008-12-29 04:41:28 +0300 |
---|---|---|
committer | Martin Poirier <theeth@yahoo.com> | 2008-12-29 04:41:28 +0300 |
commit | b6b61681efd322bd800bcaba55099cdf147547b2 (patch) | |
tree | bf745c0c58ee608ac8c3c53aa97a14932fc9135f | |
parent | 97a82102d4d24a94d8360a6f5f054e44f6fb1993 (diff) |
2.5
Transform:
First working port of the transform code:
- Object mode only (other conversions need to be ported)
- Contraints (global and local only) working
- Snap (no edit mode, obviously) working
- Numinput working
- Gears (Ctrl and Shift) working
- Only grap, rotate, scale, shear, warp and to sphere have been added as hotkey, but the rest should work too once accessible
- No manipulator
- No drawn feedback other than moving stuff and header print (no constraint line, snap circle, ...)
- No NDOF support
I've only tested Scons support, though Makefil *should* work, I *think*.
Misc:
-QuatIsNull function in arith
-Exporting project_* and view[line|ray] functions from view3d
18 files changed, 16523 insertions, 12 deletions
diff --git a/source/blender/blenlib/BLI_arithb.h b/source/blender/blenlib/BLI_arithb.h index 186681fad7d..8c55a83785b 100644 --- a/source/blender/blenlib/BLI_arithb.h +++ b/source/blender/blenlib/BLI_arithb.h @@ -123,6 +123,7 @@ void Mat3ToCompatibleEul(float mat[][3], float *eul, float *oldrot); * @section Quaternion arithmetic routines */ +int QuatIsNul(float *q); void QuatToEul(float *quat, float *eul); void QuatOne(float *); void QuatMul(float *, float *, float *); diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index a47d37eb69a..4d555a009c5 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -1087,6 +1087,10 @@ void printmatrix3( char *str, float m[][3]) /* **************** QUATERNIONS ********** */ +int QuatIsNul(float *q) +{ + return (q[0] == 0 && q[1] == 0 && q[2] == 0 && q[3] == 0); +} void QuatMul(float *q, float *q1, float *q2) { diff --git a/source/blender/editors/include/BIF_transform.h b/source/blender/editors/include/BIF_transform.h new file mode 100644 index 00000000000..ca8488e1b63 --- /dev/null +++ b/source/blender/editors/include/BIF_transform.h @@ -0,0 +1,135 @@ +/** + * $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 ***** + */ + +#ifndef BIF_TRANSFORM_H +#define BIF_TRANSFORM_H + +/* ******************* Registration Function ********************** */ + +struct wmWindowManager; +struct ListBase; +struct wmEvent; +struct bContext; + +void transform_keymap_for_space(struct wmWindowManager *wm, struct ListBase *keymap, int spaceid); +void transform_operatortypes(void); + +/* ******************** Macros & Prototypes *********************** */ + +/* MODE AND NUMINPUT FLAGS */ +#define TFM_INIT -1 +#define TFM_DUMMY 0 +#define TFM_TRANSLATION 1 +#define TFM_ROTATION 2 +#define TFM_RESIZE 3 +#define TFM_TOSPHERE 4 +#define TFM_SHEAR 5 +#define TFM_WARP 7 +#define TFM_SHRINKFATTEN 8 +#define TFM_TILT 9 +#define TFM_LAMP_ENERGY 10 +#define TFM_TRACKBALL 11 +#define TFM_PUSHPULL 12 +#define TFM_CREASE 13 +#define TFM_MIRROR 14 +#define TFM_BONESIZE 15 +#define TFM_BONE_ENVELOPE 16 +#define TFM_CURVE_SHRINKFATTEN 17 +#define TFM_BONE_ROLL 18 +#define TFM_TIME_TRANSLATE 19 +#define TFM_TIME_SLIDE 20 +#define TFM_TIME_SCALE 21 +#define TFM_TIME_EXTEND 22 +#define TFM_BAKE_TIME 23 +#define TFM_BEVEL 24 +#define TFM_BWEIGHT 25 +#define TFM_ALIGN 26 + +/* TRANSFORM CONTEXTS */ +#define CTX_NONE 0 +#define CTX_TEXTURE 1 +#define CTX_EDGE 2 +#define CTX_NO_PET 4 +#define CTX_TWEAK 8 +#define CTX_NO_MIRROR 16 +#define CTX_AUTOCONFIRM 32 +#define CTX_BMESH 64 +#define CTX_NDOF 128 + +/* Standalone call to get the transformation center corresponding to the current situation + * returns 1 if successful, 0 otherwise (usually means there's no selection) + * (if 0 is returns, *vec is unmodified) + * */ +int calculateTransformCenter(struct bContext *C, struct wmEvent *event, int centerMode, float *vec); + +struct TransInfo; +struct ScrArea; +struct Base; +struct Scene; + +struct TransInfo * BIF_GetTransInfo(void); +void BIF_setSingleAxisConstraint(float vec[3], char *text); +void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text); +void BIF_setLocalAxisConstraint(char axis, char *text); +void BIF_setLocalLockConstraint(char axis, char *text); + +int BIF_snappingSupported(void); + +struct TransformOrientation; + +void BIF_clearTransformOrientation(void); +void BIF_removeTransformOrientation(struct TransformOrientation *ts); +void BIF_manageTransformOrientation(int confirm, int set); +int BIF_menuselectTransformOrientation(void); +void BIF_selectTransformOrientation(struct TransformOrientation *ts); +void BIF_selectTransformOrientationFromIndex(int index); + +char * BIF_menustringTransformOrientation(char *title); /* the returned value was allocated and needs to be freed after use */ +int BIF_countTransformOrientation(); + +/* Drawing callbacks */ +void BIF_drawConstraint(void); +void BIF_drawPropCircle(void); +void BIF_drawSnap(void); + +void BIF_getPropCenter(float *center); + +void BIF_TransformSetUndo(char *str); + +void BIF_selectOrientation(void); + +/* view3d manipulators */ +void initManipulator(int mode); +void ManipulatorTransform(); + +//int BIF_do_manipulator(struct ScrArea *sa); +//void BIF_draw_manipulator(struct ScrArea *sa); + +#endif + diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index cd342b1da51..9b0dec908a8 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -29,8 +29,28 @@ #define ED_VIEW3D_H /* ********* exports for space_view3d/ module ********** */ +struct ARegion; +struct View3D; + float *give_cursor(Scene *scene, View3D *v3d); +void initgrabz(struct View3D *v3d, float x, float y, float z); +void window_to_3d(struct ARegion *ar, struct View3D *v3d, float *vec, short mx, short my); + +/* Projection */ + +void project_short(struct ARegion *ar, struct View3D *v3d, float *vec, short *adr); +void project_short_noclip(struct ARegion *ar, struct View3D *v3d, float *vec, short *adr); + +void project_int(struct ARegion *ar, struct View3D *v3d, float *vec, int *adr); +void project_int_noclip(struct ARegion *ar, struct View3D *v3d, float *vec, int *adr); + +void project_float(struct ARegion *ar, struct View3D *v3d, float *vec, float *adr); +void project_float_noclip(struct ARegion *ar, struct View3D *v3d, float *vec, float *adr); + +void viewline(struct ARegion *ar, struct View3D *v3d, short mval[2], float ray_start[3], float ray_end[3]); +void viewray(struct ARegion *ar, struct View3D *v3d, short mval[2], float ray_start[3], float ray_normal[3]); + #endif /* ED_VIEW3D_H */ diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index bb9a0644384..1c4bae503b6 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -130,11 +130,7 @@ void VIEW3D_OT_circle_select(struct wmOperatorType *ot); void VIEW3D_OT_smoothview(struct wmOperatorType *ot); void view3d_operator_needs_opengl(const struct bContext *C); -void viewline(ARegion *ar, View3D *v3d, short mval[2], float ray_start[3], float ray_end[3]); -void viewray(ARegion *ar, View3D *v3d, short mval[2], float ray_start[3], float ray_normal[3]); -void initgrabz(View3D *v3d, float x, float y, float z); -void window_to_3d(ARegion *ar, View3D *v3d, float *vec, short mx, short my); int boundbox_clip(View3D *v3d, float obmat[][4], struct BoundBox *bb); void view3d_project_short_clip(ARegion *ar, View3D *v3d, float *vec, short *adr, float projmat[4][4], float wmat[4][4]); @@ -143,13 +139,6 @@ void view3d_project_float(ARegion *a, float *vec, float *adr, float mat[4][4]); void view3d_get_object_project_mat(View3D *v3d, struct Object *ob, float pmat[4][4], float vmat[4][4]); void view3d_project_float(ARegion *ar, float *vec, float *adr, float mat[4][4]); -void project_short(ARegion *ar, View3D *v3d, float *vec, short *adr); -void project_int(ARegion *ar, View3D *v3d, float *vec, int *adr); -void project_int_noclip(ARegion *ar, View3D *v3d, float *vec, int *adr); -void project_short_noclip(ARegion *ar, View3D *v3d, float *vec, short *adr); -void project_float(ARegion *ar, View3D *v3d, float *vec, float *adr); -void project_float_noclip(ARegion *ar, View3D *v3d, float *vec, float *adr); - int get_view3d_viewplane(View3D *v3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize); void view_settings_from_ob(Object *ob, float *ofs, float *quat, float *dist, float *lens); void obmat_to_viewmat(View3D *v3d, Object *ob, short smooth); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 52147bbd557..e20752e28a1 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -46,6 +46,8 @@ #include "BKE_global.h" #include "BKE_utildefines.h" +#include "BIF_transform.h" + #include "RNA_access.h" #include "RNA_define.h" @@ -79,6 +81,8 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_smoothview); WM_operatortype_append(VIEW3D_OT_render_border); WM_operatortype_append(VIEW3D_OT_cursor3d); + + transform_operatortypes(); } void view3d_keymap(wmWindowManager *wm) @@ -136,5 +140,7 @@ void view3d_keymap(wmWindowManager *wm) /* TODO - this is just while we have no way to load a text datablock */ RNA_string_set(WM_keymap_add_item(keymap, "SCRIPT_OT_run_pyfile", PKEY, KM_PRESS, 0, 0)->ptr, "filename", "test.py"); + transform_keymap_for_space(wm, keymap, SPACE_VIEW3D); + } diff --git a/source/blender/editors/transform/Makefile b/source/blender/editors/transform/Makefile index b2aeaa145f7..71a75b9bda0 100644 --- a/source/blender/editors/transform/Makefile +++ b/source/blender/editors/transform/Makefile @@ -28,7 +28,7 @@ # # Makes module object directory and bounces make to subdirectories. -LIBNAME = ed_screen +LIBNAME = ed_transform DIR = $(OCGDIR)/blender/$(LIBNAME) include nan_compile.mk diff --git a/source/blender/editors/transform/SConscript b/source/blender/editors/transform/SConscript index e69de29bb2d..2c44204afa9 100644 --- a/source/blender/editors/transform/SConscript +++ b/source/blender/editors/transform/SConscript @@ -0,0 +1,11 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' ../../render/extern/include #/intern/guardedalloc #intern/bmfont' +incs += ' ../../gpu ../../makesrna' + +env.BlenderLib ( 'bf_editors_transform', sources, Split(incs), [], libtype=['core'], priority=[40] )
\ No newline at end of file diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c new file mode 100644 index 00000000000..d3c5303798f --- /dev/null +++ b/source/blender/editors/transform/transform.c @@ -0,0 +1,4827 @@ +/** + * $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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include "MEM_guardedalloc.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 "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 "BIF_drawimage.h" /* uvco_to_areaco_noclip */ +//#include "BIF_editaction.h" + +#include "BKE_action.h" /* get_action_frame */ +//#include "BKE_bad_level_calls.h"/* popmenu and error */ +#include "BKE_bmesh.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 "BSE_drawipo.h" +//#include "BSE_editnla_types.h" /* for NLAWIDTH */ +//#include "BSE_editaction_types.h" +//#include "BSE_time.h" +//#include "BSE_view.h" + +#include "ED_view3d.h" +#include "ED_screen.h" +#include "WM_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" + +#include "PIL_time.h" /* sleep */ + +//#include "blendef.h" +// +//#include "mydevice.h" + +#include "transform.h" + +/******************************** Helper functions ************************************/ + +/* GLOBAL Wrapper Fonctions */ + +//void BIF_drawSnap() +//{ +// drawSnapping(&Trans); +//} + +/* ************************** Dashed help line **************************** */ + + +/* bad frontbuffer call... because it is used in transform after force_draw() */ +static void helpline(TransInfo *t, float *vec) +{ +#if 0 // TRANSFORM_FIX_ME + float vecrot[3], cent[2]; + short mval[2]; + + VECCOPY(vecrot, vec); + if(t->flag & T_EDIT) { + Object *ob=G.obedit; + if(ob) Mat4MulVecfl(ob->obmat, vecrot); + } + else if(t->flag & T_POSE) { + Object *ob=t->poseobj; + if(ob) Mat4MulVecfl(ob->obmat, vecrot); + } + + getmouseco_areawin(mval); + projectFloatView(t, vecrot, cent); // no overflow in extreme cases + + persp(PERSP_WIN); + + glDrawBuffer(GL_FRONT); + + BIF_ThemeColor(TH_WIRE); + + setlinestyle(3); + glBegin(GL_LINE_STRIP); + glVertex2sv(mval); + glVertex2fv(cent); + glEnd(); + setlinestyle(0); + + persp(PERSP_VIEW); + bglFlush(); // flush display for frontbuffer + glDrawBuffer(GL_BACK); +#endif +} + + + +/* ************************** INPUT FROM MOUSE *************************** */ + +float InputScaleRatio(TransInfo *t, short mval[2]) { + float ratio, dx, dy; + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + dx = (float)(t->center2d[0] - t->shiftmval[0]); + dy = (float)(t->center2d[1] - t->shiftmval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + ratio+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -ratio); + } + else { + dx = (float)(t->center2d[0] - mval[0]); + dy = (float)(t->center2d[1] - mval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + return ratio; +} + +float InputHorizontalRatio(TransInfo *t, short mval[2]) { + float x, pad; + + pad = t->ar->winx / 10; + + if (t->flag & T_SHIFT_MOD) { + /* deal with Shift key by adding motion / 10 to motion before shift press */ + x = t->shiftmval[0] + (float)(mval[0] - t->shiftmval[0]) / 10.0f; + } + else { + x = mval[0]; + } + return (x - pad) / (t->ar->winx - 2 * pad); +} + +float InputHorizontalAbsolute(TransInfo *t, short mval[2]) { + float vec[3]; + if(t->flag & T_SHIFT_MOD) { + float dvec[3]; + /* calculate the main translation and the precise one separate */ + convertViewVec(t, dvec, (short)(mval[0] - t->shiftmval[0]), (short)(mval[1] - t->shiftmval[1])); + VecMulf(dvec, 0.1f); + convertViewVec(t, t->vec, (short)(t->shiftmval[0] - t->imval[0]), (short)(t->shiftmval[1] - t->imval[1])); + VecAddf(t->vec, t->vec, dvec); + } + else { + convertViewVec(t, t->vec, (short)(mval[0] - t->imval[0]), (short)(mval[1] - t->imval[1])); + } + Projf(vec, t->vec, t->viewinv[0]); + return Inpf(t->viewinv[0], vec) * 2.0f; +} + +float InputVerticalRatio(TransInfo *t, short mval[2]) { + float y, pad; + + pad = t->ar->winy / 10; + + if (t->flag & T_SHIFT_MOD) { + /* deal with Shift key by adding motion / 10 to motion before shift press */ + y = t->shiftmval[1] + (float)(mval[1] - t->shiftmval[1]) / 10.0f; + } + else { + y = mval[0]; + } + return (y - pad) / (t->ar->winy - 2 * pad); +} + +float InputVerticalAbsolute(TransInfo *t, short mval[2]) { + float vec[3]; + if(t->flag & T_SHIFT_MOD) { + float dvec[3]; + /* calculate the main translation and the precise one separate */ + convertViewVec(t, dvec, (short)(mval[0] - t->shiftmval[0]), (short)(mval[1] - t->shiftmval[1])); + VecMulf(dvec, 0.1f); + convertViewVec(t, t->vec, (short)(t->shiftmval[0] - t->imval[0]), (short)(t->shiftmval[1] - t->imval[1])); + VecAddf(t->vec, t->vec, dvec); + } + else { + convertViewVec(t, t->vec, (short)(mval[0] - t->imval[0]), (short)(mval[1] - t->imval[1])); + } + Projf(vec, t->vec, t->viewinv[1]); + return Inpf(t->viewinv[1], vec) * 2.0f; +} + +float InputDeltaAngle(TransInfo *t, short mval[2]) +{ + double dx2 = mval[0] - t->center2d[0]; + double dy2 = mval[1] - t->center2d[1]; + double B = sqrt(dx2*dx2+dy2*dy2); + + double dx1 = t->imval[0] - t->center2d[0]; + double dy1 = t->imval[1] - t->center2d[1]; + double A = sqrt(dx1*dx1+dy1*dy1); + + double dx3 = mval[0] - t->imval[0]; + double dy3 = mval[1] - t->imval[1]; + + /* use doubles here, to make sure a "1.0" (no rotation) doesnt become 9.999999e-01, which gives 0.02 for acos */ + double deler = ((dx1*dx1+dy1*dy1)+(dx2*dx2+dy2*dy2)-(dx3*dx3+dy3*dy3)) + / (2.0 * (A*B?A*B:1.0)); + /* (A*B?A*B:1.0f) this takes care of potential divide by zero errors */ + + float dphi; + + dphi = saacos((float)deler); + if( (dx1*dy2-dx2*dy1)>0.0 ) dphi= -dphi; + + /* If the angle is zero, because of lack of precision close to the 1.0 value in acos + * approximate the angle with the oposite side of the normalized triangle + * This is a good approximation here since the smallest acos value seems to be around + * 0.02 degree and lower values don't even have a 0.01% error compared to the approximation + * */ + if (dphi == 0) + { + double dx, dy; + + dx2 /= A; + dy2 /= A; + + dx1 /= B; + dy1 /= B; + + dx = dx1 - dx2; + dy = dy1 - dy2; + + dphi = sqrt(dx*dx + dy*dy); + if( (dx1*dy2-dx2*dy1)>0.0 ) dphi= -dphi; + } + + if(t->flag & T_SHIFT_MOD) dphi = dphi/30.0f; + + /* if no delta angle, don't update initial position */ + if (dphi != 0) + { + t->imval[0] = mval[0]; + t->imval[1] = mval[1]; + } + + return dphi; +} + +/* ************************** SPACE DEPENDANT CODE **************************** */ + +void setTransformViewMatrices(TransInfo *t) +{ + if(t->spacetype==SPACE_VIEW3D) { + View3D *v3d = t->view; + + Mat4CpyMat4(t->viewmat, v3d->viewmat); + Mat4CpyMat4(t->viewinv, v3d->viewinv); + Mat4CpyMat4(t->persmat, v3d->persmat); + Mat4CpyMat4(t->persinv, v3d->persinv); + t->persp = v3d->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) { + window_to_3d(t->ar, t->view, vec, dx, dy); + } + else if(t->spacetype==SPACE_IMAGE) { + View2D *v2d = t->view; + float divx, divy, aspx, aspy; + + // TRANSFORM_FIX_ME + //transform_aspect_ratio_tface_uv(&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(t->spacetype==SPACE_IPO) { + 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; + } +} + +void projectIntView(TransInfo *t, float *vec, int *adr) +{ + if (t->spacetype==SPACE_VIEW3D) { + project_int_noclip(t->ar, t->view, vec, adr); + } + else if(t->spacetype==SPACE_IMAGE) { + float aspx, aspy, v[2]; + + // TRANSFORM_FIX_ME + //transform_aspect_ratio_tface_uv(&aspx, &aspy); + v[0]= vec[0]/aspx; + v[1]= vec[1]/aspy; + + // TRANSFORM_FIX_ME + //uvco_to_areaco_noclip(v, adr); + } + else if(t->spacetype==SPACE_IPO) { + short out[2] = {0.0f, 0.0f}; + + // TRANSFORM_FIX_ME + //ipoco_to_areaco(G.v2d, vec, out); + adr[0]= out[0]; + adr[1]= out[1]; + } +} + +void projectFloatView(TransInfo *t, float *vec, float *adr) +{ + if (t->spacetype==SPACE_VIEW3D) { + project_float_noclip(t->ar, t->view, 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(t->spacetype==SPACE_IPO) { + int a[2]; + + projectIntView(t, vec, a); + adr[0]= a[0]; + adr[1]= a[1]; + } +} + +void convertVecToDisplayNum(float *vec, float *num) +{ + // TRANSFORM_FIX_ME + TransInfo *t = NULL; //BIF_GetTransInfo(); + + VECCOPY(num, vec); + + if ((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) { +#if 0 // TRANSFORM_FIX_ME + float aspx, aspy; + + if((G.sima->flag & SI_COORDFLOATS)==0) { + int width, height; + transform_width_height_tface_uv(&width, &height); + + num[0] *= width; + num[1] *= height; + } + + transform_aspect_ratio_tface_uv(&aspx, &aspy); + num[0] /= aspx; + num[1] /= aspy; +#endif + } +} + +void convertDisplayNumToVec(float *num, float *vec) +{ + // TRANSFORM_FIX_ME + TransInfo *t = NULL; //BIF_GetTransInfo(); + + VECCOPY(vec, num); + + if ((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) { +#if 0 // TRANSFORM_FIX_ME + float aspx, aspy; + + if((G.sima->flag & SI_COORDFLOATS)==0) { + int width, height; + transform_width_height_tface_uv(&width, &height); + + vec[0] /= width; + vec[1] /= height; + } + + transform_aspect_ratio_tface_uv(&aspx, &aspy); + vec[0] *= aspx; + vec[1] *= aspy; +#endif + } +} + +static void viewRedrawForce(TransInfo *t) +{ + if (t->spacetype == SPACE_VIEW3D) + { + // TRANSFORM_FIX_ME + // need to redraw ALL 3d view + ED_area_tag_redraw(t->sa); + } +#if 0 // TRANSFORM_FIX_ME + else if (t->spacetype==SPACE_IMAGE) { + if (G.sima->lock) force_draw_plus(SPACE_VIEW3D, 0); + else force_draw(0); + } + else if (t->spacetype == SPACE_ACTION) { + if (G.saction->lock) { + short context; + + /* we ignore the pointer this function returns (not needed) */ + get_action_context(&context); + + if (context == ACTCONT_ACTION) + force_draw_plus(SPACE_VIEW3D, 0); + else if (context == ACTCONT_SHAPEKEY) + force_draw_all(0); + else + force_draw(0); + } + else { + force_draw(0); + } + } + else if (t->spacetype == SPACE_NLA) { + if (G.snla->lock) + force_draw_all(0); + else + force_draw(0); + } + else if (t->spacetype == SPACE_IPO) { + /* update realtime */ + if (G.sipo->lock) { + if (G.sipo->blocktype==ID_MA || G.sipo->blocktype==ID_TE) + force_draw_plus(SPACE_BUTS, 0); + else if (G.sipo->blocktype==ID_CA) + force_draw_plus(SPACE_VIEW3D, 0); + else if (G.sipo->blocktype==ID_KE) + force_draw_plus(SPACE_VIEW3D, 0); + else if (G.sipo->blocktype==ID_PO) + force_draw_plus(SPACE_VIEW3D, 0); + else if (G.sipo->blocktype==ID_OB) + force_draw_plus(SPACE_VIEW3D, 0); + else if (G.sipo->blocktype==ID_SEQ) + force_draw_plus(SPACE_SEQ, 0); + else + force_draw(0); + } + else { + force_draw(0); + } + } +#endif +} + +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 +} + +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"; +} + +/* ************************************************* */ + +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->event = event; + + if (event->type == MOUSEMOVE) + { + t->mval[0] = event->x - t->ar->winrct.xmin; + t->mval[1] = event->y - t->ar->winrct.ymin; + } + + if (event->val) { + switch (event->type){ + /* enforce redraw of transform when modifiers are used */ + case LEFTCTRLKEY: + case RIGHTCTRLKEY: + t->redraw = 1; + break; + case LEFTSHIFTKEY: + case RIGHTSHIFTKEY: + /* shift is modifier for higher resolution transform, works nice to store this mouse position */ + t->shiftmval[0] = event->x - t->ar->winrct.xmin; + t->shiftmval[1] = event->y - t->ar->winrct.ymin; + t->flag |= T_SHIFT_MOD; + 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->flag |= T_MMB_PRESSED; + 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: + case RIGHTMOUSE: + t->state = TRANS_CANCEL; + break; + case LEFTMOUSE: + 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); + 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); + 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); + } + 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 (event->keymodifier == 0) + setUserConstraint(t, (CON_AXIS0), "along %s X"); + else if (event->keymodifier == KM_SHIFT) + 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 (event->keymodifier == 0) + setConstraint(t, mati, (CON_AXIS0), "along global X"); + else if (event->keymodifier == KM_SHIFT) + 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 (event->keymodifier == 0) + setUserConstraint(t, (CON_AXIS1), "along %s Y"); + else if (event->keymodifier == KM_SHIFT) + 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 (event->keymodifier == 0) + setConstraint(t, mati, (CON_AXIS1), "along global Y"); + else if (event->keymodifier == KM_SHIFT) + 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 (event->keymodifier == 0) + setUserConstraint(t, (CON_AXIS2), "along %s Z"); + else if ((event->keymodifier == KM_SHIFT) && ((t->flag & T_2D_EDIT)==0)) + setUserConstraint(t, (CON_AXIS0|CON_AXIS1), "locking %s Z"); + } + } + else if ((t->flag & T_2D_EDIT)==0) { + if (event->keymodifier == 0) + setConstraint(t, mati, (CON_AXIS2), "along global Z"); + else if (event->keymodifier == KM_SHIFT) + setConstraint(t, mati, (CON_AXIS0|CON_AXIS1), "locking global Z"); + } + t->redraw = 1; + } + break; + case OKEY: + if (t->flag & T_PROP_EDIT && event->keymodifier == KM_SHIFT) { + G.scene->prop_mode = (G.scene->prop_mode+1)%6; + calculatePropRatio(t); + t->redraw= 1; + } + break; + case PADPLUSKEY: + if(event->keymodifier & KM_ALT && t->flag & T_PROP_EDIT) { + t->propsize*= 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->propsize*= 1.1f; + calculatePropRatio(t); + } + else view_editmove(event->type); + t->redraw= 1; + break; + case PADMINUS: + if(event->keymodifier & KM_ALT && t->flag & T_PROP_EDIT) { + t->propsize*= 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->propsize*= 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->context & CTX_NDOF) == 0) + { + /* Confirm on normal transform only */ + t->state = TRANS_CONFIRM; + } + break; + case NDOF_CANCEL: + if (t->context & 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->context & 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){ + /* no redraw on release modifier keys! this makes sure you can assign the 'grid' still + after releasing modifer key */ + case MIDDLEMOUSE: + if ((t->flag & T_NO_CONSTRAINT)==0) { + t->flag &= ~T_MMB_PRESSED; + postSelectConstraint(t); + t->redraw = 1; + } + break; + case LEFTMOUSE: + case RIGHTMOUSE: + if (t->context & CTX_TWEAK) + t->state = TRANS_CONFIRM; + break; + case LEFTSHIFTKEY: + case RIGHTSHIFTKEY: + /* shift is modifier for higher resolution transform */ + t->flag &= ~T_SHIFT_MOD; + 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->context = CTX_NONE; + + t->mode = TFM_DUMMY; + + initTransInfo(C, t, 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; +} + +void initTransform(bContext *C, TransInfo *t, int mode, int context, wmEvent *event) +{ + /* added initialize, for external calls to set stuff in TransInfo, like undo string */ + + t->state = TRANS_RUNNING; + + t->context = context; + + t->mode = mode; + + initTransInfo(C, t, event); // internal data, mouse, vectors + + if(t->spacetype == SPACE_VIEW3D) + { + View3D *v3d = t->view; + //calc_manipulator_stats(curarea); + Mat3CpyMat4(t->spacemtx, v3d->twmat); + Mat3Ortho(t->spacemtx); + } + else + Mat3One(t->spacemtx); + + createTransData(C, t); // make TransData structs from selection + + initSnapping(t); // Initialize snapping data AFTER mode flags + + if (t->total == 0) { + postTrans(t); + return; + } + + /* 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); + + 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_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 */ + 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; + } +} + +void transformApply(TransInfo *t) +{ + if (1) // MOUSE MOVE + { + if (t->flag & T_MMB_PRESSED) + t->con.mode |= CON_SELECT; + t->redraw = 1; + } + if (t->redraw) + { + // RESET MOUSE MOVE + + selectConstraint(t); + if (t->transform) { + t->transform(t, t->mval); // calls recalcData() + } + t->redraw = 0; + } + + /* If auto confirm is on, break after one pass */ + if (t->context & CTX_AUTOCONFIRM) + { + t->state = TRANS_CONFIRM; + } + + if (BKE_ptcache_get_continue_physics()) + { + // TRANSFORM_FIX_ME + //do_screenhandlers(G.curscreen); + t->redraw = 1; + } +} + +int transformEnd(TransInfo *t) +{ + if (t->state != TRANS_RUNNING) + { + /* handle restoring objects */ + if(t->state == TRANS_CANCEL) + restoreTransObjects(t); // calls recalcData() + + /* free data */ + postTrans(t); + + /* aftertrans does insert ipos and action channels, 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 0 // TRANSFORM_FIX_ME + if(t->state == TRANS_CANCEL) { + if(t->undostr) BIF_undo_push(t->undostr); + } + else { + if(t->undostr) BIF_undo_push(t->undostr); + else BIF_undo_push(transform_to_undostr(t)); + } + t->undostr= NULL; +#endif + return 1; + } + + t->event = NULL; + + return 0; +} + +/* ************************** Manipulator init and main **************************** */ + +void initManipulator(int mode) +{ +#if 0 // TRANSFORM_FIX_ME + Trans.state = TRANS_RUNNING; + + Trans.context = CTX_NONE; + + Trans.mode = mode; + + /* automatic switch to scaling bone envelopes */ + if(mode==TFM_RESIZE && G.obedit && G.obedit->type==OB_ARMATURE) { + bArmature *arm= G.obedit->data; + if(arm->drawtype==ARM_ENVELOPE) + mode= TFM_BONE_ENVELOPE; + } + + initTrans(&Trans); // internal data, mouse, vectors + + G.moving |= G_TRANSFORM_MANIP; // signal to draw manipuls while transform + createTransData(&Trans); // make TransData structs from selection + + if (Trans.total == 0) + return; + + initSnapping(&Trans); // 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 */ + mode = Trans.mode; + + calculatePropRatio(&Trans); + calculateCenter(&Trans); + + switch (mode) { + case TFM_TRANSLATION: + initTranslation(&Trans); + break; + case TFM_ROTATION: + initRotation(&Trans); + break; + case TFM_RESIZE: + initResize(&Trans); + break; + case TFM_TRACKBALL: + initTrackball(&Trans); + break; + } + + Trans.flag |= T_USES_MANIPULATOR; +#endif +} + +void ManipulatorTransform() +{ +#if 0 // TRANSFORM_FIX_ME + int mouse_moved = 0; + short pmval[2] = {0, 0}, mval[2], val; + unsigned short event; + + if (Trans.total == 0) + return; + + Trans.redraw = 1; /* initial draw */ + + while (Trans.state == TRANS_RUNNING) { + + getmouseco_areawin(mval); + + if (mval[0] != pmval[0] || mval[1] != pmval[1]) { + Trans.redraw = 1; + } + if (Trans.redraw) { + pmval[0] = mval[0]; + pmval[1] = mval[1]; + + //selectConstraint(&Trans); needed? + if (Trans.transform) { + Trans.transform(&Trans, mval); + } + Trans.redraw = 0; + } + + /* essential for idling subloop */ + if( qtest()==0) PIL_sleep_ms(2); + + while( qtest() ) { + event= extern_qread(&val); + + switch (event){ + case MOUSEX: + case MOUSEY: + mouse_moved = 1; + break; + /* enforce redraw of transform when modifiers are used */ + case LEFTCTRLKEY: + case RIGHTCTRLKEY: + if(val) Trans.redraw = 1; + break; + case LEFTSHIFTKEY: + case RIGHTSHIFTKEY: + /* shift is modifier for higher resolution transform, works nice to store this mouse position */ + if(val) { + getmouseco_areawin(Trans.shiftmval); + Trans.flag |= T_SHIFT_MOD; + Trans.redraw = 1; + } + else Trans.flag &= ~T_SHIFT_MOD; + break; + + case ESCKEY: + case RIGHTMOUSE: + Trans.state = TRANS_CANCEL; + break; + case LEFTMOUSE: + if(mouse_moved==0 && val==0) break; + // else we pass on event to next, which cancels + case SPACEKEY: + case PADENTER: + case RETKEY: + Trans.state = TRANS_CONFIRM; + break; + // case NDOFMOTION: + // viewmoveNDOF(1); + // break; + } + if(val) { + switch(event) { + case PADPLUSKEY: + if(G.qual & LR_ALTKEY && Trans.flag & T_PROP_EDIT) { + Trans.propsize*= 1.1f; + calculatePropRatio(&Trans); + } + Trans.redraw= 1; + break; + case PAGEUPKEY: + case WHEELDOWNMOUSE: + if (Trans.flag & T_AUTOIK) { + transform_autoik_update(&Trans, 1); + } + else if(Trans.flag & T_PROP_EDIT) { + Trans.propsize*= 1.1f; + calculatePropRatio(&Trans); + } + else view_editmove(event); + Trans.redraw= 1; + break; + case PADMINUS: + if(G.qual & LR_ALTKEY && Trans.flag & T_PROP_EDIT) { + Trans.propsize*= 0.90909090f; + calculatePropRatio(&Trans); + } + Trans.redraw= 1; + break; + case PAGEDOWNKEY: + case WHEELUPMOUSE: + if (Trans.flag & T_AUTOIK) { + transform_autoik_update(&Trans, -1); + } + else if (Trans.flag & T_PROP_EDIT) { + Trans.propsize*= 0.90909090f; + calculatePropRatio(&Trans); + } + else view_editmove(event); + Trans.redraw= 1; + break; + } + + // Numerical input events + Trans.redraw |= handleNumInput(&(Trans.num), event); + } + } + } + + if(Trans.state == TRANS_CANCEL) { + restoreTransObjects(&Trans); + } + + /* free data, reset vars */ + postTrans(&Trans); + + /* aftertrans does insert ipos and action channels, and clears base flags */ + special_aftertrans_update(&Trans); + + /* send events out for redraws */ + viewRedrawPost(&Trans); + + if(Trans.state != TRANS_CANCEL) { + BIF_undo_push(transform_to_undostr(&Trans)); + } +#endif +} + +/* ************************** 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]; +} + +static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat) +{ + /* quaternions get limited with euler... */ + /* this function only does the delta rotation */ + + if(protectflag) { + 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); + if (td->tdi) { + TransDataIpokey *tdi= td->tdi; + cob.matrix[3][0]= tdi->locx[0]; + cob.matrix[3][1]= tdi->locy[0]; + cob.matrix[3][2]= tdi->locz[0]; + } + else { + 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 */ + if (td->tdi) { + TransDataIpokey *tdi= td->tdi; + tdi->locx[0]= cob.matrix[3][0]; + tdi->locy[0]= cob.matrix[3][1]; + tdi->locz[0]= cob.matrix[3][2]; + } + else { + 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->flag & TD_USEQUAT) { + /* quats */ + if (td->ext) + QuatToMat4(td->ext->quat, cob.matrix); + else + return; + } + else if (td->tdi) { + /* ipo-keys eulers */ + TransDataIpokey *tdi= td->tdi; + float eul[3]; + + eul[0]= tdi->rotx[0]; + eul[1]= tdi->roty[0]; + eul[2]= tdi->rotz[0]; + + EulToMat4(eul, cob.matrix); + } + else { + /* eulers */ + if (td->ext) + EulToMat4(td->ext->rot, 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->flag & TD_USEQUAT) { + /* quats */ + Mat4ToQuat(cob.matrix, td->ext->quat); + } + else if (td->tdi) { + /* ipo-keys eulers */ + TransDataIpokey *tdi= td->tdi; + float eul[3]; + + Mat4ToEul(cob.matrix, eul); + + tdi->rotx[0]= eul[0]; + tdi->roty[0]= eul[1]; + tdi->rotz[0]= eul[2]; + } + else { + /* eulers */ + Mat4ToEul(cob.matrix, td->ext->rot); + } + } +} + +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->tdi) { + TransDataIpokey *tdi= td->tdi; + float size[3]; + + size[0]= tdi->sizex[0]; + size[1]= tdi->sizey[0]; + size[2]= tdi->sizez[0]; + SizeToMat4(size, cob.matrix); + } + else 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->tdi) { + TransDataIpokey *tdi= td->tdi; + float size[3]; + + Mat4ToSize(cob.matrix, size); + + tdi->sizex[0]= size[0]; + tdi->sizey[0]= size[1]; + tdi->sizez[0]= size[2]; + } + else 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; + + 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; + +/* warp is done fully in view space */ + calculateCenterCursor(t); + t->fac = (float)(t->center2d[0] - t->imval[0]); + + /* 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) + { + // 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, G.obedit->obmat[3]); + VecSubf(gcursor, gcursor, G.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 * InputHorizontalRatio(t, mval); + + 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); + + viewRedrawForce(t); + + helpline(t, gcursor); + + return 1; +} + +/* ************************** SHEAR *************************** */ + +void initShear(TransInfo *t) +{ + t->mode = TFM_SHEAR; + t->transform = Shear; + t->handleEvent = handleEventShear; + + 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) + { + // Use customData pointer to signal Shear direction + if (t->customData == 0) + t->customData = (void*)1; + else + 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); + + // Custom data signals shear direction + if (t->customData == 0) + value = 0.05f * InputHorizontalAbsolute(t, mval); + else + value = 0.05f * InputVerticalAbsolute(t, mval); + + 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 (G.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); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + +/* ************************** RESIZE *************************** */ + +void initResize(TransInfo *t) +{ + t->mode = TFM_RESIZE; + t->transform = Resize; + + t->flag |= T_NULL_ONE; + t->num.flag |= NUM_NULL_ONE; + t->num.flag |= NUM_AFFECT_ALL; + if (!G.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; + + t->fac = (float)sqrt( + ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +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 && (G.scene->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 + /* handle ipokeys? */ + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + /* calculate delta size (equal for size and dsize) */ + + vec[0]= (tdi->oldsize[0])*(fsize[0] -1.0f) * td->factor; + vec[1]= (tdi->oldsize[1])*(fsize[1] -1.0f) * td->factor; + vec[2]= (tdi->oldsize[2])*(fsize[2] -1.0f) * td->factor; + + add_tdi_poin(tdi->sizex, tdi->oldsize, vec[0]); + add_tdi_poin(tdi->sizey, tdi->oldsize+1, vec[1]); + add_tdi_poin(tdi->sizez, tdi->oldsize+2, vec[2]); + + } + else 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); + + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + add_tdi_poin(tdi->locx, tdi->oldloc, vec[0]); + add_tdi_poin(tdi->locy, tdi->oldloc+1, vec[1]); + add_tdi_poin(tdi->locz, tdi->oldloc+2, vec[2]); + } + else 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 = InputScaleRatio(t, mval); + + /* flip scale, but not for manipulator center handle */ + if ((t->center2d[0] - mval[0]) * (t->center2d[0] - t->imval[0]) + + (t->center2d[1] - mval[1]) * (t->center2d[1] - t->imval[1]) < 0) + ratio *= -1.0f; + } + + size[0] = size[1] = size[2] = ratio; + + snapGrid(t, size); + + if (hasNumInput(&t->num)) { + applyNumInput(&t->num, size); + constraintNumInput(t, size); + } + + applySnapping(t, 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); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + +/* ************************** TOSPHERE *************************** */ + +void initToSphere(TransInfo *t) +{ + TransData *td = t->data; + int i; + + t->mode = TFM_TOSPHERE; + t->transform = ToSphere; + + 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 = InputHorizontalRatio(t, mval); + + 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); + + viewRedrawForce(t); + + return 1; +} + +/* ************************** ROTATION *************************** */ + + +void initRotation(TransInfo *t) +{ + t->mode = TFM_ROTATION; + t->transform = Rotation; + + 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->fac = 0; + + 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 && (G.scene->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 + 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); + + 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); + + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + add_tdi_poin(tdi->locx, tdi->oldloc, vec[0]); + add_tdi_poin(tdi->locy, tdi->oldloc+1, vec[1]); + add_tdi_poin(tdi->locz, tdi->oldloc+2, vec[2]); + } + else VecAddf(td->loc, td->iloc, vec); + } + + + constraintTransLim(t, td); + + /* rotation */ + if ((t->flag & T_V3D_ALIGN)==0) { // align mode doesn't rotate objects itself + if(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 { + float obmat[3][3]; + + /* are there ipo keys? */ + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + float current_rot[3]; + float rot[3]; + + /* current IPO value for compatible euler */ + current_rot[0] = (tdi->rotx) ? tdi->rotx[0] : 0.0f; + current_rot[1] = (tdi->roty) ? tdi->roty[0] : 0.0f; + current_rot[2] = (tdi->rotz) ? tdi->rotz[0] : 0.0f; + VecMulf(current_rot, (float)(M_PI_2 / 9.0)); + + /* calculate the total rotatation in eulers */ + VecAddf(eul, td->ext->irot, td->ext->drot); + EulToMat3(eul, obmat); + /* mat = transform, obmat = object rotation */ + Mat3MulMat3(fmat, mat, obmat); + + Mat3ToCompatibleEul(fmat, eul, current_rot); + + /* correct back for delta rot */ + if(tdi->flag & TOB_IPODROT) { + VecSubf(rot, eul, td->ext->irot); + } + else { + VecSubf(rot, eul, td->ext->drot); + } + + VecMulf(rot, (float)(9.0/M_PI_2)); + VecSubf(rot, rot, tdi->oldrot); + + protectedRotateBits(td->protectflag, rot, tdi->oldrot); + + add_tdi_poin(tdi->rotx, tdi->oldrot, rot[0]); + add_tdi_poin(tdi->roty, tdi->oldrot+1, rot[1]); + add_tdi_poin(tdi->rotz, tdi->oldrot+2, rot[2]); + } + else { + 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 */ + EulToMat3(eul, obmat); + /* mat = transform, obmat = object rotation */ + Mat3MulMat3(fmat, smat, obmat); + + Mat3ToCompatibleEul(fmat, eul, td->ext->rot); + + /* 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); + + t->fac += InputDeltaAngle(t, mval); + + final = t->fac; + + 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); + + t->val = final; // used in manipulator + Mat3CpyMat3(t->mat, mat); // used in manipulator + + applyRotation(t, final, axis); + + recalcData(t); + + ED_area_headerprint(t->sa, str); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + + +/* ************************** TRACKBALL *************************** */ + +void initTrackball(TransInfo *t) +{ + t->mode = TFM_TRACKBALL; + t->transform = 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->fac = 0; + + 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); + + /* factore has to become setting or so */ + phi[0]= 0.01f*(float)( t->imval[1] - mval[1] ); + phi[1]= 0.01f*(float)( mval[0] - t->imval[0] ); + + 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); + + if(t->flag & T_SHIFT_MOD) { + if(phi[0] != 0.0) phi[0]/= 5.0f; + if(phi[1] != 0.0) phi[1]/= 5.0f; + } + } + + VecRotToMat3(axis1, phi[0], smat); + VecRotToMat3(axis2, phi[1], totmat); + + Mat3MulMat3(mat, smat, totmat); + + Mat3CpyMat3(t->mat, mat); // used in manipulator + + applyTrackball(t, axis1, axis2, phi); + + recalcData(t); + + ED_area_headerprint(t->sa, str); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + +/* ************************** TRANSLATION *************************** */ + +void initTranslation(TransInfo *t) +{ + t->mode = TFM_TRANSLATION; + t->transform = Translation; + + t->idx_max = (t->flag & T_2D_EDIT)? 1: 2; + t->num.flag = 0; + t->num.idx_max = t->idx_max; + + t->ndof.axis = 7; + + if(t->spacetype == SPACE_VIEW3D) { + View3D *v3d = t->view; + + /* initgrabz() defines a factor for perspective depth correction, used in window_to_3d() */ + if(t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + float vec[3]; + + VECCOPY(vec, t->center); + Mat4MulVecfl(ob->obmat, vec); + initgrabz(v3d, vec[0], vec[1], vec[2]); + } + else { + initgrabz(v3d, t->center[0], t->center[1], t->center[2]); + } + + 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 dvec[3]; + float dist; + + convertVecToDisplayNum(vec, dvec); + + if (hasNumInput(&t->num)) { + outputNumInput(&(t->num), tvec); + dist = VecLength(t->num.val); + } + else { + dist = VecLength(vec); + sprintf(&tvec[0], "%.4f", dvec[0]); + sprintf(&tvec[20], "%.4f", dvec[1]); + sprintf(&tvec[40], "%.4f", dvec[2]); + } + + 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= G.scene->toolsettings->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); + + /* transdata ipokey */ + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + add_tdi_poin(tdi->locx, tdi->oldloc, tvec[0]); + add_tdi_poin(tdi->locy, tdi->oldloc+1, tvec[1]); + add_tdi_poin(tdi->locz, tdi->oldloc+2, tvec[2]); + } + else 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->flag & T_SHIFT_MOD) { + float dvec[3]; + /* calculate the main translation and the precise one separate */ + convertViewVec(t, dvec, (short)(mval[0] - t->shiftmval[0]), (short)(mval[1] - t->shiftmval[1])); + VecMulf(dvec, 0.1f); + convertViewVec(t, t->vec, (short)(t->shiftmval[0] - t->imval[0]), (short)(t->shiftmval[1] - t->imval[1])); + VecAddf(t->vec, t->vec, dvec); + } + else convertViewVec(t, t->vec, (short)(mval[0] - t->imval[0]), (short)(mval[1] - t->imval[1])); + + if (t->con.mode & CON_APPLY) { + float pvec[3] = {0.0f, 0.0f, 0.0f}; + applySnapping(t, t->vec); + t->con.applyVec(t, NULL, t->vec, tvec, pvec); + VECCOPY(t->vec, tvec); + headerTranslation(t, pvec, str); + } + else { + applyNDofInput(&t->ndof, t->vec); + snapGrid(t, t->vec); + applyNumInput(&t->num, t->vec); + applySnapping(t, t->vec); + headerTranslation(t, t->vec, str); + } + + applyTranslation(t, t->vec); + + /* evil hack - redo translation if cliiping needeed */ + if (t->flag & T_CLIP_UV && clipUVTransform(t, t->vec, 0)) + applyTranslation(t, t->vec); + + recalcData(t); + + ED_area_headerprint(t->sa, str); + + viewRedrawForce(t); + + drawSnapping(t); + + return 1; +} + +/* ************************** SHRINK/FATTEN *************************** */ + +void initShrinkFatten(TransInfo *t) +{ + // If not in mesh edit mode, fallback to Resize + if (G.obedit==NULL || G.obedit->type != OB_MESH) { + initResize(t); + } + else { + t->mode = TFM_SHRINKFATTEN; + t->transform = ShrinkFatten; + + 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 = -InputVerticalAbsolute(t, mval); + + 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); + + viewRedrawForce(t); + + return 1; +} + +/* ************************** TILT *************************** */ + +void initTilt(TransInfo *t) +{ + t->mode = TFM_TILT; + t->transform = Tilt; + + 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->fac = 0; + + t->flag |= T_NO_CONSTRAINT; +} + + + +int Tilt(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + int i; + char str[50]; + + float final; + + t->fac += InputDeltaAngle(t, mval); + + final = t->fac; + + 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); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + + +/* ******************** Curve Shrink/Fatten *************** */ + +int CurveShrinkFatten(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float ratio; + int i; + char str[50]; + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + ratio+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -ratio); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + 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); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + +void initCurveShrinkFatten(TransInfo *t) +{ + t->mode = TFM_CURVE_SHRINKFATTEN; + t->transform = CurveShrinkFatten; + + 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; + + t->fac = (float)sqrt( ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); +} + +/* ************************** PUSH/PULL *************************** */ + +void initPushPull(TransInfo *t) +{ + t->mode = TFM_PUSHPULL; + t->transform = PushPull; + + 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 = InputVerticalAbsolute(t, mval); + + 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); + + viewRedrawForce(t); + + return 1; +} + +/* ************************** BEVEL **************************** */ + +void initBevel(TransInfo *t) +{ + t->mode = TFM_BEVEL; + t->flag |= T_NO_CONSTRAINT; + t->num.flag |= NUM_NO_NEGATIVE; + t->transform = Bevel; + t->handleEvent = handleEventBevel; + + 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) { + 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 = InputHorizontalAbsolute(t, mval)/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); + + viewRedrawForce(t); + + return 1; +} + +/* ************************** BEVEL WEIGHT *************************** */ + +void initBevelWeight(TransInfo *t) +{ + t->mode = TFM_BWEIGHT; + t->transform = BevelWeight; + + 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; + + t->fac = (float)sqrt( + ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +int BevelWeight(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float weight; + int i; + char str[50]; + + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + weight = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + weight+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -weight); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + weight = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + 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); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + +/* ************************** CREASE *************************** */ + +void initCrease(TransInfo *t) +{ + t->mode = TFM_CREASE; + t->transform = Crease; + + 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; + + t->fac = (float)sqrt( + ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +int Crease(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float crease; + int i; + char str[50]; + + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + crease = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + crease+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -crease); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + crease = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + 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); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + +/* ******************** EditBone (B-bone) width scaling *************** */ + +void initBoneSize(TransInfo *t) +{ + t->mode = TFM_BONESIZE; + t->transform = BoneSize; + + 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; + + t->fac = (float)sqrt( ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +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]; + + /* 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 { + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + ratio+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -ratio); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + /* flip scale, but not for manipulator center handle */ + if ((t->center2d[0] - mval[0]) * (t->center2d[0] - t->imval[0]) + + (t->center2d[1] - mval[1]) * (t->center2d[1] - t->imval[1]) < 0) + ratio *= -1.0f; + } + + 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); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + + +/* ******************** EditBone envelope *************** */ + +void initBoneEnvelope(TransInfo *t) +{ + t->mode = TFM_BONE_ENVELOPE; + t->transform = BoneEnvelope; + + 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; + + t->fac = (float)sqrt( ( + ((float)(t->center2d[1] - t->imval[1]))*((float)(t->center2d[1] - t->imval[1])) + + + ((float)(t->center2d[0] - t->imval[0]))*((float)(t->center2d[0] - t->imval[0])) + ) ); + + if(t->fac==0.0f) t->fac= 1.0f; // prevent Inf +} + +int BoneEnvelope(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float ratio; + int i; + char str[50]; + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + float dx= (float)(t->center2d[0] - t->shiftmval[0]); + float dy= (float)(t->center2d[1] - t->shiftmval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + + dx= (float)(t->center2d[0] - mval[0]); + dy= (float)(t->center2d[1] - mval[1]); + ratio+= 0.1f*(float)(sqrt( dx*dx + dy*dy)/t->fac -ratio); + + } + else { + float dx= (float)(t->center2d[0] - mval[0]); + float dy= (float)(t->center2d[1] - mval[1]); + ratio = (float)sqrt( dx*dx + dy*dy)/t->fac; + } + + 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); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + + +/* ******************** EditBone roll *************** */ + +void initBoneRoll(TransInfo *t) +{ + t->mode = TFM_BONE_ROLL; + t->transform = BoneRoll; + + 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->fac = 0.0f; + + t->flag |= T_NO_CONSTRAINT; +} + +int BoneRoll(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + int i; + char str[50]; + + float final; + + t->fac += InputDeltaAngle(t, mval); + + final = t->fac; + + 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); + + viewRedrawForce(t); + + if(!(t->flag & T_USES_MANIPULATOR)) helpline (t, t->center); + + return 1; +} + +/* ************************** BAKE TIME ******************* */ + +void initBakeTime(TransInfo *t) +{ + 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->transform = BakeTime; + t->fac = 0.1f; +} + +int BakeTime(TransInfo *t, short mval[2]) +{ + TransData *td = t->data; + float time; + int i; + char str[50]; + + + if(t->flag & T_SHIFT_MOD) { + /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ + time= (float)(t->center2d[0] - t->shiftmval[0])*t->fac; + time+= 0.1f*((float)(t->center2d[0]*t->fac - mval[0]) -time); + } + else { + time = (float)(t->center2d[0] - mval[0])*t->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); + + viewRedrawForce(t); + + helpline (t, t->center); + + return 1; +} + +/* ************************** MIRROR *************************** */ + +void initMirror(TransInfo *t) +{ + t->flag |= T_NULL_ONE; + if (!G.obedit) { + t->flag |= T_NO_ZERO; + } + + t->transform = Mirror; +} + +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); + + viewRedrawForce(t); + } + 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); + + ED_area_headerprint(t->sa, "Select a mirror axis (X, Y, Z)"); + + viewRedrawForce(t); + } + + return 1; +} + +/* ************************** ALIGN *************************** */ + +void initAlign(TransInfo *t) +{ + t->flag |= T_NO_CONSTRAINT; + + t->transform = Align; +} + +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(G.scene->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. + */ +static short getAnimEdit_SnapMode(TransInfo *t) +{ + short autosnap= SACTSNAP_OFF; +#if 0 // TRANSFORM_FIX_ME + /* currently, some of these are only for the action editor */ + if (t->spacetype == SPACE_ACTION && G.saction) { + switch (G.saction->autosnap) { + case SACTSNAP_OFF: + if (G.qual == LR_CTRLKEY) + autosnap= SACTSNAP_STEP; + else if (G.qual == LR_SHIFTKEY) + autosnap= SACTSNAP_FRAME; + else if (G.qual == LR_ALTKEY) + autosnap= SACTSNAP_MARKER; + else + autosnap= SACTSNAP_OFF; + break; + case SACTSNAP_STEP: + autosnap= (G.qual==LR_CTRLKEY)? SACTSNAP_OFF: SACTSNAP_STEP; + break; + case SACTSNAP_FRAME: + autosnap= (G.qual==LR_SHIFTKEY)? SACTSNAP_OFF: SACTSNAP_FRAME; + break; + case SACTSNAP_MARKER: + autosnap= (G.qual==LR_ALTKEY)? SACTSNAP_OFF: SACTSNAP_MARKER; + break; + } + } + else if (t->spacetype == SPACE_NLA && G.snla) { + switch (G.snla->autosnap) { + case SACTSNAP_OFF: + if (G.qual == LR_CTRLKEY) + autosnap= SACTSNAP_STEP; + else if (G.qual == LR_SHIFTKEY) + autosnap= SACTSNAP_FRAME; + else if (G.qual == LR_ALTKEY) + autosnap= SACTSNAP_MARKER; + else + autosnap= SACTSNAP_OFF; + break; + case SACTSNAP_STEP: + autosnap= (G.qual==LR_CTRLKEY)? SACTSNAP_OFF: SACTSNAP_STEP; + break; + case SACTSNAP_FRAME: + autosnap= (G.qual==LR_SHIFTKEY)? SACTSNAP_OFF: SACTSNAP_FRAME; + break; + case SACTSNAP_MARKER: + autosnap= (G.qual==LR_ALTKEY)? SACTSNAP_OFF: SACTSNAP_MARKER; + break; + } + } + else { + if (G.qual == LR_CTRLKEY) + autosnap= SACTSNAP_STEP; + else if (G.qual == LR_SHIFTKEY) + autosnap= SACTSNAP_FRAME; + else if (G.qual == LR_ALTKEY) + autosnap= SACTSNAP_MARKER; + else + autosnap= SACTSNAP_OFF; + } +#endif + 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) +{ +#if 0 // TRANSFORM_FIX_ME + short drawtime; + + /* currently, some of these are only for the action editor */ + if (t->spacetype == SPACE_ACTION && G.saction) { + drawtime = (G.saction->flag & SACTION_DRAWTIME)? 1 : 0; + } + else if (t->spacetype == SPACE_NLA && G.snla) { + drawtime = (G.snla->flag & SNLA_DRAWTIME)? 1 : 0; + } + else { + drawtime = 0; + } + + return drawtime; +#endif +return 0; +} + + +/* 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, Object *ob, short autosnap) +{ +#if 0 // TRANSFORM_FIX_ME + /* snap key to nearest frame? */ + if (autosnap == SACTSNAP_FRAME) { + short doTime= getAnimEdit_DrawTime(t); + double secf= FPS; + double val; + + /* convert frame to nla-action time (if needed) */ + if (ob) + val= get_action_frame_inv(ob, *(td->val)); + 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 (ob) + *(td->val)= get_action_frame(ob, val); + 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 (ob) + val= get_action_frame_inv(ob, *(td->val)); + else + val= *(td->val); + + /* snap to nearest marker */ + val= (float)find_nearest_marker_time(val); + + /* convert frame out of nla-action time */ + if (ob) + *(td->val)= get_action_frame(ob, val); + else + *(td->val)= val; + } +#endif +} + +/* ----------------- Translation ----------------------- */ + +void initTimeTranslate(TransInfo *t) +{ + t->mode = TFM_TIME_TRANSLATE; + t->transform = TimeTranslate; + + /* 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 { + Scene *scene = t->scene; + short autosnap= getAnimEdit_SnapMode(t); + short doTime = getAnimEdit_DrawTime(t); + double secf= FPS; + float val= t->fac; + + /* 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; + + short doTime= getAnimEdit_DrawTime(t); + double secf= FPS; + + 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->ob is a pointer to the object, + * whose active action is where this keyframe comes from + */ + Object *ob= td->ob; + + /* check if any need to apply nla-scaling */ + if (ob) { + deltax = t->fac; + + if (autosnap == SACTSNAP_STEP) { + if (doTime) + deltax= (float)( floor((deltax/secf) + 0.5f) * secf ); + else + deltax= (float)( floor(deltax + 0.5f) ); + } + + val = get_action_frame_inv(ob, td->ival); + val += deltax; + *(td->val) = get_action_frame(ob, val); + } + else { + deltax = val = t->fac; + + 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, ob, autosnap); + } +} + +int TimeTranslate(TransInfo *t, short mval[2]) +{ + View2D *v2d = t->view; + float cval[2], sval[2]; + char str[200]; + +#if 0 // TRANSFORM_FIX_ME + /* calculate translation amount from mouse movement - in 'time-grid space' */ + areamouseco_to_ipoco(v2d, mval, &cval[0], &cval[1]); + areamouseco_to_ipoco(v2d, t->imval, &sval[0], &sval[1]); + + /* we only need to calculate effect for time (applyTimeTranslate only needs that) */ + t->fac= cval[0] - sval[0]; + + /* handle numeric-input stuff */ + t->vec[0] = t->fac; + applyNumInput(&t->num, &t->vec[0]); + t->fac = t->vec[0]; + headerTimeTranslate(t, str); + + applyTimeTranslate(t, sval[0]); + + recalcData(t); + + ED_area_headerprint(t->sa, str); + + viewRedrawForce(t); +#endif + + return 1; +} + +/* ----------------- Time Slide ----------------------- */ + +void initTimeSlide(TransInfo *t) +{ + /* this tool is only really available in the Action Editor... */ + if (t->spacetype == SPACE_ACTION) { + /* set flag for drawing stuff*/ + // TRANSFORM_FIX_ME + //G.saction->flag |= SACTION_MOVING; + } + + t->mode = TFM_TIME_SLIDE; + t->transform = TimeSlide; + t->flag |= T_FREE_CUSTOMDATA; + + /* 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->fac; + float val; + + val= 2.0*(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) { + float cvalf = t->fac; + +#if 0 // TRANSFORM_FIX_ME + if (NLA_ACTION_SCALED) + cvalf= get_action_frame(OBACT, cvalf); + + G.saction->timeslide= cvalf; +#endif + } + + /* 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->ob is a pointer to the object, + * whose active action is where this keyframe comes from + */ + Object *ob= td->ob; + float cval = t->fac; + + /* apply scaling to necessary values */ + if (ob) + cval= get_action_frame(ob, cval); + + /* 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 = 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 */ +#if 0 // TRANSFORM_FIX_ME + areamouseco_to_ipoco(v2d, mval, &cval[0], &cval[1]); + areamouseco_to_ipoco(v2d, t->imval, &sval[0], &sval[1]); +#endif + + /* t->fac stores cval[0], which is the current mouse-pointer location (in frames) */ + t->fac= cval[0]; + + /* handle numeric-input stuff */ + t->vec[0] = 2.0*(cval[0]-sval[0]) / (maxx-minx); + applyNumInput(&t->num, &t->vec[0]); + t->fac = (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); + + viewRedrawForce(t); + + return 1; +} + +/* ----------------- Scaling ----------------------- */ + +void initTimeScale(TransInfo *t) +{ + t->mode = TFM_TIME_SCALE; + t->transform = TimeScale; + + 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->fac); + + sprintf(str, "ScaleX: %s", &tvec[0]); +} + +static void applyTimeScale(TransInfo *t) { + Scene *scene = t->scene; + TransData *td = t->data; + int i; + + short autosnap= getAnimEdit_SnapMode(t); + short doTime= getAnimEdit_DrawTime(t); + double secf= FPS; + + + for (i = 0 ; i < t->total; i++, td++) { + /* it is assumed that td->ob is a pointer to the object, + * whose active action is where this keyframe comes from + */ + Object *ob= td->ob; + float startx= CFRA; + float fac= t->fac; + + 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-scaling */ + if (ob) + startx= get_action_frame(ob, startx); + + /* now, calculate the new value */ + *(td->val) = td->ival - startx; + *(td->val) *= fac; + *(td->val) += startx; + + /* apply nearest snapping */ + doAnimEdit_SnapFrame(t, td, ob, 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]; + +#if 0 // TRANSFORM_FIX_ME + switch (t->spacetype) { + case SPACE_ACTION: + width= ACTWIDTH; + break; + case SPACE_NLA: + width= NLAWIDTH; + break; + } +#endif + + /* calculate scaling factor */ + startx= sval-(width/2+(t->ar->winx)/2); + deltax= cval-(width/2+(t->ar->winx)/2); + t->fac = deltax / startx; + + /* handle numeric-input stuff */ + t->vec[0] = t->fac; + applyNumInput(&t->num, &t->vec[0]); + t->fac = t->vec[0]; + headerTimeScale(t, str); + + applyTimeScale(t); + + recalcData(t); + + ED_area_headerprint(t->sa, str); + + viewRedrawForce(t); + + 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 +} diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h new file mode 100644 index 00000000000..b8e439f5d10 --- /dev/null +++ b/source/blender/editors/transform/transform.h @@ -0,0 +1,569 @@ +/** + * $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 ***** + */ + +#ifndef TRANSFORM_H +#define TRANSFORM_H + +#include "BIF_transform.h" + +/* ************************** Types ***************************** */ + +struct TransInfo; +struct TransData; +struct TransSnap; +struct NumInput; +struct Object; +struct View3D; +struct ScrArea; +struct Scene; +struct bPose; +struct bConstraint; +struct BezTriple; +struct wmOperatorType; +struct bContext; +struct wmEvent; +struct ARegion; + +typedef struct NDofInput { + int flag; + int axis; + float fval[7]; + float factor[3]; +} NDofInput; + +typedef struct NumInput { + short idx; + short idx_max; + short flag; /* Different flags to indicate different behaviors */ + float val[3]; /* Direct value of the input */ + int ctrl[3]; /* Control to indicate what to do with the numbers that are typed */ +} NumInput ; + +/* + The ctrl value has different meaning: + 0 : No value has been typed + + otherwise, |value| - 1 is where the cursor is located after the period + Positive : number is positive + Negative : number is negative +*/ + +typedef struct TransSnap { + short modePoint; + short modeTarget; + int status; + float snapPoint[3]; + float snapTarget[3]; + float snapNormal[3]; + float snapTangent[3]; + float dist; // Distance from snapPoint to snapTarget + double last; + void (*applySnap)(struct TransInfo *, float *); + void (*calcSnap)(struct TransInfo *, float *); + void (*targetSnap)(struct TransInfo *); + float (*distance)(struct TransInfo *, float p1[3], float p2[3]); // Get the transform distance between two points (used by Closest snap) +} TransSnap; + +typedef struct TransCon { + char text[50]; /* Description of the Constraint for header_print */ + float mtx[3][3]; /* Matrix of the Constraint space */ + float imtx[3][3]; /* Inverse Matrix of the Constraint space */ + float pmtx[3][3]; /* Projection Constraint Matrix (same as imtx with some axis == 0) */ + float center[3]; /* transformation center to define where to draw the view widget + ALWAYS in global space. Unlike the transformation center */ + short imval[2]; /* initial mouse value for visual calculation */ + /* the one in TransInfo is not garanty to stay the same (Rotates change it) */ + int mode; /* Mode flags of the Constraint */ + void (*drawExtra)(struct TransInfo *); + /* For constraints that needs to draw differently from the other + uses this instead of the generic draw function */ + void (*applyVec)(struct TransInfo *, struct TransData *, float *, float *, float *); + /* Apply function pointer for linear vectorial transformation */ + /* The last three parameters are pointers to the in/out/printable vectors */ + void (*applySize)(struct TransInfo *, struct TransData *, float [3][3]); + /* Apply function pointer for size transformation */ + void (*applyRot)(struct TransInfo *, struct TransData *, float [3], float *); + /* Apply function pointer for rotation transformation */ +} TransCon; + +typedef struct TransDataIpokey { + int flag; /* which keys */ + float *locx, *locy, *locz; /* channel pointers */ + float *rotx, *roty, *rotz; + float *quatx, *quaty, *quatz, *quatw; + float *sizex, *sizey, *sizez; + float oldloc[9]; /* storage old values */ + float oldrot[9]; + float oldsize[9]; + float oldquat[12]; +} TransDataIpokey; + +typedef struct TransDataExtension { + float drot[3]; /* Initial object drot */ + float dsize[3]; /* Initial object dsize */ + float *rot; /* Rotation of the data to transform (Faculative) */ + float irot[3]; /* Initial rotation */ + float *quat; /* Rotation quaternion of the data to transform (Faculative) */ + float iquat[4]; /* Initial rotation quaternion */ + float *size; /* Size of the data to transform (Faculative) */ + float isize[3]; /* Initial size */ + float obmat[4][4]; /* Object matrix */ +} TransDataExtension; + +typedef struct TransData2D { + float loc[3]; /* Location of data used to transform (x,y,0) */ + float *loc2d; /* Pointer to real 2d location of data */ +} TransData2D; + +/* we need to store 2 handles for each transdata incase the other handle wasnt selected */ +typedef struct TransDataCurveHandleFlags { + char ih1, ih2; + char *h1, *h2; +} TransDataCurveHandleFlags; + + +typedef struct TransData { + float dist; /* Distance needed to affect element (for Proportionnal Editing) */ + float rdist; /* Distance to the nearest element (for Proportionnal Editing) */ + float factor; /* Factor of the transformation (for Proportionnal Editing) */ + float *loc; /* Location of the data to transform */ + float iloc[3]; /* Initial location */ + float *val; /* Value pointer for special transforms */ + float ival; /* Old value*/ + float center[3]; /* Individual data center */ + float mtx[3][3]; /* Transformation matrix from data space to global space */ + float smtx[3][3]; /* Transformation matrix from global space to data space */ + float axismtx[3][3];/* Axis orientation matrix of the data */ + struct Object *ob; + struct bConstraint *con; /* for objects/bones, the first constraint in its constraint stack */ + TransDataExtension *ext; /* for objects, poses. 1 single malloc per TransInfo! */ + TransDataIpokey *tdi; /* for objects, ipo keys. per transdata a malloc */ + TransDataCurveHandleFlags *hdata; /* for curves, stores handle flags for modification/cancel */ + void *extra; /* extra data (mirrored element pointer, in editmode mesh to EditVert) (editbone for roll fixing) (...) */ + short flag; /* Various flags */ + short protectflag; /* If set, copy of Object or PoseChannel protection */ +/*#ifdef WITH_VERSE*/ + void *verse; /* pointer at verse data struct (VerseVert, etc.) */ +/*#endif*/ +} TransData; + +typedef struct TransInfo { + int mode; /* current mode */ + int flag; /* generic flags for special behaviors */ + short state; /* current state (running, canceled,...)*/ + int context; /* current context */ + float val; /* init value for some transformations (and rotation angle) */ + float fac; /* factor for distance based transform */ + int (*transform)(struct TransInfo *, short *); + /* transform function pointer */ + int (*handleEvent)(struct TransInfo *, struct wmEvent *event); + /* event handler function pointer RETURN 1 if redraw is needed */ + int total; /* total number of transformed data */ + TransData *data; /* transformed data (array) */ + TransDataExtension *ext; /* transformed data extension (array) */ + TransData2D *data2d; /* transformed data for 2d (array) */ + TransCon con; /* transformed constraint */ + TransSnap tsnap; + NumInput num; /* numerical input */ + NDofInput ndof; /* ndof input */ + char redraw; /* redraw flag */ + float propsize; /* proportional circle radius */ + char proptext[20]; /* proportional falloff text */ + float center[3]; /* center of transformation */ + int center2d[2]; /* center in screen coordinates */ + short imval[2]; /* initial mouse position */ + short shiftmval[2]; /* mouse position when shift was pressed */ + short idx_max; /* maximum index on the input vector */ + float snap[3]; /* Snapping Gears */ + + float viewmat[4][4]; /* copy from G.vd, prevents feedback, */ + float viewinv[4][4]; /* and to make sure we don't have to */ + float persmat[4][4]; /* access G.vd from other space types */ + float persinv[4][4]; + short persp; + short around; + char spacetype; /* spacetype where transforming is */ + + float vec[3]; /* translation, to show for widget */ + float mat[3][3]; /* rot/rescale, to show for widget */ + + char *undostr; /* if set, uses this string for undo */ + float spacemtx[3][3]; /* orientation matrix of the current space */ + char spacename[32]; /* name of the current space */ + + struct Object *poseobj; /* if t->flag & T_POSE, this denotes pose object */ + + void *customData; /* Per Transform custom data */ + + /*************** NEW STUFF *********************/ + + float values[4]; + void *view; + struct ScrArea *sa; + struct ARegion *ar; + struct Scene *scene; + struct wmEvent *event; /* last event, reset at the start of each transformApply and nulled at the transformEnd */ + short mval[2]; /* current mouse position */ +} TransInfo; + + +/* ******************** Macros & Prototypes *********************** */ + +/* NUMINPUT FLAGS */ +#define NUM_NULL_ONE 2 +#define NUM_NO_NEGATIVE 4 +#define NUM_NO_ZERO 8 +#define NUM_NO_FRACTION 16 +#define NUM_AFFECT_ALL 32 + +/* NDOFINPUT FLAGS */ +#define NDOF_INIT 1 + +/* transinfo->state */ +#define TRANS_RUNNING 0 +#define TRANS_CONFIRM 1 +#define TRANS_CANCEL 2 + +/* transinfo->flag */ +#define T_OBJECT (1 << 0) +#define T_EDIT (1 << 1) +#define T_POSE (1 << 2) +#define T_TEXTURE (1 << 3) +#define T_CAMERA (1 << 4) + // when shift pressed, higher resolution transform. cannot rely on G.qual, need event! +#define T_SHIFT_MOD (1 << 5) + // trans on points, having no rotation/scale +#define T_POINTS (1 << 6) + // for manipulator exceptions, like scaling using center point, drawing help lines +#define T_USES_MANIPULATOR (1 << 7) + + /* restrictions flags */ +#define T_ALL_RESTRICTIONS ((1 << 8)|(1 << 9)|(1 << 10)) +#define T_NO_CONSTRAINT (1 << 8) +#define T_NULL_ONE (1 << 9) +#define T_NO_ZERO (1 << 10) + +#define T_PROP_EDIT (1 << 11) +#define T_PROP_CONNECTED (1 << 12) + + /* if MMB is pressed or not */ +#define T_MMB_PRESSED (1 << 13) + +#define T_V3D_ALIGN (1 << 14) + /* for 2d views like uv or ipo */ +#define T_2D_EDIT (1 << 15) +#define T_CLIP_UV (1 << 16) + +#define T_FREE_CUSTOMDATA (1 << 17) + /* auto-ik is on */ +#define T_AUTOIK (1 << 18) + +/* ******************************************************************************** */ + +/* transinfo->con->mode */ +#define CON_APPLY 1 +#define CON_AXIS0 2 +#define CON_AXIS1 4 +#define CON_AXIS2 8 +#define CON_SELECT 16 +#define CON_NOFLIP 32 /* does not reorient vector to face viewport when on */ +#define CON_LOCAL 64 +#define CON_USER 128 + +/* transdata->flag */ +#define TD_SELECTED 1 +#define TD_ACTIVE (1 << 1) +#define TD_NOACTION (1 << 2) +#define TD_USEQUAT (1 << 3) +#define TD_NOTCONNECTED (1 << 4) +#define TD_SINGLESIZE (1 << 5) /* used for scaling of MetaElem->rad */ +#ifdef WITH_VERSE + #define TD_VERSE_OBJECT (1 << 6) + #define TD_VERSE_VERT (1 << 7) +#endif +#define TD_TIMEONLY (1 << 8) +#define TD_NOCENTER (1 << 9) +#define TD_NO_EXT (1 << 10) /* ext abused for particle key timing */ +#define TD_SKIP (1 << 11) /* don't transform this data */ +#define TD_BEZTRIPLE (1 << 12) /* if this is a bez triple, we need to restore the handles, if this is set transdata->misc.hdata needs freeing */ +#define TD_NO_LOC (1 << 13) /* when this is set, don't apply translation changes to this element */ + +/* transsnap->status */ +#define SNAP_ON 1 +#define TARGET_INIT 2 +#define POINT_INIT 4 + +/* transsnap->modePoint */ +#define SNAP_GRID 0 +#define SNAP_GEO 1 + +/* transsnap->modeTarget */ +#define SNAP_CLOSEST 0 +#define SNAP_CENTER 1 +#define SNAP_MEDIAN 2 +#define SNAP_ACTIVE 3 + + +void TFM_OT_transform(struct wmOperatorType *ot); + +void initTransform(struct bContext *C, struct TransInfo *t, int mode, int context, struct wmEvent *event); +void transformEvent(TransInfo *t, struct wmEvent *event); +void transformApply(TransInfo *t); +int transformEnd(TransInfo *t); + +void setTransformViewMatrices(TransInfo *t); +void convertViewVec(TransInfo *t, float *vec, short dx, short dy); +void projectIntView(TransInfo *t, float *vec, int *adr); +void projectFloatView(TransInfo *t, float *vec, float *adr); + +void convertVecToDisplayNum(float *vec, float *num); +void convertDisplayNumToVec(float *num, float *vec); + +void initWarp(TransInfo *t); +int handleEventWarp(TransInfo *t, struct wmEvent *event); +int Warp(TransInfo *t, short mval[2]); + +void initShear(TransInfo *t); +int handleEventShear(TransInfo *t, struct wmEvent *event); +int Shear(TransInfo *t, short mval[2]); + +void initResize(TransInfo *t); +int Resize(TransInfo *t, short mval[2]); + +void initTranslation(TransInfo *t); +int Translation(TransInfo *t, short mval[2]); + +void initToSphere(TransInfo *t); +int ToSphere(TransInfo *t, short mval[2]); + +void initRotation(TransInfo *t); +int Rotation(TransInfo *t, short mval[2]); + +void initShrinkFatten(TransInfo *t); +int ShrinkFatten(TransInfo *t, short mval[2]); + +void initTilt(TransInfo *t); +int Tilt(TransInfo *t, short mval[2]); + +void initCurveShrinkFatten(TransInfo *t); +int CurveShrinkFatten(TransInfo *t, short mval[2]); + +void initTrackball(TransInfo *t); +int Trackball(TransInfo *t, short mval[2]); + +void initPushPull(TransInfo *t); +int PushPull(TransInfo *t, short mval[2]); + +void initBevel(TransInfo *t); +int handleEventBevel(TransInfo *t, struct wmEvent *event); +int Bevel(TransInfo *t, short mval[2]); + +void initBevelWeight(TransInfo *t); +int BevelWeight(TransInfo *t, short mval[2]); + +void initCrease(TransInfo *t); +int Crease(TransInfo *t, short mval[2]); + +void initBoneSize(TransInfo *t); +int BoneSize(TransInfo *t, short mval[2]); + +void initBoneEnvelope(TransInfo *t); +int BoneEnvelope(TransInfo *t, short mval[2]); + +void initBoneRoll(TransInfo *t); +int BoneRoll(TransInfo *t, short mval[2]); + +void initTimeTranslate(TransInfo *t); +int TimeTranslate(TransInfo *t, short mval[2]); + +void initTimeSlide(TransInfo *t); +int TimeSlide(TransInfo *t, short mval[2]); + +void initTimeScale(TransInfo *t); +int TimeScale(TransInfo *t, short mval[2]); + +void initBakeTime(TransInfo *t); +int BakeTime(TransInfo *t, short mval[2]); + +void initMirror(TransInfo *t); +int Mirror(TransInfo *t, short mval[2]); + +void initAlign(TransInfo *t); +int Align(TransInfo *t, short mval[2]); + +/*********************** transform_conversions.c ********** */ +struct ListBase; +void flushTransGPactionData(TransInfo *t); +void flushTransIpoData(TransInfo *t); +void flushTransUVs(TransInfo *t); +void flushTransParticles(TransInfo *t); +int clipUVTransform(TransInfo *t, float *vec, int resize); + +/*********************** exported from transform_manipulator.c ********** */ +void draw_manipulator_ext(struct ScrArea *sa, int type, char axis, int col, float vec[3], float mat[][3]); +int calc_manipulator_stats(struct ScrArea *sa); +float get_drawsize(struct View3D *v3d, struct ScrArea *sa, float *co); + +/*********************** TransData Creation and General Handling *********** */ +void createTransData(struct bContext *C, TransInfo *t); +void sort_trans_data_dist(TransInfo *t); +void add_tdi_poin(float *poin, float *old, float delta); +void special_aftertrans_update(TransInfo *t); + +void transform_autoik_update(TransInfo *t, short mode); + +/* auto-keying stuff used by special_aftertrans_update */ +short autokeyframe_cfra_can_key(struct Object *ob); +void autokeyframe_ob_cb_func(struct Object *ob, int tmode); +void autokeyframe_pose_cb_func(struct Object *ob, int tmode, short targetless_ik); + +/*********************** Constraints *****************************/ + +void getConstraintMatrix(TransInfo *t); +void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]); +void setLocalConstraint(TransInfo *t, int mode, const char text[]); +void setUserConstraint(TransInfo *t, int mode, const char text[]); + +void constraintNumInput(TransInfo *t, float vec[3]); + +void getConstraintMatrix(TransInfo *t); +int isLockConstraint(TransInfo *t); +int getConstraintSpaceDimension(TransInfo *t); +char constraintModeToChar(TransInfo *t); + +void startConstraint(TransInfo *t); +void stopConstraint(TransInfo *t); + +void initSelectConstraint(TransInfo *t, float mtx[3][3]); +void selectConstraint(TransInfo *t); +void postSelectConstraint(TransInfo *t); + +void setNearestAxis(TransInfo *t); + +/*********************** Snapping ********************************/ + +typedef enum { + NO_GEARS = 0, + BIG_GEARS = 1, + SMALL_GEARS = 2 +} GearsType; + +void snapGrid(TransInfo *t, float *val); +void snapGridAction(TransInfo *t, float *val, GearsType action); + +void initSnapping(struct TransInfo *t); +void applySnapping(TransInfo *t, float *vec); +void resetSnapping(TransInfo *t); +int handleSnapping(TransInfo *t, struct wmEvent *event); +void drawSnapping(TransInfo *t); +int usingSnappingNormal(TransInfo *t); +int validSnappingNormal(TransInfo *t); + +/*********************** Generics ********************************/ + +void initTransInfo(struct bContext *C, TransInfo *t, struct wmEvent *event); +void postTrans (TransInfo *t); +void resetTransRestrictions(TransInfo *t); + +void drawLine(float *center, float *dir, char axis, short options); + +TransDataCurveHandleFlags *initTransDataCurveHandes(TransData *td, struct BezTriple *bezt); + +/* DRAWLINE options flags */ +#define DRAWLIGHT 1 +#define DRAWDASHED 2 +#define DRAWBOLD 4 + +void applyTransObjects(TransInfo *t); +void restoreTransObjects(TransInfo *t); +void recalcData(TransInfo *t); + +void calculateCenter(TransInfo *t); +void calculateCenter2D(TransInfo *t); +void calculateCenterBound(TransInfo *t); +void calculateCenterMedian(TransInfo *t); +void calculateCenterCursor(TransInfo *t); + +void calculateCenterCursor2D(TransInfo *t); +void calculatePropRatio(TransInfo *t); + +void getViewVector(TransInfo *t, float coord[3], float vec[3]); + +TransInfo * BIF_GetTransInfo(void); + +/*********************** NumInput ********************************/ + +void initNumInput(NumInput *n); +void outputNumInput(NumInput *n, char *str); +short hasNumInput(NumInput *n); +void applyNumInput(NumInput *n, float *vec); +char handleNumInput(NumInput *n, struct wmEvent *event); + +/*********************** NDofInput ********************************/ + +void initNDofInput(NDofInput *n); +int hasNDofInput(NDofInput *n); +void applyNDofInput(NDofInput *n, float *vec); +int handleNDofInput(NDofInput *n, struct wmEvent *event); + +/* handleNDofInput return values */ +#define NDOF_REFRESH 1 +#define NDOF_NOMOVE 2 +#define NDOF_CONFIRM 3 +#define NDOF_CANCEL 4 + + +/*********************** TransSpace ******************************/ + +int manageObjectSpace(int confirm, int set); +int manageMeshSpace(int confirm, int set); +int manageBoneSpace(int confirm, int set); + +/* Those two fill in mat and return non-zero on success */ +int createSpaceNormal(float mat[3][3], float normal[3]); +int createSpaceNormalTangent(float mat[3][3], float normal[3], float tangent[3]); + +int addMatrixSpace(float mat[3][3], char name[]); +int addObjectSpace(struct Object *ob); +void applyTransformOrientation(void); + + +#define ORIENTATION_NONE 0 +#define ORIENTATION_NORMAL 1 +#define ORIENTATION_VERT 2 +#define ORIENTATION_EDGE 3 +#define ORIENTATION_FACE 4 + +int getTransformOrientation(struct bContext *C, float normal[3], float plane[3], int activeOnly); +int createSpaceNormal(float mat[3][3], float normal[3]); +int createSpaceNormalTangent(float mat[3][3], float normal[3], float tangent[3]); + +#endif + + diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c new file mode 100644 index 00000000000..94c4ffa1497 --- /dev/null +++ b/source/blender/editors/transform/transform_constraints.c @@ -0,0 +1,1070 @@ +/** + * $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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_effect_types.h" +#include "DNA_image_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +//#include "BIF_screen.h" +//#include "BIF_resources.h" +//#include "BIF_mywindow.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" + +#include "ED_view3d.h" + +#include "BLI_arithb.h" + +//#include "BDR_drawobject.h" /* drawcircball */ +// +//#include "blendef.h" +// +//#include "mydevice.h" + +#include "WM_types.h" + +#include "transform.h" + +static void drawObjectConstraint(TransInfo *t); + +/* ************************** CONSTRAINTS ************************* */ +void constraintNumInput(TransInfo *t, float vec[3]) +{ + int mode = t->con.mode; + if (mode & CON_APPLY) { + float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f; + + if (getConstraintSpaceDimension(t) == 2) { + int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2); + if (axis == (CON_AXIS0|CON_AXIS1)) { + vec[2] = nval; + } + else if (axis == (CON_AXIS1|CON_AXIS2)) { + vec[2] = vec[1]; + vec[1] = vec[0]; + vec[0] = nval; + } + else if (axis == (CON_AXIS0|CON_AXIS2)) { + vec[2] = vec[1]; + vec[1] = nval; + } + } + else if (getConstraintSpaceDimension(t) == 1) { + if (mode & CON_AXIS0) { + vec[1] = nval; + vec[2] = nval; + } + else if (mode & CON_AXIS1) { + vec[1] = vec[0]; + vec[0] = nval; + vec[2] = nval; + } + else if (mode & CON_AXIS2) { + vec[2] = vec[0]; + vec[0] = nval; + vec[1] = nval; + } + } + } +} + +static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) { + int i = 0; + + Mat3MulVecfl(t->con.imtx, vec); + + snapGrid(t, vec); + + if (t->num.flag & T_NULL_ONE) { + if (!(t->con.mode & CON_AXIS0)) + vec[0] = 1.0f; + + if (!(t->con.mode & CON_AXIS1)) + vec[1] = 1.0f; + + if (!(t->con.mode & CON_AXIS2)) + vec[2] = 1.0f; + } + + if (hasNumInput(&t->num)) { + applyNumInput(&t->num, vec); + constraintNumInput(t, vec); + } + + if (t->con.mode & CON_AXIS0) { + pvec[i++] = vec[0]; + } + if (t->con.mode & CON_AXIS1) { + pvec[i++] = vec[1]; + } + if (t->con.mode & CON_AXIS2) { + pvec[i++] = vec[2]; + } + + Mat3MulVecfl(t->con.mtx, vec); +} + +static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) { + float norm[3], vec[3], factor; + + if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f) + return; + + /* For when view is parallel to constraint... will cause NaNs otherwise + So we take vertical motion in 3D space and apply it to the + constraint axis. Nice for camera grab + MMB */ + if(1.0f - fabs(Inpf(axis, t->viewinv[2])) < 0.000001f) { + Projf(vec, in, t->viewinv[1]); + factor = Inpf(t->viewinv[1], vec) * 2.0f; + /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */ + if(factor<0.0f) factor*= -factor; + else factor*= factor; + + VECCOPY(out, axis); + Normalize(out); + VecMulf(out, -factor); /* -factor makes move down going backwards */ + } + else { + float cb[3], ab[3]; + + VECCOPY(out, axis); + + /* Get view vector on axis to define a plane */ + VecAddf(vec, t->con.center, in); + getViewVector(t, vec, norm); + + Crossf(vec, norm, axis); + + /* Project input vector on the plane passing on axis */ + Projf(vec, in, vec); + VecSubf(vec, in, vec); + + /* intersect the two lines: axis and norm */ + Crossf(cb, vec, norm); + Crossf(ab, axis, norm); + + VecMulf(out, Inpf(cb, ab) / Inpf(ab, ab)); + } +} + +static void planeProjection(TransInfo *t, float in[3], float out[3]) { + float vec[3], factor, norm[3]; + + VecAddf(vec, in, t->con.center); + getViewVector(t, vec, norm); + + VecSubf(vec, out, in); + + factor = Inpf(vec, norm); + if (fabs(factor) <= 0.001) { + return; /* prevent divide by zero */ + } + factor = Inpf(vec, vec) / factor; + + VECCOPY(vec, norm); + VecMulf(vec, factor); + + VecAddf(out, in, vec); +} + +/* + * Generic callback for constant spacial constraints applied to linear motion + * + * The IN vector in projected into the constrained space and then further + * projected along the view vector. + * (in perspective mode, the view vector is relative to the position on screen) + * + */ + +static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3]) +{ + VECCOPY(out, in); + if (!td && t->con.mode & CON_APPLY) { + Mat3MulVecfl(t->con.pmtx, out); + + // With snap, a projection is alright, no need to correct for view alignment + if ((t->tsnap.status & SNAP_ON) == 0) { + if (getConstraintSpaceDimension(t) == 2) { + if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) { + planeProjection(t, in, out); + } + } + else if (getConstraintSpaceDimension(t) == 1) { + float c[3]; + + if (t->con.mode & CON_AXIS0) { + VECCOPY(c, t->con.mtx[0]); + } + else if (t->con.mode & CON_AXIS1) { + VECCOPY(c, t->con.mtx[1]); + } + else if (t->con.mode & CON_AXIS2) { + VECCOPY(c, t->con.mtx[2]); + } + axisProjection(t, c, in, out); + } + } + postConstraintChecks(t, out, pvec); + } +} + +/* + * Generic callback for object based spacial constraints applied to linear motion + * + * At first, the following is applied to the first data in the array + * The IN vector in projected into the constrained space and then further + * projected along the view vector. + * (in perspective mode, the view vector is relative to the position on screen) + * + * Further down, that vector is mapped to each data's space. + */ + +static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3]) +{ + VECCOPY(out, in); + if (t->con.mode & CON_APPLY) { + if (!td) { + Mat3MulVecfl(t->con.pmtx, out); + if (getConstraintSpaceDimension(t) == 2) { + if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) { + planeProjection(t, in, out); + } + } + else if (getConstraintSpaceDimension(t) == 1) { + float c[3]; + + if (t->con.mode & CON_AXIS0) { + VECCOPY(c, t->con.mtx[0]); + } + else if (t->con.mode & CON_AXIS1) { + VECCOPY(c, t->con.mtx[1]); + } + else if (t->con.mode & CON_AXIS2) { + VECCOPY(c, t->con.mtx[2]); + } + axisProjection(t, c, in, out); + } + postConstraintChecks(t, out, pvec); + VECCOPY(out, pvec); + } + else { + int i=0; + + out[0] = out[1] = out[2] = 0.0f; + if (t->con.mode & CON_AXIS0) { + out[0] = in[i++]; + } + if (t->con.mode & CON_AXIS1) { + out[1] = in[i++]; + } + if (t->con.mode & CON_AXIS2) { + out[2] = in[i++]; + } + Mat3MulVecfl(td->axismtx, out); + } + } +} + +/* + * Generic callback for constant spacial constraints applied to resize motion + * + * + */ + +static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +{ + if (!td && t->con.mode & CON_APPLY) { + float tmat[3][3]; + + if (!(t->con.mode & CON_AXIS0)) { + smat[0][0] = 1.0f; + } + if (!(t->con.mode & CON_AXIS1)) { + smat[1][1] = 1.0f; + } + if (!(t->con.mode & CON_AXIS2)) { + smat[2][2] = 1.0f; + } + + Mat3MulMat3(tmat, smat, t->con.imtx); + Mat3MulMat3(smat, t->con.mtx, tmat); + } +} + +/* + * Callback for object based spacial constraints applied to resize motion + * + * + */ + +static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +{ + if (td && t->con.mode & CON_APPLY) { + float tmat[3][3]; + float imat[3][3]; + + Mat3Inv(imat, td->axismtx); + + if (!(t->con.mode & CON_AXIS0)) { + smat[0][0] = 1.0f; + } + if (!(t->con.mode & CON_AXIS1)) { + smat[1][1] = 1.0f; + } + if (!(t->con.mode & CON_AXIS2)) { + smat[2][2] = 1.0f; + } + + Mat3MulMat3(tmat, smat, imat); + Mat3MulMat3(smat, td->axismtx, tmat); + } +} + +/* + * Generic callback for constant spacial constraints applied to rotations + * + * The rotation axis is copied into VEC. + * + * In the case of single axis constraints, the rotation axis is directly the one constrained to. + * For planar constraints (2 axis), the rotation axis is the normal of the plane. + * + * The following only applies when CON_NOFLIP is not set. + * The vector is then modified to always point away from the screen (in global space) + * This insures that the rotation is always logically following the mouse. + * (ie: not doing counterclockwise rotations when the mouse moves clockwise). + */ + +static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +{ + if (!td && t->con.mode & CON_APPLY) { + int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2); + + switch(mode) { + case CON_AXIS0: + case (CON_AXIS1|CON_AXIS2): + VECCOPY(vec, t->con.mtx[0]); + break; + case CON_AXIS1: + case (CON_AXIS0|CON_AXIS2): + VECCOPY(vec, t->con.mtx[1]); + break; + case CON_AXIS2: + case (CON_AXIS0|CON_AXIS1): + VECCOPY(vec, t->con.mtx[2]); + break; + } + /* don't flip axis if asked to or if num input */ + if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) { + if (Inpf(vec, t->viewinv[2]) > 0.0f) { + *angle = -(*angle); + } + } + } +} + +/* + * Callback for object based spacial constraints applied to rotations + * + * The rotation axis is copied into VEC. + * + * In the case of single axis constraints, the rotation axis is directly the one constrained to. + * For planar constraints (2 axis), the rotation axis is the normal of the plane. + * + * The following only applies when CON_NOFLIP is not set. + * The vector is then modified to always point away from the screen (in global space) + * This insures that the rotation is always logically following the mouse. + * (ie: not doing counterclockwise rotations when the mouse moves clockwise). + */ + +static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +{ + if (t->con.mode & CON_APPLY) { + int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2); + + /* on setup call, use first object */ + if (td == NULL) { + td= t->data; + } + + switch(mode) { + case CON_AXIS0: + case (CON_AXIS1|CON_AXIS2): + VECCOPY(vec, td->axismtx[0]); + break; + case CON_AXIS1: + case (CON_AXIS0|CON_AXIS2): + VECCOPY(vec, td->axismtx[1]); + break; + case CON_AXIS2: + case (CON_AXIS0|CON_AXIS1): + VECCOPY(vec, td->axismtx[2]); + break; + } + if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) { + if (Inpf(vec, t->viewinv[2]) > 0.0f) { + *angle = -(*angle); + } + } + } +} + +/*--------------------- INTERNAL SETUP CALLS ------------------*/ + +void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) { + strncpy(t->con.text + 1, text, 48); + Mat3CpyMat3(t->con.mtx, space); + t->con.mode = mode; + getConstraintMatrix(t); + + startConstraint(t); + + t->con.drawExtra = NULL; + t->con.applyVec = applyAxisConstraintVec; + t->con.applySize = applyAxisConstraintSize; + t->con.applyRot = applyAxisConstraintRot; + t->redraw = 1; +} + +void setLocalConstraint(TransInfo *t, int mode, const char text[]) { + if (t->flag & T_EDIT) { + float obmat[3][3]; + Mat3CpyMat4(obmat, G.obedit->obmat); + setConstraint(t, obmat, mode|CON_LOCAL, text); + } + else { + if (t->total == 1) { + setConstraint(t, t->data->axismtx, mode|CON_LOCAL, text); + } + else { + strncpy(t->con.text + 1, text, 48); + Mat3CpyMat3(t->con.mtx, t->data->axismtx); + t->con.mode = mode|CON_LOCAL; + getConstraintMatrix(t); + + startConstraint(t); + + t->con.drawExtra = drawObjectConstraint; + t->con.applyVec = applyObjectConstraintVec; + t->con.applySize = applyObjectConstraintSize; + t->con.applyRot = applyObjectConstraintRot; + t->redraw = 1; + } + } +} + +/* + Set the constraint according to the user defined orientation + + ftext is a format string passed to sprintf. It will add the name of + the orientation where %s is (logically). +*/ +void setUserConstraint(TransInfo *t, int mode, const char ftext[]) { + char text[40]; + short twmode= (t->spacetype==SPACE_VIEW3D)? ((View3D*)t->view)->twmode: V3D_MANIP_GLOBAL; + + switch(twmode) { + case V3D_MANIP_GLOBAL: + /* + sprintf(text, ftext, "global"); + Mat3One(mtx); + setConstraint(t, mtx, mode, text); + break; + */ + case V3D_MANIP_LOCAL: + sprintf(text, ftext, "local"); + setLocalConstraint(t, mode, text); + break; + case V3D_MANIP_NORMAL: + sprintf(text, ftext, "normal"); + setConstraint(t, t->spacemtx, mode, text); + break; + case V3D_MANIP_VIEW: + sprintf(text, ftext, "view"); + setConstraint(t, t->spacemtx, mode, text); + break; + default: /* V3D_MANIP_CUSTOM */ + sprintf(text, ftext, t->spacename); + setConstraint(t, t->spacemtx, mode, text); + break; + } + + t->con.mode |= CON_USER; +} + +/*--------------------- EXTERNAL SETUP CALLS ------------------*/ + +void BIF_setLocalLockConstraint(char axis, char *text) { + TransInfo *t = BIF_GetTransInfo(); + + if (t->total == 0) { + return; + } + + switch (axis) { + case 'x': + setLocalConstraint(t, (CON_AXIS1|CON_AXIS2), text); + break; + case 'y': + setLocalConstraint(t, (CON_AXIS0|CON_AXIS2), text); + break; + case 'z': + setLocalConstraint(t, (CON_AXIS0|CON_AXIS1), text); + break; + } +} + +void BIF_setLocalAxisConstraint(char axis, char *text) { + TransInfo *t = BIF_GetTransInfo(); + + if (t->total == 0) { + return; + } + + switch (axis) { + case 'X': + setLocalConstraint(t, CON_AXIS0, text); + break; + case 'Y': + setLocalConstraint(t, CON_AXIS1, text); + break; + case 'Z': + setLocalConstraint(t, CON_AXIS2, text); + break; + } +} + +/* text is optional, for header print */ +void BIF_setSingleAxisConstraint(float vec[3], char *text) { + TransInfo *t = BIF_GetTransInfo(); + float space[3][3], v[3]; + + if (t->total == 0) { + return; + } + + VECCOPY(space[0], vec); + + v[0] = vec[2]; + v[1] = vec[0]; + v[2] = vec[1]; + + Crossf(space[1], vec, v); + Crossf(space[2], vec, space[1]); + Mat3Ortho(space); + + Mat3CpyMat3(t->con.mtx, space); + t->con.mode = CON_AXIS0; + + getConstraintMatrix(t); + + startConstraint(t); + + /* start copying with an offset of 1, to reserve a spot for the SPACE char */ + if(text) + { + strncpy(t->con.text+1, text, 48); /* 50 in struct */ + } + else + { + t->con.text[1] = '\0'; /* No text */ + } + + t->con.drawExtra = NULL; + t->con.applyVec = applyAxisConstraintVec; + t->con.applySize = applyAxisConstraintSize; + t->con.applyRot = applyAxisConstraintRot; + t->redraw = 1; +} + +void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text) { + TransInfo *t = BIF_GetTransInfo(); + float space[3][3]; + + if (t->total == 0) { + return; + } + + VECCOPY(space[0], vec1); + VECCOPY(space[1], vec2); + Crossf(space[2], space[0], space[1]); + Mat3Ortho(space); + + Mat3CpyMat3(t->con.mtx, space); + t->con.mode = CON_AXIS0|CON_AXIS1; + + getConstraintMatrix(t); + + startConstraint(t); + + /* start copying with an offset of 1, to reserve a spot for the SPACE char */ + if(text) + { + strncpy(t->con.text+1, text, 48); /* 50 in struct */ + } + else + { + t->con.text[1] = '\0'; /* No text */ + } + + t->con.drawExtra = NULL; + t->con.applyVec = applyAxisConstraintVec; + t->con.applySize = applyAxisConstraintSize; + t->con.applyRot = applyAxisConstraintRot; + t->redraw = 1; +} + +/*----------------- DRAWING CONSTRAINTS -------------------*/ + +void BIF_drawConstraint(void) +{ + TransInfo *t = BIF_GetTransInfo(); + TransCon *tc = &(t->con); + + if (t->spacetype!=SPACE_VIEW3D) + return; + if (!(tc->mode & CON_APPLY)) + return; + if (t->flag & T_USES_MANIPULATOR) + return; + if (t->flag & T_NO_CONSTRAINT) + return; + + /* nasty exception for Z constraint in camera view */ + // TRANSFORM_FIX_ME +// if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB) +// return; + + if (tc->drawExtra) { + tc->drawExtra(t); + } + else { + if (tc->mode & CON_SELECT) { + float vec[3]; + char col2[3] = {255,255,255}; + convertViewVec(t, vec, (short)(t->mval[0] - t->con.imval[0]), (short)(t->mval[1] - t->con.imval[1])); + VecAddf(vec, vec, tc->center); + + drawLine(tc->center, tc->mtx[0], 'x', 0); + drawLine(tc->center, tc->mtx[1], 'y', 0); + drawLine(tc->center, tc->mtx[2], 'z', 0); + + glColor3ubv((GLubyte *)col2); + + glDisable(GL_DEPTH_TEST); + setlinestyle(1); + glBegin(GL_LINE_STRIP); + glVertex3fv(tc->center); + glVertex3fv(vec); + glEnd(); + setlinestyle(0); + // TRANSFORM_FIX_ME + //if(G.vd->zbuf) + glEnable(GL_DEPTH_TEST); + } + + if (tc->mode & CON_AXIS0) { + drawLine(tc->center, tc->mtx[0], 'x', DRAWLIGHT); + } + if (tc->mode & CON_AXIS1) { + drawLine(tc->center, tc->mtx[1], 'y', DRAWLIGHT); + } + if (tc->mode & CON_AXIS2) { + drawLine(tc->center, tc->mtx[2], 'z', DRAWLIGHT); + } + } +} + +/* called from drawview.c, as an extra per-window draw option */ +void BIF_drawPropCircle() +{ + TransInfo *t = BIF_GetTransInfo(); + + if (t->flag & T_PROP_EDIT) { + // TRANSFORM_FIX_ME +#if 0 + float tmat[4][4], imat[4][4]; + + BIF_ThemeColor(TH_GRID); + + /* if editmode we need to go into object space */ + if(G.obedit && t->spacetype == SPACE_VIEW3D) + mymultmatrix(G.obedit->obmat); + + mygetmatrix(tmat); + Mat4Invert(imat, tmat); + + set_inverted_drawing(1); + drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat); + set_inverted_drawing(0); + + /* if editmode we restore */ + if(G.obedit && t->spacetype == SPACE_VIEW3D) + myloadmatrix(G.vd->viewmat); +#endif + } +} + +void BIF_getPropCenter(float *center) +{ + TransInfo *t = BIF_GetTransInfo(); + + if (t && t->flag & T_PROP_EDIT) { + VECCOPY(center, t->center); + } + else + center[0] = center[1] = center[2] = 0.0f; +} +static void drawObjectConstraint(TransInfo *t) { + int i; + TransData * td = t->data; + + /* Draw the first one lighter because that's the one who controls the others. + Meaning the transformation is projected on that one and just copied on the others + constraint space. + In a nutshell, the object with light axis is controlled by the user and the others follow. + Without drawing the first light, users have little clue what they are doing. + */ + if (t->con.mode & CON_AXIS0) { + drawLine(td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT); + } + if (t->con.mode & CON_AXIS1) { + drawLine(td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT); + } + if (t->con.mode & CON_AXIS2) { + drawLine(td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT); + } + + td++; + + for(i=1;i<t->total;i++,td++) { + if (t->con.mode & CON_AXIS0) { + drawLine(td->ob->obmat[3], td->axismtx[0], 'x', 0); + } + if (t->con.mode & CON_AXIS1) { + drawLine(td->ob->obmat[3], td->axismtx[1], 'y', 0); + } + if (t->con.mode & CON_AXIS2) { + drawLine(td->ob->obmat[3], td->axismtx[2], 'z', 0); + } + } +} + +/*--------------------- START / STOP CONSTRAINTS ---------------------- */ + +void startConstraint(TransInfo *t) { + t->con.mode |= CON_APPLY; + *t->con.text = ' '; + t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max); +} + +void stopConstraint(TransInfo *t) { + t->con.mode &= ~(CON_APPLY|CON_SELECT); + *t->con.text = '\0'; + t->num.idx_max = t->idx_max; +} + +void getConstraintMatrix(TransInfo *t) +{ + float mat[3][3]; + Mat3Inv(t->con.imtx, t->con.mtx); + Mat3One(t->con.pmtx); + + if (!(t->con.mode & CON_AXIS0)) { + t->con.pmtx[0][0] = + t->con.pmtx[0][1] = + t->con.pmtx[0][2] = 0.0f; + } + + if (!(t->con.mode & CON_AXIS1)) { + t->con.pmtx[1][0] = + t->con.pmtx[1][1] = + t->con.pmtx[1][2] = 0.0f; + } + + if (!(t->con.mode & CON_AXIS2)) { + t->con.pmtx[2][0] = + t->con.pmtx[2][1] = + t->con.pmtx[2][2] = 0.0f; + } + + Mat3MulMat3(mat, t->con.pmtx, t->con.imtx); + Mat3MulMat3(t->con.pmtx, t->con.mtx, mat); +} + +/*------------------------- MMB Select -------------------------------*/ + +void initSelectConstraint(TransInfo *t, float mtx[3][3]) +{ + Mat3CpyMat3(t->con.mtx, mtx); + t->con.mode |= CON_APPLY; + t->con.mode |= CON_SELECT; + t->con.mode &= ~CON_LOCAL; + + setNearestAxis(t); + t->con.drawExtra = NULL; + t->con.applyVec = applyAxisConstraintVec; + t->con.applySize = applyAxisConstraintSize; + t->con.applyRot = applyAxisConstraintRot; +} + +void selectConstraint(TransInfo *t) { + if (t->con.mode & CON_SELECT) { + setNearestAxis(t); + startConstraint(t); + } +} + +void postSelectConstraint(TransInfo *t) +{ + if (!(t->con.mode & CON_SELECT)) + return; + + t->con.mode &= ~CON_AXIS0; + t->con.mode &= ~CON_AXIS1; + t->con.mode &= ~CON_AXIS2; + t->con.mode &= ~CON_SELECT; + + setNearestAxis(t); + + startConstraint(t); + t->redraw = 1; +} + +static void setNearestAxis2d(TransInfo *t) +{ + /* no correction needed... just use whichever one is lower */ + if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) { + t->con.mode |= CON_AXIS1; + sprintf(t->con.text, " along Y axis"); + } + else { + t->con.mode |= CON_AXIS0; + sprintf(t->con.text, " along X axis"); + } +} + +static void setNearestAxis3d(TransInfo *t) +{ + wmEvent *event = t->event; + float zfac; + float mvec[3], axis[3], proj[3]; + float len[3]; + int i, icoord[2]; + + /* calculate mouse movement */ + mvec[0] = (float)(t->mval[0] - t->con.imval[0]); + mvec[1] = (float)(t->mval[1] - t->con.imval[1]); + mvec[2] = 0.0f; + + /* we need to correct axis length for the current zoomlevel of view, + this to prevent projected values to be clipped behind the camera + and to overflow the short integers. + The formula used is a bit stupid, just a simplification of the substraction + of two 2D points 30 pixels apart (that's the last factor in the formula) after + projecting them with window_to_3d and then get the length of that vector. + */ + zfac= t->persmat[0][3]*t->center[0]+ t->persmat[1][3]*t->center[1]+ t->persmat[2][3]*t->center[2]+ t->persmat[3][3]; + zfac = VecLength(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f; + + for (i = 0; i<3; i++) { + VECCOPY(axis, t->con.mtx[i]); + + VecMulf(axis, zfac); + /* now we can project to get window coordinate */ + VecAddf(axis, axis, t->con.center); + projectIntView(t, axis, icoord); + + axis[0] = (float)(icoord[0] - t->center2d[0]); + axis[1] = (float)(icoord[1] - t->center2d[1]); + axis[2] = 0.0f; + + if (Normalize(axis) != 0.0f) { + Projf(proj, mvec, axis); + VecSubf(axis, mvec, proj); + len[i] = Normalize(axis); + } + else { + len[i] = 10000000000.0f; + } + } + + if (len[0] <= len[1] && len[0] <= len[2]) { + if (event->shift) { + t->con.mode |= (CON_AXIS1|CON_AXIS2); + sprintf(t->con.text, " locking %s X axis", t->spacename); + } + else { + t->con.mode |= CON_AXIS0; + sprintf(t->con.text, " along %s X axis", t->spacename); + } + } + else if (len[1] <= len[0] && len[1] <= len[2]) { + if (event->shift) { + t->con.mode |= (CON_AXIS0|CON_AXIS2); + sprintf(t->con.text, " locking %s Y axis", t->spacename); + } + else { + t->con.mode |= CON_AXIS1; + sprintf(t->con.text, " along %s Y axis", t->spacename); + } + } + else if (len[2] <= len[1] && len[2] <= len[0]) { + if (event->shift) { + t->con.mode |= (CON_AXIS0|CON_AXIS1); + sprintf(t->con.text, " locking %s Z axis", t->spacename); + } + else { + t->con.mode |= CON_AXIS2; + sprintf(t->con.text, " along %s Z axis", t->spacename); + } + } +} + +void setNearestAxis(TransInfo *t) +{ + /* clear any prior constraint flags */ + t->con.mode &= ~CON_AXIS0; + t->con.mode &= ~CON_AXIS1; + t->con.mode &= ~CON_AXIS2; + + /* constraint setting - depends on spacetype */ + if (t->spacetype == SPACE_VIEW3D) { + /* 3d-view */ + setNearestAxis3d(t); + } + else { + /* assume that this means a 2D-Editor */ + setNearestAxis2d(t); + } + + getConstraintMatrix(t); +} + +/*-------------- HELPER FUNCTIONS ----------------*/ + +char constraintModeToChar(TransInfo *t) { + if ((t->con.mode & CON_APPLY)==0) { + return '\0'; + } + switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) { + case (CON_AXIS0): + case (CON_AXIS1|CON_AXIS2): + return 'X'; + case (CON_AXIS1): + case (CON_AXIS0|CON_AXIS2): + return 'Y'; + case (CON_AXIS2): + case (CON_AXIS0|CON_AXIS1): + return 'Z'; + default: + return '\0'; + } +} + + +int isLockConstraint(TransInfo *t) { + int mode = t->con.mode; + + if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1)) + return 1; + + if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2)) + return 1; + + if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2)) + return 1; + + return 0; +} + +/* + * Returns the dimension of the constraint space. + * + * For that reason, the flags always needs to be set to properly evaluate here, + * even if they aren't actually used in the callback function. (Which could happen + * for weird constraints not yet designed. Along a path for example.) + */ + +int getConstraintSpaceDimension(TransInfo *t) +{ + int n = 0; + + if (t->con.mode & CON_AXIS0) + n++; + + if (t->con.mode & CON_AXIS1) + n++; + + if (t->con.mode & CON_AXIS2) + n++; + + return n; +/* + Someone willing to do it criptically could do the following instead: + + return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2); + + Based on the assumptions that the axis flags are one after the other and start at 1 +*/ +} diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c new file mode 100644 index 00000000000..cfc9a61e32c --- /dev/null +++ b/source/blender/editors/transform/transform_conversions.c @@ -0,0 +1,4461 @@ +/** + * $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 ***** + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_effect_types.h" +#include "DNA_image_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_meta_types.h" +#include "DNA_modifier_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_particle_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_texture_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" +#include "DNA_userdef_types.h" +#include "DNA_property_types.h" +#include "DNA_vfont_types.h" +#include "DNA_constraint_types.h" +#include "DNA_listBase.h" +#include "DNA_gpencil_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_blender.h" +#include "BKE_cloth.h" +#include "BKE_curve.h" +#include "BKE_constraint.h" +#include "BKE_depsgraph.h" +#include "BKE_displist.h" +#include "BKE_DerivedMesh.h" +#include "BKE_effect.h" +#include "BKE_font.h" +#include "BKE_global.h" +#include "BKE_ipo.h" +#include "BKE_lattice.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_softbody.h" +#include "BKE_utildefines.h" +#include "BKE_bmesh.h" +#include "BKE_context.h" + +//#include "BIF_editaction.h" +//#include "BIF_editview.h" +//#include "BIF_editlattice.h" +//#include "BIF_editconstraint.h" +#include "BIF_editarmature.h" +//#include "BIF_editmesh.h" +//#include "BIF_editnla.h" +//#include "BIF_editsima.h" +//#include "BIF_editparticle.h" +#include "BIF_gl.h" +//#include "BIF_keyframing.h" +//#include "BIF_poseobject.h" +//#include "BIF_meshtools.h" +//#include "BIF_mywindow.h" +//#include "BIF_resources.h" +#include "BIF_retopo.h" +//#include "BIF_screen.h" +//#include "BIF_space.h" +//#include "BIF_toolbox.h" + +#include "ED_types.h" +#include "ED_view3d.h" + +//#include "BSE_drawipo.h" +//#include "BSE_edit.h" +//#include "BSE_editipo.h" +//#include "BSE_editipo_types.h" +//#include "BSE_editaction_types.h" + +//#include "BDR_drawaction.h" // list of keyframes in action +//#include "BDR_editobject.h" // reset_slowparents() +//#include "BDR_gpencil.h" +//#include "BDR_unwrapper.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" + +//#include "editmesh.h" +// +//#include "blendef.h" +// +//#include "mydevice.h" + +extern ListBase editNurb; +extern ListBase editelems; + +#include "transform.h" + +#include "BLO_sys_types.h" // for intptr_t support + +/************ STUBS TO GET COMPILE ************/ +void transform_aspect_ratio_tface_uv(float *a1, float *a2) {} + + + +/* local function prototype - for Object/Bone Constraints */ +static short constraints_list_needinv(TransInfo *t, ListBase *list); +/* local function prototype - for finding number of keyframes that are selected for editing */ +static int count_ipo_keys(Ipo *ipo, char side, float cfra); + +/* ************************** Functions *************************** */ + +static void qsort_trans_data(TransInfo *t, TransData *head, TransData *tail) { + TransData pivot = *head; + TransData *ihead = head; + TransData *itail = tail; + short connected = t->flag & T_PROP_CONNECTED; + + while (head < tail) + { + if (connected) { + while ((tail->dist >= pivot.dist) && (head < tail)) + tail--; + } + else { + while ((tail->rdist >= pivot.rdist) && (head < tail)) + tail--; + } + + if (head != tail) + { + *head = *tail; + head++; + } + + if (connected) { + while ((head->dist <= pivot.dist) && (head < tail)) + head++; + } + else { + while ((head->rdist <= pivot.rdist) && (head < tail)) + head++; + } + + if (head != tail) + { + *tail = *head; + tail--; + } + } + + *head = pivot; + if (ihead < head) { + qsort_trans_data(t, ihead, head-1); + } + if (itail > head) { + qsort_trans_data(t, head+1, itail); + } +} + +void sort_trans_data_dist(TransInfo *t) { + TransData *start = t->data; + int i = 1; + + while(i < t->total && start->flag & TD_SELECTED) { + start++; + i++; + } + qsort_trans_data(t, start, t->data + t->total - 1); +} + +static void sort_trans_data(TransInfo *t) +{ + TransData *sel, *unsel; + TransData temp; + unsel = t->data; + sel = t->data; + sel += t->total - 1; + while (sel > unsel) { + while (unsel->flag & TD_SELECTED) { + unsel++; + if (unsel == sel) { + return; + } + } + while (!(sel->flag & TD_SELECTED)) { + sel--; + if (unsel == sel) { + return; + } + } + temp = *unsel; + *unsel = *sel; + *sel = temp; + sel--; + unsel++; + } +} + +/* distance calculated from not-selected vertex to nearest selected vertex + warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */ +static void set_prop_dist(TransInfo *t, short with_dist) +{ + TransData *tob; + int a; + + for(a=0, tob= t->data; a<t->total; a++, tob++) { + + tob->rdist= 0.0f; // init, it was mallocced + + if((tob->flag & TD_SELECTED)==0) { + TransData *td; + int i; + float dist, vec[3]; + + tob->rdist = -1.0f; // signal for next loop + + for (i = 0, td= t->data; i < t->total; i++, td++) { + if(td->flag & TD_SELECTED) { + VecSubf(vec, tob->center, td->center); + Mat3MulVecfl(tob->mtx, vec); + dist = Normalize(vec); + if (tob->rdist == -1.0f) { + tob->rdist = dist; + } + else if (dist < tob->rdist) { + tob->rdist = dist; + } + } + else break; // by definition transdata has selected items in beginning + } + if (with_dist) { + tob->dist = tob->rdist; + } + } + } +} + +/* ************************** CONVERSIONS ************************* */ + +/* ********************* texture space ********* */ + +static void createTransTexspace(bContext *C, TransInfo *t) +{ + Scene *scene = CTX_data_scene(C); + TransData *td; + Object *ob; + ID *id; + int *texflag; + + ob = OBACT; + + if (ob == NULL) { // Shouldn't logically happen, but still... + t->total = 0; + return; + } + + id = ob->data; + if(id == NULL || !ELEM3( GS(id->name), ID_ME, ID_CU, ID_MB )) { + t->total = 0; + return; + } + + t->total = 1; + td= t->data= MEM_callocN(sizeof(TransData), "TransTexspace"); + td->ext= t->ext= MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + + td->flag= TD_SELECTED; + VECCOPY(td->center, ob->obmat[3]); + td->ob = ob; + + Mat3CpyMat4(td->mtx, ob->obmat); + Mat3CpyMat4(td->axismtx, ob->obmat); + Mat3Ortho(td->axismtx); + Mat3Inv(td->smtx, td->mtx); + + if (give_obdata_texspace(ob, &texflag, &td->loc, &td->ext->size, &td->ext->rot)) { + *texflag &= ~AUTOSPACE; + } + + VECCOPY(td->iloc, td->loc); + VECCOPY(td->ext->irot, td->ext->rot); + VECCOPY(td->ext->isize, td->ext->size); +} + +/* ********************* edge (for crease) ***** */ + +static void createTransEdge(bContext *C, TransInfo *t) { +#if 0 // TRANSFORM_FIX_ME + TransData *td = NULL; + EditMesh *em = G.editMesh; + EditEdge *eed; + float mtx[3][3], smtx[3][3]; + int count=0, countsel=0; + int propmode = t->flag & T_PROP_EDIT; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + if (eed->f & SELECT) countsel++; + if (propmode) count++; + } + } + + if (countsel == 0) + return; + + if(propmode) { + t->total = count; + } + else { + t->total = countsel; + } + + td= t->data= MEM_callocN(t->total * sizeof(TransData), "TransCrease"); + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0 && (eed->f & SELECT || propmode)) { + /* need to set center for center calculations */ + VecAddf(td->center, eed->v1->co, eed->v2->co); + VecMulf(td->center, 0.5f); + + td->loc= NULL; + if (eed->f & SELECT) + td->flag= TD_SELECTED; + else + td->flag= 0; + + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td->ext = NULL; + td->tdi = NULL; + if (t->mode == TFM_BWEIGHT) { + td->val = &(eed->bweight); + td->ival = eed->bweight; + } + else { + td->val = &(eed->crease); + td->ival = eed->crease; + } + + td++; + } + } +#endif +} + +/* ********************* pose mode ************* */ + +static bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan) +{ + bConstraint *con= pchan->constraints.first; + + for(;con; con= con->next) { + if(con->type==CONSTRAINT_TYPE_KINEMATIC && (con->enforce!=0.0)) { + bKinematicConstraint *data= con->data; + + if(data->tar==NULL) + return data; + if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0) + return data; + } + } + return NULL; +} + +static short apply_targetless_ik(Object *ob) +{ + bPoseChannel *pchan, *parchan, *chanlist[256]; + bKinematicConstraint *data; + int segcount, apply= 0; + + /* now we got a difficult situation... we have to find the + target-less IK pchans, and apply transformation to the all + pchans that were in the chain */ + + for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { + data= has_targetless_ik(pchan); + if(data && (data->flag & CONSTRAINT_IK_AUTO)) { + + /* fill the array with the bones of the chain (armature.c does same, keep it synced) */ + segcount= 0; + + /* exclude tip from chain? */ + if(!(data->flag & CONSTRAINT_IK_TIP)) + parchan= pchan->parent; + else + parchan= pchan; + + /* Find the chain's root & count the segments needed */ + for (; parchan; parchan=parchan->parent){ + chanlist[segcount]= parchan; + segcount++; + + if(segcount==data->rootbone || segcount>255) break; // 255 is weak + } + for(;segcount;segcount--) { + Bone *bone; + float rmat[4][4], tmat[4][4], imat[4][4]; + + /* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */ + /* we put in channel the entire result of rmat= (channel * constraint * IK) */ + /* pose_mat(b) = pose_mat(b-1) * offs_bone * rmat */ + /* rmat = pose_mat(b) * inv( pose_mat(b-1) * offs_bone ) */ + + parchan= chanlist[segcount-1]; + bone= parchan->bone; + bone->flag |= BONE_TRANSFORM; /* ensures it gets an auto key inserted */ + + if(parchan->parent) { + Bone *parbone= parchan->parent->bone; + float offs_bone[4][4]; + + /* offs_bone = yoffs(b-1) + root(b) + bonemat(b) */ + Mat4CpyMat3(offs_bone, bone->bone_mat); + + /* The bone's root offset (is in the parent's coordinate system) */ + VECCOPY(offs_bone[3], bone->head); + + /* Get the length translation of parent (length along y axis) */ + offs_bone[3][1]+= parbone->length; + + /* pose_mat(b-1) * offs_bone */ + if(parchan->bone->flag & BONE_HINGE) { + /* the rotation of the parent restposition */ + Mat4CpyMat4(rmat, parbone->arm_mat); /* rmat used as temp */ + + /* the location of actual parent transform */ + VECCOPY(rmat[3], offs_bone[3]); + offs_bone[3][0]= offs_bone[3][1]= offs_bone[3][2]= 0.0f; + Mat4MulVecfl(parchan->parent->pose_mat, rmat[3]); + + Mat4MulMat4(tmat, offs_bone, rmat); + } + else if(parchan->bone->flag & BONE_NO_SCALE) { + Mat4MulMat4(tmat, offs_bone, parchan->parent->pose_mat); + Mat4Ortho(tmat); + } + else + Mat4MulMat4(tmat, offs_bone, parchan->parent->pose_mat); + + Mat4Invert(imat, tmat); + } + else { + Mat4CpyMat3(tmat, bone->bone_mat); + + VECCOPY(tmat[3], bone->head); + Mat4Invert(imat, tmat); + } + /* result matrix */ + Mat4MulMat4(rmat, parchan->pose_mat, imat); + + /* apply and decompose, doesn't work for constraints or non-uniform scale well */ + { + float rmat3[3][3], qmat[3][3], imat[3][3], smat[3][3]; + + Mat3CpyMat4(rmat3, rmat); + + /* quaternion */ + Mat3ToQuat(rmat3, parchan->quat); + + /* for size, remove rotation */ + /* causes problems with some constraints (so apply only if needed) */ + if (data->flag & CONSTRAINT_IK_STRETCH) { + QuatToMat3(parchan->quat, qmat); + Mat3Inv(imat, qmat); + Mat3MulMat3(smat, rmat3, imat); + Mat3ToSize(smat, parchan->size); + } + + /* causes problems with some constraints (e.g. childof), so disable this */ + /* as it is IK shouldn't affect location directly */ + /* VECCOPY(parchan->loc, rmat[3]); */ + } + + } + + apply= 1; + data->flag &= ~CONSTRAINT_IK_AUTO; + } + } + + return apply; +} + +static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td) +{ + Bone *bone= pchan->bone; + float pmat[3][3], omat[3][3]; + float cmat[3][3], tmat[3][3]; + float vec[3]; + + VECCOPY(vec, pchan->pose_mat[3]); + VECCOPY(td->center, vec); + + td->ob = ob; + td->flag= TD_SELECTED|TD_USEQUAT; + if (bone->flag & BONE_HINGE_CHILD_TRANSFORM) + { + td->flag |= TD_NOCENTER; + } + + if (bone->flag & BONE_TRANSFORM_CHILD) + { + td->flag |= TD_NOCENTER; + td->flag |= TD_NO_LOC; + } + + td->protectflag= pchan->protectflag; + + td->loc = pchan->loc; + VECCOPY(td->iloc, pchan->loc); + + td->ext->rot= NULL; + td->ext->quat= pchan->quat; + td->ext->size= pchan->size; + + QUATCOPY(td->ext->iquat, pchan->quat); + VECCOPY(td->ext->isize, pchan->size); + + /* proper way to get parent transform + own transform + constraints transform */ + Mat3CpyMat4(omat, ob->obmat); + + if(pchan->parent) { + if(pchan->bone->flag & BONE_HINGE) + Mat3CpyMat4(pmat, pchan->parent->bone->arm_mat); + else + Mat3CpyMat4(pmat, pchan->parent->pose_mat); + + if (constraints_list_needinv(t, &pchan->constraints)) { + Mat3CpyMat4(tmat, pchan->constinv); + Mat3Inv(cmat, tmat); + Mat3MulSerie(td->mtx, pchan->bone->bone_mat, pmat, omat, cmat, 0,0,0,0); // dang mulserie swaps args + } + else + Mat3MulSerie(td->mtx, pchan->bone->bone_mat, pmat, omat, 0,0,0,0,0); // dang mulserie swaps args + } + else { + if (constraints_list_needinv(t, &pchan->constraints)) { + Mat3CpyMat4(tmat, pchan->constinv); + Mat3Inv(cmat, tmat); + Mat3MulSerie(td->mtx, pchan->bone->bone_mat, omat, cmat, 0,0,0,0,0); // dang mulserie swaps args + } + else + Mat3MulMat3(td->mtx, omat, pchan->bone->bone_mat); // Mat3MulMat3 has swapped args! + } + + Mat3Inv(td->smtx, td->mtx); + + /* for axismat we use bone's own transform */ + Mat3CpyMat4(pmat, pchan->pose_mat); + Mat3MulMat3(td->axismtx, omat, pmat); + Mat3Ortho(td->axismtx); + + if(t->mode==TFM_BONESIZE) { + bArmature *arm= t->poseobj->data; + + if(arm->drawtype==ARM_ENVELOPE) { + td->loc= NULL; + td->val= &bone->dist; + td->ival= bone->dist; + } + else { + // abusive storage of scale in the loc pointer :) + td->loc= &bone->xwidth; + VECCOPY (td->iloc, td->loc); + td->val= NULL; + } + } + + /* in this case we can do target-less IK grabbing */ + if(t->mode==TFM_TRANSLATION) { + bKinematicConstraint *data= has_targetless_ik(pchan); + if(data) { + if(data->flag & CONSTRAINT_IK_TIP) { + VECCOPY(data->grabtarget, pchan->pose_tail); + } + else { + VECCOPY(data->grabtarget, pchan->pose_head); + } + td->loc = data->grabtarget; + VECCOPY(td->iloc, td->loc); + data->flag |= CONSTRAINT_IK_AUTO; + + /* only object matrix correction */ + Mat3CpyMat3 (td->mtx, omat); + Mat3Inv (td->smtx, td->mtx); + } + } + + /* store reference to first constraint */ + td->con= pchan->constraints.first; +} + +static void bone_children_clear_transflag(TransInfo *t, ListBase *lb) +{ + Bone *bone= lb->first; + + for(;bone;bone= bone->next) { + if((bone->flag & BONE_HINGE) && (bone->flag & BONE_CONNECTED)) + { + bone->flag |= BONE_HINGE_CHILD_TRANSFORM; + } + else if (bone->flag & BONE_TRANSFORM && (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)) + { + bone->flag |= BONE_TRANSFORM_CHILD; + } + else + { + bone->flag &= ~BONE_TRANSFORM; + } + + bone_children_clear_transflag(t, &bone->childbase); + } +} + +/* sets transform flags in the bones, returns total */ +static void set_pose_transflags(TransInfo *t, Object *ob) +{ + bArmature *arm= ob->data; + bPoseChannel *pchan; + Bone *bone; + int hastranslation; + + t->total= 0; + + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + bone= pchan->bone; + if(bone->layer & arm->layer) { + if(bone->flag & BONE_SELECTED) + bone->flag |= BONE_TRANSFORM; + else + bone->flag &= ~BONE_TRANSFORM; + + bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM; + bone->flag &= ~BONE_TRANSFORM_CHILD; + } + } + + /* make sure no bone can be transformed when a parent is transformed */ + /* since pchans are depsgraph sorted, the parents are in beginning of list */ + if(t->mode!=TFM_BONESIZE) { + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + bone= pchan->bone; + if(bone->flag & BONE_TRANSFORM) + bone_children_clear_transflag(t, &bone->childbase); + } + } + /* now count, and check if we have autoIK or have to switch from translate to rotate */ + hastranslation= 0; + + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + bone= pchan->bone; + if(bone->flag & BONE_TRANSFORM) { + + t->total++; + + if(t->mode==TFM_TRANSLATION) { + if( has_targetless_ik(pchan)==NULL ) { + if(pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) { + if(pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) + hastranslation= 1; + } + else if((pchan->protectflag & OB_LOCK_LOC)!=OB_LOCK_LOC) + hastranslation= 1; + } + else + hastranslation= 1; + } + } + } + + /* if there are no translatable bones, do rotation */ + if(t->mode==TFM_TRANSLATION && !hastranslation) + t->mode= TFM_ROTATION; +} + + +/* -------- Auto-IK ---------- */ + +/* adjust pose-channel's auto-ik chainlen */ +static void pchan_autoik_adjust (bPoseChannel *pchan, short chainlen) +{ + bConstraint *con; + + /* don't bother to search if no valid constraints */ + if ((pchan->constflag & (PCHAN_HAS_IK|PCHAN_HAS_TARGET))==0) + return; + + /* check if pchan has ik-constraint */ + for (con= pchan->constraints.first; con; con= con->next) { + if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce!=0.0)) { + bKinematicConstraint *data= con->data; + + /* only accept if a temporary one (for auto-ik) */ + if (data->flag & CONSTRAINT_IK_TEMP) { + /* chainlen is new chainlen, but is limited by maximum chainlen */ + if ((chainlen==0) || (chainlen > data->max_rootbone)) + data->rootbone= data->max_rootbone; + else + data->rootbone= chainlen; + } + } + } +} + +/* change the chain-length of auto-ik */ +void transform_autoik_update (TransInfo *t, short mode) +{ + short *chainlen= &G.scene->toolsettings->autoik_chainlen; + bPoseChannel *pchan; + + /* mode determines what change to apply to chainlen */ + if (mode == 1) { + /* mode=1 is from WHEELMOUSEDOWN... increases len */ + (*chainlen)++; + } + else if (mode == -1) { + /* mode==-1 is from WHEELMOUSEUP... decreases len */ + if (*chainlen > 0) (*chainlen)--; + } + + /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ + if (ELEM(NULL, t->poseobj, t->poseobj->pose)) + return; + + /* apply to all pose-channels */ + for (pchan=t->poseobj->pose->chanbase.first; pchan; pchan=pchan->next) { + pchan_autoik_adjust(pchan, *chainlen); + } +} + +/* frees temporal IKs */ +static void pose_grab_with_ik_clear(Object *ob) +{ + bKinematicConstraint *data; + bPoseChannel *pchan; + bConstraint *con, *next; + + for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + /* clear all temporary lock flags */ + pchan->ikflag &= ~(BONE_IK_NO_XDOF_TEMP|BONE_IK_NO_YDOF_TEMP|BONE_IK_NO_ZDOF_TEMP); + + pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_TARGET); + /* remove all temporary IK-constraints added */ + for (con= pchan->constraints.first; con; con= next) { + next= con->next; + if (con->type==CONSTRAINT_TYPE_KINEMATIC) { + data= con->data; + if (data->flag & CONSTRAINT_IK_TEMP) { + BLI_remlink(&pchan->constraints, con); + MEM_freeN(con->data); + MEM_freeN(con); + continue; + } + pchan->constflag |= PCHAN_HAS_IK; + if(data->tar==NULL || (data->tar->type==OB_ARMATURE && data->subtarget[0]==0)) + pchan->constflag |= PCHAN_HAS_TARGET; + } + } + } +} + +/* adds the IK to pchan - returns if added */ +static short pose_grab_with_ik_add(bPoseChannel *pchan) +{ + bKinematicConstraint *data; + bConstraint *con; + bConstraint *targetless = 0; + + /* Sanity check */ + if (pchan == NULL) + return 0; + + /* Rule: not if there's already an IK on this channel */ + for (con= pchan->constraints.first; con; con= con->next) { + if (con->type==CONSTRAINT_TYPE_KINEMATIC) { + bKinematicConstraint *data= con->data; + if(data->tar==NULL || (data->tar->type==OB_ARMATURE && data->subtarget[0]==0)) { + targetless = con; + /* but, if this is a targetless IK, we make it auto anyway (for the children loop) */ + if (con->enforce!=0.0) { + targetless->flag |= CONSTRAINT_IK_AUTO; + return 0; + } + } + if ((con->flag & CONSTRAINT_DISABLE)==0 && (con->enforce!=0.0)) + return 0; + } + } + + // TRANSFORM_FIX_ME + //con = add_new_constraint(CONSTRAINT_TYPE_KINEMATIC); + BLI_addtail(&pchan->constraints, con); + pchan->constflag |= (PCHAN_HAS_IK|PCHAN_HAS_TARGET); /* for draw, but also for detecting while pose solving */ + data= con->data; + if (targetless) { /* if exists use values from last targetless IK-constraint as base */ + *data = *((bKinematicConstraint*)targetless->data); + } + else + data->flag= CONSTRAINT_IK_TIP; + data->flag |= CONSTRAINT_IK_TEMP|CONSTRAINT_IK_AUTO; + VECCOPY(data->grabtarget, pchan->pose_tail); + data->rootbone= 1; + + /* we include only a connected chain */ + while ((pchan) && (pchan->bone->flag & BONE_CONNECTED)) { + /* here, we set ik-settings for bone from pchan->protectflag */ + if (pchan->protectflag & OB_LOCK_ROTX) pchan->ikflag |= BONE_IK_NO_XDOF_TEMP; + if (pchan->protectflag & OB_LOCK_ROTY) pchan->ikflag |= BONE_IK_NO_YDOF_TEMP; + if (pchan->protectflag & OB_LOCK_ROTZ) pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP; + + /* now we count this pchan as being included */ + data->rootbone++; + pchan= pchan->parent; + } + + /* make a copy of maximum chain-length */ + data->max_rootbone= data->rootbone; + + return 1; +} + +/* bone is a candidate to get IK, but we don't do it if it has children connected */ +static short pose_grab_with_ik_children(bPose *pose, Bone *bone) +{ + Bone *bonec; + short wentdeeper=0, added=0; + + /* go deeper if children & children are connected */ + for (bonec= bone->childbase.first; bonec; bonec= bonec->next) { + if (bonec->flag & BONE_CONNECTED) { + wentdeeper= 1; + added+= pose_grab_with_ik_children(pose, bonec); + } + } + if (wentdeeper==0) { + bPoseChannel *pchan= get_pose_channel(pose, bone->name); + if (pchan) + added+= pose_grab_with_ik_add(pchan); + } + + return added; +} + +/* main call which adds temporal IK chains */ +static short pose_grab_with_ik(Object *ob) +{ + bArmature *arm; + bPoseChannel *pchan, *parent; + Bone *bonec; + short tot_ik= 0; + + if ((ob==NULL) || (ob->pose==NULL) || (ob->flag & OB_POSEMODE)==0) + return 0; + + arm = ob->data; + + /* Rule: allow multiple Bones (but they must be selected, and only one ik-solver per chain should get added) */ + for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + if (pchan->bone->layer & arm->layer) { + if (pchan->bone->flag & BONE_SELECTED) { + /* Rule: no IK for solitatry (unconnected) bones */ + for (bonec=pchan->bone->childbase.first; bonec; bonec=bonec->next) { + if (bonec->flag & BONE_CONNECTED) { + break; + } + } + if ((pchan->bone->flag & BONE_CONNECTED)==0 && (bonec == NULL)) + continue; + + /* rule: if selected Bone is not a root bone, it gets a temporal IK */ + if (pchan->parent) { + /* only adds if there's no IK yet (and no parent bone was selected) */ + for (parent= pchan->parent; parent; parent= parent->parent) { + if (parent->bone->flag & BONE_SELECTED) + break; + } + if (parent == NULL) + tot_ik += pose_grab_with_ik_add(pchan); + } + else { + /* rule: go over the children and add IK to the tips */ + tot_ik += pose_grab_with_ik_children(ob->pose, pchan->bone); + } + } + } + } + + return (tot_ik) ? 1 : 0; +} + + +/* only called with pose mode active object now */ +static void createTransPose(bContext *C, TransInfo *t, Object *ob) +{ + // TRANSFORM_FIX_ME +#if 0 + bArmature *arm; + bPoseChannel *pchan; + TransData *td; + TransDataExtension *tdx; + short ik_on= 0; + int i; + + t->total= 0; + + /* check validity of state */ + arm=get_armature (ob); + if (arm==NULL || ob->pose==NULL) return; + + if (arm->flag & ARM_RESTPOS) { + if(ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE)==0) { + notice("Pose edit not possible while Rest Position is enabled"); + return; + } + } + if (!(ob->lay & G.vd->lay)) return; + + /* do we need to add temporal IK chains? */ + if ((arm->flag & ARM_AUTO_IK) && t->mode==TFM_TRANSLATION) { + ik_on= pose_grab_with_ik(ob); + if (ik_on) t->flag |= T_AUTOIK; + } + + /* set flags and count total (warning, can change transform to rotate) */ + set_pose_transflags(t, ob); + + if(t->total==0) return; + + t->flag |= T_POSE; + t->poseobj= ob; /* we also allow non-active objects to be transformed, in weightpaint */ + + /* make sure the lock is set OK, unlock can be accidentally saved? */ + ob->pose->flag |= POSE_LOCKED; + ob->pose->flag &= ~POSE_DO_UNLOCK; + + /* init trans data */ + td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransPoseBone"); + tdx = t->ext = MEM_callocN(t->total*sizeof(TransDataExtension), "TransPoseBoneExt"); + for(i=0; i<t->total; i++, td++, tdx++) { + td->ext= tdx; + td->tdi = NULL; + td->val = NULL; + } + + /* use pose channels to fill trans data */ + td= t->data; + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + if(pchan->bone->flag & BONE_TRANSFORM) { + add_pose_transdata(t, pchan, ob, td); + td++; + } + } + + if(td != (t->data+t->total)) printf("Bone selection count error\n"); + + /* initialise initial auto=ik chainlen's? */ + if (ik_on) transform_autoik_update(t, 0); +#endif +} + +/* ********************* armature ************** */ + +static void createTransArmatureVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + EditBone *ebo; + bArmature *arm= G.obedit->data; + TransData *td; + float mtx[3][3], smtx[3][3], delta[3], bonemat[3][3]; + + t->total = 0; + for (ebo=G.edbo.first;ebo;ebo=ebo->next) { + if(ebo->layer & arm->layer) { + if (t->mode==TFM_BONESIZE) { + if (ebo->flag & BONE_SELECTED) + t->total++; + } + else if (t->mode==TFM_BONE_ROLL) { + if (ebo->flag & BONE_SELECTED) + t->total++; + } + else { + if (ebo->flag & BONE_TIPSEL) + t->total++; + if (ebo->flag & BONE_ROOTSEL) + t->total++; + } + } + } + + if (!t->total) return; + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransEditBone"); + + for (ebo=G.edbo.first;ebo;ebo=ebo->next){ + ebo->oldlength= ebo->length; // length==0.0 on extrude, used for scaling radius of bone points + + if(ebo->layer & arm->layer) { + if (t->mode==TFM_BONE_ENVELOPE) { + + if (ebo->flag & BONE_ROOTSEL){ + td->val= &ebo->rad_head; + td->ival= *td->val; + + VECCOPY (td->center, ebo->head); + td->flag= TD_SELECTED; + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td->loc = NULL; + td->ext = NULL; + td->tdi = NULL; + + td++; + } + if (ebo->flag & BONE_TIPSEL){ + td->val= &ebo->rad_tail; + td->ival= *td->val; + VECCOPY (td->center, ebo->tail); + td->flag= TD_SELECTED; + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td->loc = NULL; + td->ext = NULL; + td->tdi = NULL; + + td++; + } + + } + else if (t->mode==TFM_BONESIZE) { + if (ebo->flag & BONE_SELECTED) { + if(arm->drawtype==ARM_ENVELOPE) { + td->loc= NULL; + td->val= &ebo->dist; + td->ival= ebo->dist; + } + else { + // abusive storage of scale in the loc pointer :) + td->loc= &ebo->xwidth; + VECCOPY (td->iloc, td->loc); + td->val= NULL; + } + VECCOPY (td->center, ebo->head); + td->flag= TD_SELECTED; + + /* use local bone matrix */ + VecSubf(delta, ebo->tail, ebo->head); + vec_roll_to_mat3(delta, ebo->roll, bonemat); + Mat3MulMat3(td->mtx, mtx, bonemat); + Mat3Inv(td->smtx, td->mtx); + + Mat3CpyMat3(td->axismtx, td->mtx); + Mat3Ortho(td->axismtx); + + td->ext = NULL; + td->tdi = NULL; + + td++; + } + } + else if (t->mode==TFM_BONE_ROLL) { + if (ebo->flag & BONE_SELECTED) { + td->loc= NULL; + td->val= &(ebo->roll); + td->ival= ebo->roll; + + VECCOPY (td->center, ebo->head); + td->flag= TD_SELECTED; + + td->ext = NULL; + td->tdi = NULL; + + td++; + } + } + else { + if (ebo->flag & BONE_TIPSEL){ + VECCOPY (td->iloc, ebo->tail); + VECCOPY (td->center, td->iloc); + td->loc= ebo->tail; + td->flag= TD_SELECTED; + if (ebo->flag & BONE_EDITMODE_LOCKED) + td->protectflag = OB_LOCK_LOC|OB_LOCK_ROT|OB_LOCK_SCALE; + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + VecSubf(delta, ebo->tail, ebo->head); + vec_roll_to_mat3(delta, ebo->roll, td->axismtx); + + if ((ebo->flag & BONE_ROOTSEL) == 0) + { + td->extra = ebo; + } + + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + + td++; + } + if (ebo->flag & BONE_ROOTSEL){ + VECCOPY (td->iloc, ebo->head); + VECCOPY (td->center, td->iloc); + td->loc= ebo->head; + td->flag= TD_SELECTED; + if (ebo->flag & BONE_EDITMODE_LOCKED) + td->protectflag = OB_LOCK_LOC|OB_LOCK_ROT|OB_LOCK_SCALE; + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + VecSubf(delta, ebo->tail, ebo->head); + vec_roll_to_mat3(delta, ebo->roll, td->axismtx); + + td->extra = ebo; /* to fix roll */ + + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + + td++; + } + } + } + } +#endif +} + +/* ********************* meta elements ********* */ + +static void createTransMBallVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + MetaElem *ml; + TransData *td; + TransDataExtension *tx; + float mtx[3][3], smtx[3][3]; + int count=0, countsel=0; + int propmode = t->flag & T_PROP_EDIT; + + /* count totals */ + for(ml= editelems.first; ml; ml= ml->next) { + if(ml->flag & SELECT) countsel++; + if(propmode) count++; + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel==0) return; + + if(propmode) t->total = count; + else t->total = countsel; + + td = t->data= MEM_callocN(t->total*sizeof(TransData), "TransObData(MBall EditMode)"); + tx = t->ext = MEM_callocN(t->total*sizeof(TransDataExtension), "MetaElement_TransExtension"); + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + for(ml= editelems.first; ml; ml= ml->next) { + if(propmode || (ml->flag & SELECT)) { + td->loc= &ml->x; + VECCOPY(td->iloc, td->loc); + VECCOPY(td->center, td->loc); + + if(ml->flag & SELECT) td->flag= TD_SELECTED | TD_USEQUAT | TD_SINGLESIZE; + else td->flag= TD_USEQUAT; + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td->ext = tx; + td->tdi = NULL; + + /* Radius of MetaElem (mass of MetaElem influence) */ + if(ml->flag & MB_SCALE_RAD){ + td->val = &ml->rad; + td->ival = ml->rad; + } + else{ + td->val = &ml->s; + td->ival = ml->s; + } + + /* expx/expy/expz determine "shape" of some MetaElem types */ + tx->size = &ml->expx; + tx->isize[0] = ml->expx; + tx->isize[1] = ml->expy; + tx->isize[2] = ml->expz; + + /* quat is used for rotation of MetaElem */ + tx->quat = ml->quat; + QUATCOPY(tx->iquat, ml->quat); + + tx->rot = NULL; + + td++; + tx++; + } + } +#endif +} + +/* ********************* curve/surface ********* */ + +static void calc_distanceCurveVerts(TransData *head, TransData *tail) { + TransData *td, *td_near = NULL; + for (td = head; td<=tail; td++) { + if (td->flag & TD_SELECTED) { + td_near = td; + td->dist = 0.0f; + } + else if(td_near) { + float dist; + dist = VecLenf(td_near->center, td->center); + if (dist < (td-1)->dist) { + td->dist = (td-1)->dist; + } + else { + td->dist = dist; + } + } + else { + td->dist = MAXFLOAT; + td->flag |= TD_NOTCONNECTED; + } + } + td_near = NULL; + for (td = tail; td>=head; td--) { + if (td->flag & TD_SELECTED) { + td_near = td; + td->dist = 0.0f; + } + else if(td_near) { + float dist; + dist = VecLenf(td_near->center, td->center); + if (td->flag & TD_NOTCONNECTED || dist < td->dist || (td+1)->dist < td->dist) { + td->flag &= ~TD_NOTCONNECTED; + if (dist < (td+1)->dist) { + td->dist = (td+1)->dist; + } + else { + td->dist = dist; + } + } + } + } +} + +/* Utility function for getting the handle data from bezier's */ +TransDataCurveHandleFlags *initTransDataCurveHandes(TransData *td, struct BezTriple *bezt) { + TransDataCurveHandleFlags *hdata; + td->flag |= TD_BEZTRIPLE; + hdata = td->hdata = MEM_mallocN(sizeof(TransDataCurveHandleFlags), "CuHandle Data"); + hdata->ih1 = bezt->h1; + hdata->h1 = &bezt->h1; + hdata->ih2 = bezt->h2; /* incase the second is not selected */ + hdata->h2 = &bezt->h2; + return hdata; +} + +static void createTransCurveVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *td = NULL; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float mtx[3][3], smtx[3][3]; + int a; + int count=0, countsel=0; + int propmode = t->flag & T_PROP_EDIT; + + /* count total of vertices, check identical as in 2nd loop for making transdata! */ + for(nu= editNurb.first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + for(a=0, bezt= nu->bezt; a<nu->pntsu; a++, bezt++) { + if(bezt->hide==0) { + if (G.f & G_HIDDENHANDLES) { + if(bezt->f2 & SELECT) countsel+=3; + if(propmode) count+= 3; + } else { + if(bezt->f1 & SELECT) countsel++; + if(bezt->f2 & SELECT) countsel++; + if(bezt->f3 & SELECT) countsel++; + if(propmode) count+= 3; + } + } + } + } + else { + for(a= nu->pntsu*nu->pntsv, bp= nu->bp; a>0; a--, bp++) { + if(bp->hide==0) { + if(propmode) count++; + if(bp->f1 & SELECT) countsel++; + } + } + } + } + /* note: in prop mode we need at least 1 selected */ + if (countsel==0) return; + + if(propmode) t->total = count; + else t->total = countsel; + t->data= MEM_callocN(t->total*sizeof(TransData), "TransObData(Curve EditMode)"); + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + td = t->data; + for(nu= editNurb.first; nu; nu= nu->next) { + if((nu->type & 7)==CU_BEZIER) { + TransData *head, *tail; + head = tail = td; + for(a=0, bezt= nu->bezt; a<nu->pntsu; a++, bezt++) { + if(bezt->hide==0) { + TransDataCurveHandleFlags *hdata = NULL; + + if( propmode || + ((bezt->f2 & SELECT) && (G.f & G_HIDDENHANDLES)) || + ((bezt->f1 & SELECT) && (G.f & G_HIDDENHANDLES)==0) + ) { + VECCOPY(td->iloc, bezt->vec[0]); + td->loc= bezt->vec[0]; + VECCOPY(td->center, bezt->vec[1]); + if (G.f & G_HIDDENHANDLES) { + if(bezt->f2 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + } else { + if(bezt->f1 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + } + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + + hdata = initTransDataCurveHandes(td, bezt); + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td++; + count++; + tail++; + } + + /* This is the Curve Point, the other two are handles */ + if(propmode || (bezt->f2 & SELECT)) { + VECCOPY(td->iloc, bezt->vec[1]); + td->loc= bezt->vec[1]; + VECCOPY(td->center, td->loc); + if(bezt->f2 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + td->ext = NULL; + td->tdi = NULL; + + if (t->mode==TFM_CURVE_SHRINKFATTEN) { /* || t->mode==TFM_RESIZE) {*/ /* TODO - make points scale */ + td->val = &(bezt->radius); + td->ival = bezt->radius; + } else if (t->mode==TFM_TILT) { + td->val = &(bezt->alfa); + td->ival = bezt->alfa; + } else { + td->val = NULL; + } + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + if ((bezt->f1&SELECT)==0 && (bezt->f3&SELECT)==0) + /* If the middle is selected but the sides arnt, this is needed */ + if (hdata==NULL) { /* if the handle was not saved by the previous handle */ + hdata = initTransDataCurveHandes(td, bezt); + } + + td++; + count++; + tail++; + } + if( propmode || + ((bezt->f2 & SELECT) && (G.f & G_HIDDENHANDLES)) || + ((bezt->f3 & SELECT) && (G.f & G_HIDDENHANDLES)==0) + ) { + VECCOPY(td->iloc, bezt->vec[2]); + td->loc= bezt->vec[2]; + VECCOPY(td->center, bezt->vec[1]); + if (G.f & G_HIDDENHANDLES) { + if(bezt->f2 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + } else { + if(bezt->f3 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + } + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + + if (hdata==NULL) { /* if the handle was not saved by the previous handle */ + hdata = initTransDataCurveHandes(td, bezt); + } + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td++; + count++; + tail++; + } + } + else if (propmode && head != tail) { + calc_distanceCurveVerts(head, tail-1); + head = tail; + } + } + if (propmode && head != tail) + calc_distanceCurveVerts(head, tail-1); + + /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandes + * but for now just dont change handle types */ + if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT) == 0) + testhandlesNurb(nu); /* sets the handles based on their selection, do this after the data is copied to the TransData */ + } + else { + TransData *head, *tail; + head = tail = td; + for(a= nu->pntsu*nu->pntsv, bp= nu->bp; a>0; a--, bp++) { + if(bp->hide==0) { + if(propmode || (bp->f1 & SELECT)) { + VECCOPY(td->iloc, bp->vec); + td->loc= bp->vec; + VECCOPY(td->center, td->loc); + if(bp->f1 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + td->ext = NULL; + td->tdi = NULL; + + if (t->mode==TFM_CURVE_SHRINKFATTEN || t->mode==TFM_RESIZE) { + td->val = &(bp->radius); + td->ival = bp->radius; + } else { + td->val = &(bp->alfa); + td->ival = bp->alfa; + } + + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td++; + count++; + tail++; + } + } + else if (propmode && head != tail) { + calc_distanceCurveVerts(head, tail-1); + head = tail; + } + } + if (propmode && head != tail) + calc_distanceCurveVerts(head, tail-1); + } + } +#endif +} + +/* ********************* lattice *************** */ + +static void createTransLatticeVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *td = NULL; + BPoint *bp; + float mtx[3][3], smtx[3][3]; + int a; + int count=0, countsel=0; + int propmode = t->flag & T_PROP_EDIT; + + bp= editLatt->def; + a= editLatt->pntsu*editLatt->pntsv*editLatt->pntsw; + while(a--) { + if(bp->hide==0) { + if(bp->f1 & SELECT) countsel++; + if(propmode) count++; + } + bp++; + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel==0) return; + + if(propmode) t->total = count; + else t->total = countsel; + t->data= MEM_callocN(t->total*sizeof(TransData), "TransObData(Lattice EditMode)"); + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + td = t->data; + bp= editLatt->def; + a= editLatt->pntsu*editLatt->pntsv*editLatt->pntsw; + while(a--) { + if(propmode || (bp->f1 & SELECT)) { + if(bp->hide==0) { + VECCOPY(td->iloc, bp->vec); + td->loc= bp->vec; + VECCOPY(td->center, td->loc); + if(bp->f1 & SELECT) td->flag= TD_SELECTED; + else td->flag= 0; + Mat3CpyMat3(td->smtx, smtx); + Mat3CpyMat3(td->mtx, mtx); + + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + + td++; + count++; + } + } + bp++; + } +#endif +} + +/* ******************* particle edit **************** */ +static void createTransParticleVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *td = NULL; + TransDataExtension *tx; + Base *base = BASACT; + Object *ob = OBACT; + ParticleSystem *psys = PE_get_current(ob); + ParticleSystemModifierData *psmd = NULL; + ParticleEditSettings *pset = PE_settings(); + ParticleData *pa = NULL; + ParticleEdit *edit; + ParticleEditKey *key; + float mat[4][4]; + int i,k, totpart, transformparticle; + int count = 0, hasselected = 0; + int propmode = t->flag & T_PROP_EDIT; + + if(psys==NULL || G.scene->selectmode==SCE_SELECT_PATH) return; + + psmd = psys_get_modifier(ob,psys); + + edit = psys->edit; + totpart = psys->totpart; + base->flag |= BA_HAS_RECALC_DATA; + + for(i=0, pa=psys->particles; i<totpart; i++, pa++) { + pa->flag &= ~PARS_TRANSFORM; + transformparticle= 0; + + if((pa->flag & PARS_HIDE)==0) { + for(k=0, key=edit->keys[i]; k<pa->totkey; k++, key++) { + if((key->flag&PEK_HIDE)==0) { + if(key->flag&PEK_SELECT) { + hasselected= 1; + transformparticle= 1; + } + else if(propmode) + transformparticle= 1; + } + } + } + + if(transformparticle) { + count += pa->totkey; + pa->flag |= PARS_TRANSFORM; + } + } + + /* note: in prop mode we need at least 1 selected */ + if (hasselected==0) return; + + t->total = count; + td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Particle Mode)"); + + if(t->mode == TFM_BAKE_TIME) + tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "Particle_TransExtension"); + else + tx = t->ext = NULL; + + Mat4One(mat); + + Mat4Invert(ob->imat,ob->obmat); + + for(i=0, pa=psys->particles; i<totpart; i++, pa++) { + TransData *head, *tail; + head = tail = td; + + if(!(pa->flag & PARS_TRANSFORM)) continue; + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, mat); + + for(k=0, key=edit->keys[i]; k<pa->totkey; k++, key++) { + VECCOPY(key->world_co, key->co); + Mat4MulVecfl(mat, key->world_co); + td->loc = key->world_co; + + VECCOPY(td->iloc, td->loc); + VECCOPY(td->center, td->loc); + + if(key->flag & PEK_SELECT) + td->flag |= TD_SELECTED; + else if(!propmode) + td->flag |= TD_SKIP; + + Mat3One(td->mtx); + Mat3One(td->smtx); + + /* don't allow moving roots */ + if(k==0 && pset->flag & PE_LOCK_FIRST) + td->protectflag |= OB_LOCK_LOC; + + td->ob = ob; + td->ext = tx; + td->tdi = NULL; + if(t->mode == TFM_BAKE_TIME) { + td->val = key->time; + td->ival = *(key->time); + /* abuse size and quat for min/max values */ + td->flag |= TD_NO_EXT; + if(k==0) tx->size = 0; + else tx->size = (key - 1)->time; + + if(k == pa->totkey - 1) tx->quat = 0; + else tx->quat = (key + 1)->time; + } + + td++; + if(tx) + tx++; + tail++; + } + if (propmode && head != tail) + calc_distanceCurveVerts(head, tail - 1); + } +#endif +} + +void flushTransParticles(TransInfo *t) +{ +#if 0 // TRANSFORM_FIX_ME + Scene *scene = t->scene; + Object *ob = OBACT; + ParticleSystem *psys = PE_get_current(ob); + ParticleSystemModifierData *psmd; + ParticleData *pa; + ParticleEditKey *key; + TransData *td; + float mat[4][4], imat[4][4], co[3]; + int i, k, propmode = t->flag & T_PROP_EDIT; + + psmd = psys_get_modifier(ob, psys); + + /* we do transform in world space, so flush world space position + * back to particle local space */ + td= t->data; + for(i=0, pa=psys->particles; i<psys->totpart; i++, pa++, td++) { + if(!(pa->flag & PARS_TRANSFORM)) continue; + + psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, mat); + Mat4Invert(imat,mat); + + for(k=0, key=psys->edit->keys[i]; k<pa->totkey; k++, key++) { + VECCOPY(co, key->world_co); + Mat4MulVecfl(imat, co); + + /* optimization for proportional edit */ + if(!propmode || !FloatCompare(key->co, co, 0.0001f)) { + VECCOPY(key->co, co); + pa->flag |= PARS_EDIT_RECALC; + } + } + } + + PE_update_object(OBACT, 1); +#endif +} + +/* ********************* mesh ****************** */ + +/* proportional distance based on connectivity */ +#define E_VEC(a) (vectors + (3 * (a)->tmp.l)) +#define E_NEAR(a) (nears[((a)->tmp.l)]) +#define THRESHOLD 0.0001f +static void editmesh_set_connectivity_distance(int total, float *vectors, EditVert **nears) +{ + EditMesh *em = G.editMesh; + EditVert *eve; + EditEdge *eed; + int i= 0, done= 1; + + /* f2 flag is used for 'selection' */ + /* tmp.l is offset on scratch array */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0) { + eve->tmp.l = i++; + + if(eve->f & SELECT) { + eve->f2= 2; + E_NEAR(eve) = eve; + E_VEC(eve)[0] = 0.0f; + E_VEC(eve)[1] = 0.0f; + E_VEC(eve)[2] = 0.0f; + } + else { + eve->f2 = 0; + } + } + } + + + /* Floodfill routine */ + /* + At worst this is n*n of complexity where n is number of edges + Best case would be n if the list is ordered perfectly. + Estimate is n log n in average (so not too bad) + */ + while(done) { + done= 0; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0) { + EditVert *v1= eed->v1, *v2= eed->v2; + float *vec2 = E_VEC(v2); + float *vec1 = E_VEC(v1); + + if (v1->f2 + v2->f2 == 4) + continue; + + if (v1->f2) { + if (v2->f2) { + float nvec[3]; + float len1 = VecLength(vec1); + float len2 = VecLength(vec2); + float lenn; + /* for v2 if not selected */ + if (v2->f2 != 2) { + VecSubf(nvec, v2->co, E_NEAR(v1)->co); + lenn = VecLength(nvec); + /* 1 < n < 2 */ + if (lenn - len1 > THRESHOLD && len2 - lenn > THRESHOLD) { + VECCOPY(vec2, nvec); + E_NEAR(v2) = E_NEAR(v1); + done = 1; + } + /* n < 1 < 2 */ + else if (len2 - len1 > THRESHOLD && len1 - lenn > THRESHOLD) { + VECCOPY(vec2, vec1); + E_NEAR(v2) = E_NEAR(v1); + done = 1; + } + } + /* for v1 if not selected */ + if (v1->f2 != 2) { + VecSubf(nvec, v1->co, E_NEAR(v2)->co); + lenn = VecLength(nvec); + /* 2 < n < 1 */ + if (lenn - len2 > THRESHOLD && len1 - lenn > THRESHOLD) { + VECCOPY(vec1, nvec); + E_NEAR(v1) = E_NEAR(v2); + done = 1; + } + /* n < 2 < 1 */ + else if (len1 - len2 > THRESHOLD && len2 - lenn > THRESHOLD) { + VECCOPY(vec1, vec2); + E_NEAR(v1) = E_NEAR(v2); + done = 1; + } + } + } + else { + v2->f2 = 1; + VecSubf(vec2, v2->co, E_NEAR(v1)->co); + /* 2 < 1 */ + if (VecLength(vec1) - VecLength(vec2) > THRESHOLD) { + VECCOPY(vec2, vec1); + } + E_NEAR(v2) = E_NEAR(v1); + done = 1; + } + } + else if (v2->f2) { + v1->f2 = 1; + VecSubf(vec1, v1->co, E_NEAR(v2)->co); + /* 2 < 1 */ + if (VecLength(vec2) - VecLength(vec1) > THRESHOLD) { + VECCOPY(vec1, vec2); + } + E_NEAR(v1) = E_NEAR(v2); + done = 1; + } + } + } + } +} + +/* loop-in-a-loop I know, but we need it! (ton) */ +static void get_face_center(float *cent, EditVert *eve) +{ + EditMesh *em = G.editMesh; + EditFace *efa; + + for(efa= em->faces.first; efa; efa= efa->next) + if(efa->f & SELECT) + if(efa->v1==eve || efa->v2==eve || efa->v3==eve || efa->v4==eve) + break; + if(efa) { + VECCOPY(cent, efa->cent); + } +} + +//way to overwrite what data is edited with transform +//static void VertsToTransData(TransData *td, EditVert *eve, BakeKey *key) +static void VertsToTransData(TransData *td, EditVert *eve) +{ + td->flag = 0; + //if(key) + // td->loc = key->co; + //else + td->loc = eve->co; + + VECCOPY(td->center, td->loc); +// TRANSFORM_FIX_ME +// if(G.vd->around==V3D_LOCAL && (G.scene->selectmode & SCE_SELECT_FACE)) +// get_face_center(td->center, eve); + VECCOPY(td->iloc, td->loc); + + // Setting normals + VECCOPY(td->axismtx[2], eve->no); + td->axismtx[0][0] = + td->axismtx[0][1] = + td->axismtx[0][2] = + td->axismtx[1][0] = + td->axismtx[1][1] = + td->axismtx[1][2] = 0.0f; + + td->ext = NULL; + td->tdi = NULL; + td->val = NULL; + td->extra = NULL; + if (BIF_GetTransInfo()->mode == TFM_BWEIGHT) { + td->val = &(eve->bweight); + td->ival = eve->bweight; + } + +#ifdef WITH_VERSE + if(eve->vvert) { + td->verse = (void*)eve->vvert; + td->flag |= TD_VERSE_VERT; + } + else + td->flag &= ~TD_VERSE_VERT; +#endif +} + +/* *********************** CrazySpace correction. Now without doing subsurf optimal ****************** */ + +static void make_vertexcos__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s) +{ + float *vec = userData; + + vec+= 3*index; + VECCOPY(vec, co); +} + +static int modifiers_disable_subsurf_temporary(Object *ob) +{ + ModifierData *md; + int disabled = 0; + + for(md=ob->modifiers.first; md; md=md->next) + if(md->type==eModifierType_Subsurf) + if(md->mode & eModifierMode_OnCage) { + md->mode ^= eModifierMode_DisableTemporary; + disabled= 1; + } + + return disabled; +} + +/* disable subsurf temporal, get mapped cos, and enable it */ +static float *get_crazy_mapped_editverts(void) +{ + DerivedMesh *dm; + float *vertexcos; + + /* disable subsurf temporal, get mapped cos, and enable it */ + if(modifiers_disable_subsurf_temporary(G.obedit)) { + /* need to make new derivemesh */ + makeDerivedMesh(G.obedit, CD_MASK_BAREMESH); + } + + /* now get the cage */ + dm= editmesh_get_derived_cage(CD_MASK_BAREMESH); + + vertexcos= MEM_mallocN(3*sizeof(float)*G.totvert, "vertexcos map"); + dm->foreachMappedVert(dm, make_vertexcos__mapFunc, vertexcos); + + dm->release(dm); + + /* set back the flag, no new cage needs to be built, transform does it */ + modifiers_disable_subsurf_temporary(G.obedit); + + return vertexcos; +} + +#define TAN_MAKE_VEC(a, b, c) a[0]= b[0] + 0.2f*(b[0]-c[0]); a[1]= b[1] + 0.2f*(b[1]-c[1]); a[2]= b[2] + 0.2f*(b[2]-c[2]) +static void set_crazy_vertex_quat(float *quat, float *v1, float *v2, float *v3, float *def1, float *def2, float *def3) +{ + float vecu[3], vecv[3]; + float q1[4], q2[4]; + + TAN_MAKE_VEC(vecu, v1, v2); + TAN_MAKE_VEC(vecv, v1, v3); + triatoquat(v1, vecu, vecv, q1); + + TAN_MAKE_VEC(vecu, def1, def2); + TAN_MAKE_VEC(vecv, def1, def3); + triatoquat(def1, vecu, vecv, q2); + + QuatSub(quat, q2, q1); +} +#undef TAN_MAKE_VEC + +static void set_crazyspace_quats(float *origcos, float *mappedcos, float *quats) +{ + EditMesh *em = G.editMesh; + EditVert *eve, *prev; + EditFace *efa; + float *v1, *v2, *v3, *v4, *co1, *co2, *co3, *co4; + intptr_t index= 0; + + /* two abused locations in vertices */ + for(eve= em->verts.first; eve; eve= eve->next, index++) { + eve->tmp.p = NULL; + eve->prev= (EditVert *)index; + } + + /* first store two sets of tangent vectors in vertices, we derive it just from the face-edges */ + for(efa= em->faces.first; efa; efa= efa->next) { + + /* retrieve mapped coordinates */ + v1= mappedcos + 3*(intptr_t)(efa->v1->prev); + v2= mappedcos + 3*(intptr_t)(efa->v2->prev); + v3= mappedcos + 3*(intptr_t)(efa->v3->prev); + + co1= (origcos)? origcos + 3*(intptr_t)(efa->v1->prev): efa->v1->co; + co2= (origcos)? origcos + 3*(intptr_t)(efa->v2->prev): efa->v2->co; + co3= (origcos)? origcos + 3*(intptr_t)(efa->v3->prev): efa->v3->co; + + if(efa->v2->tmp.p==NULL && efa->v2->f1) { + set_crazy_vertex_quat(quats, co2, co3, co1, v2, v3, v1); + efa->v2->tmp.p= (void*)quats; + quats+= 4; + } + + if(efa->v4) { + v4= mappedcos + 3*(intptr_t)(efa->v4->prev); + co4= (origcos)? origcos + 3*(intptr_t)(efa->v4->prev): efa->v4->co; + + if(efa->v1->tmp.p==NULL && efa->v1->f1) { + set_crazy_vertex_quat(quats, co1, co2, co4, v1, v2, v4); + efa->v1->tmp.p= (void*)quats; + quats+= 4; + } + if(efa->v3->tmp.p==NULL && efa->v3->f1) { + set_crazy_vertex_quat(quats, co3, co4, co2, v3, v4, v2); + efa->v3->tmp.p= (void*)quats; + quats+= 4; + } + if(efa->v4->tmp.p==NULL && efa->v4->f1) { + set_crazy_vertex_quat(quats, co4, co1, co3, v4, v1, v3); + efa->v4->tmp.p= (void*)quats; + quats+= 4; + } + } + else { + if(efa->v1->tmp.p==NULL && efa->v1->f1) { + set_crazy_vertex_quat(quats, co1, co2, co3, v1, v2, v3); + efa->v1->tmp.p= (void*)quats; + quats+= 4; + } + if(efa->v3->tmp.p==NULL && efa->v3->f1) { + set_crazy_vertex_quat(quats, co3, co1, co2, v3, v1, v2); + efa->v3->tmp.p= (void*)quats; + quats+= 4; + } + } + } + + /* restore abused prev pointer */ + for(prev= NULL, eve= em->verts.first; eve; prev= eve, eve= eve->next) + eve->prev= prev; + +} + +void createTransBMeshVerts(TransInfo *t, BME_Mesh *bm, BME_TransData_Head *td) { + BME_Vert *v; + BME_TransData *vtd; + TransData *tob; + int i; + + tob = t->data = MEM_callocN(td->len*sizeof(TransData), "TransObData(Bevel tool)"); + + for (i=0,v=bm->verts.first;v;v=v->next) { + if ( (vtd = BME_get_transdata(td,v)) ) { + tob->loc = vtd->loc; + tob->val = &vtd->factor; + VECCOPY(tob->iloc,vtd->co); + VECCOPY(tob->center,vtd->org); + VECCOPY(tob->axismtx[0],vtd->vec); + tob->axismtx[1][0] = vtd->max ? *vtd->max : 0; + tob++; + i++; + } + } + /* since td is a memarena, it can hold more transdata than actual elements + * (i.e. we can't depend on td->len to determine the number of actual elements) */ + t->total = i; +} + +static void createTransEditVerts(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *tob = NULL; + EditMesh *em = G.editMesh; + EditVert *eve; + EditVert **nears = NULL; + EditVert *eve_act = NULL; + float *vectors = NULL, *mappedcos = NULL, *quats= NULL; + float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; + int count=0, countsel=0, a, totleft; + int propmode = t->flag & T_PROP_EDIT; + int mirror = 0; + + if ((t->context & CTX_NO_MIRROR) == 0 && (G.scene->toolsettings->editbutflag & B_MESH_X_MIRROR)) + { + mirror = 1; + } + + // transform now requires awareness for select mode, so we tag the f1 flags in verts + if(G.scene->selectmode & SCE_SELECT_VERTEX) { + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0 && (eve->f & SELECT)) + eve->f1= SELECT; + else + eve->f1= 0; + } + } + else if(G.scene->selectmode & SCE_SELECT_EDGE) { + EditEdge *eed; + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->h==0 && (eed->f & SELECT)) + eed->v1->f1= eed->v2->f1= SELECT; + } + } + else { + EditFace *efa; + for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->h==0 && (efa->f & SELECT)) { + efa->v1->f1= efa->v2->f1= efa->v3->f1= SELECT; + if(efa->v4) efa->v4->f1= SELECT; + } + } + } + + /* now we can count */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->h==0) { + if(eve->f1) countsel++; + if(propmode) count++; + } + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel==0) return; + + /* check active */ + if (G.editMesh->selected.last) { + EditSelection *ese = G.editMesh->selected.last; + if ( ese->type == EDITVERT ) { + eve_act = (EditVert *)ese->data; + } + } + + + if(propmode) { + t->total = count; + + /* allocating scratch arrays */ + vectors = (float *)MEM_mallocN(t->total * 3 * sizeof(float), "scratch vectors"); + nears = (EditVert**)MEM_mallocN(t->total * sizeof(EditVert*), "scratch nears"); + } + else t->total = countsel; + tob= t->data= MEM_callocN(t->total*sizeof(TransData), "TransObData(Mesh EditMode)"); + + Mat3CpyMat4(mtx, G.obedit->obmat); + Mat3Inv(smtx, mtx); + + if(propmode) editmesh_set_connectivity_distance(t->total, vectors, nears); + + /* detect CrazySpace [tm] */ + if(propmode==0) { + if(modifiers_getCageIndex(G.obedit, NULL)>=0) { + if(modifiers_isDeformed(G.obedit)) { + /* check if we can use deform matrices for modifier from the + start up to stack, they are more accurate than quats */ + totleft= editmesh_get_first_deform_matrices(&defmats, &defcos); + + /* if we still have more modifiers, also do crazyspace + correction with quats, relative to the coordinates after + the modifiers that support deform matrices (defcos) */ + if(totleft > 0) { + mappedcos= get_crazy_mapped_editverts(); + quats= MEM_mallocN( (t->total)*sizeof(float)*4, "crazy quats"); + set_crazyspace_quats((float*)defcos, mappedcos, quats); + if(mappedcos) + MEM_freeN(mappedcos); + } + + if(defcos) + MEM_freeN(defcos); + } + } + } + + /* find out which half we do */ + if(mirror) { + for (eve=em->verts.first; eve; eve=eve->next) { + if(eve->h==0 && eve->f1 && eve->co[0]!=0.0f) { + if(eve->co[0]<0.0f) + mirror = -1; + break; + } + } + } + + for (a=0, eve=em->verts.first; eve; eve=eve->next, a++) { + if(eve->h==0) { + if(propmode || eve->f1) { + VertsToTransData(tob, eve); + + /* selected */ + if(eve->f1) tob->flag |= TD_SELECTED; + + /* active */ + if(eve == eve_act) tob->flag |= TD_ACTIVE; + + if(propmode) { + if (eve->f2) { + float vec[3]; + VECCOPY(vec, E_VEC(eve)); + Mat3MulVecfl(mtx, vec); + tob->dist= VecLength(vec); + } + else { + tob->flag |= TD_NOTCONNECTED; + tob->dist = MAXFLOAT; + } + } + + /* CrazySpace */ + if(defmats || (quats && eve->tmp.p)) { + float mat[3][3], imat[3][3], qmat[3][3]; + + /* use both or either quat and defmat correction */ + if(quats && eve->tmp.f) { + QuatToMat3(eve->tmp.p, qmat); + + if(defmats) + Mat3MulSerie(mat, mtx, qmat, defmats[a], + NULL, NULL, NULL, NULL, NULL); + else + Mat3MulMat3(mat, mtx, qmat); + } + else + Mat3MulMat3(mat, mtx, defmats[a]); + + Mat3Inv(imat, mat); + + Mat3CpyMat3(tob->smtx, imat); + Mat3CpyMat3(tob->mtx, mat); + } + else { + Mat3CpyMat3(tob->smtx, smtx); + Mat3CpyMat3(tob->mtx, mtx); + } + + /* Mirror? */ + if( (mirror>0 && tob->iloc[0]>0.0f) || (mirror<0 && tob->iloc[0]<0.0f)) { + EditVert *vmir= editmesh_get_x_mirror_vert(G.obedit, tob->iloc); /* initializes octree on first call */ + if(vmir != eve) tob->extra = vmir; + } + tob++; + } + } + } + if (propmode) { + MEM_freeN(vectors); + MEM_freeN(nears); + } + /* crazy space free */ + if(quats) + MEM_freeN(quats); + if(defmats) + MEM_freeN(defmats); +#endif +} + +/* ********************* UV ****************** */ + +static void UVsToTransData(TransData *td, TransData2D *td2d, float *uv, int selected) +{ + float aspx, aspy; + + transform_aspect_ratio_tface_uv(&aspx, &aspy); + + /* uv coords are scaled by aspects. this is needed for rotations and + proportional editing to be consistent with the stretchted uv coords + that are displayed. this also means that for display and numinput, + and when the the uv coords are flushed, these are converted each time */ + td2d->loc[0] = uv[0]*aspx; + td2d->loc[1] = uv[1]*aspy; + td2d->loc[2] = 0.0f; + td2d->loc2d = uv; + + td->flag = 0; + td->loc = td2d->loc; + VECCOPY(td->center, td->loc); + VECCOPY(td->iloc, td->loc); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext= NULL; td->tdi= NULL; td->val= NULL; + + if(selected) { + td->flag |= TD_SELECTED; + td->dist= 0.0; + } + else { + td->dist= MAXFLOAT; + } + Mat3One(td->mtx); + Mat3One(td->smtx); +} + +static void createTransUVs(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *td = NULL; + TransData2D *td2d = NULL; + MTFace *tf; + int count=0, countsel=0; + int propmode = t->flag & T_PROP_EDIT; + int efa_s1,efa_s2,efa_s3,efa_s4; + + EditMesh *em = G.editMesh; + EditFace *efa; + + if(is_uv_tface_editing_allowed()==0) return; + + /* count */ + if (G.sima->flag & SI_BE_SQUARE && !propmode) { + for (efa= em->faces.first; efa; efa= efa->next) { + /* store face pointer for second loop, prevent second lookup */ + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (simaFaceDraw_Check(efa, tf)) { + efa->tmp.p = tf; + + efa_s1 = simaUVSel_Check(efa, tf, 0); + efa_s2 = simaUVSel_Check(efa, tf, 1); + efa_s3 = simaUVSel_Check(efa, tf, 2); + if (efa->v4) { + efa_s4 = simaUVSel_Check(efa, tf, 3); + if ( efa_s1 || efa_s2 || efa_s3 || efa_s4 ) { + countsel += 4; /* all corners of this quad need their edges moved. so we must store TD for each */ + } + } else { + /* tri's are delt with normally when SI_BE_SQUARE's enabled */ + if (efa_s1) countsel++; + if (efa_s2) countsel++; + if (efa_s3) countsel++; + } + } else { + efa->tmp.p = NULL; + } + } + } else { + for (efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (simaFaceDraw_Check(efa, tf)) { + efa->tmp.p = tf; + + if (simaUVSel_Check(efa, tf, 0)) countsel++; + if (simaUVSel_Check(efa, tf, 1)) countsel++; + if (simaUVSel_Check(efa, tf, 2)) countsel++; + if (efa->v4 && simaUVSel_Check(efa, tf, 3)) countsel++; + if(propmode) + count += (efa->v4)? 4: 3; + } else { + efa->tmp.p = NULL; + } + } + } + + /* note: in prop mode we need at least 1 selected */ + if (countsel==0) return; + + t->total= (propmode)? count: countsel; + t->data= MEM_callocN(t->total*sizeof(TransData), "TransObData(UV Editing)"); + /* for each 2d uv coord a 3d vector is allocated, so that they can be + treated just as if they were 3d verts */ + t->data2d= MEM_callocN(t->total*sizeof(TransData2D), "TransObData2D(UV Editing)"); + + if(G.sima->flag & SI_CLIP_UV) + t->flag |= T_CLIP_UV; + + td= t->data; + td2d= t->data2d; + + if (G.sima->flag & SI_BE_SQUARE && !propmode) { + for (efa= em->faces.first; efa; efa= efa->next) { + tf=(MTFace *)efa->tmp.p; + if (tf) { + efa_s1 = simaUVSel_Check(efa, tf, 0); + efa_s2 = simaUVSel_Check(efa, tf, 1); + efa_s3 = simaUVSel_Check(efa, tf, 2); + + if (efa->v4) { + efa_s4 = simaUVSel_Check(efa, tf, 3); + + if ( efa_s1 || efa_s2 || efa_s3 || efa_s4 ) { + /* all corners of this quad need their edges moved. so we must store TD for each */ + + UVsToTransData(td, td2d, tf->uv[0], efa_s1); + if (!efa_s1) td->flag |= TD_SKIP; + td++; td2d++; + + UVsToTransData(td, td2d, tf->uv[1], efa_s2); + if (!efa_s2) td->flag |= TD_SKIP; + td++; td2d++; + + UVsToTransData(td, td2d, tf->uv[2], efa_s3); + if (!efa_s3) td->flag |= TD_SKIP; + td++; td2d++; + + UVsToTransData(td, td2d, tf->uv[3], efa_s4); + if (!efa_s4) td->flag |= TD_SKIP; + td++; td2d++; + } + } else { + /* tri's are delt with normally when SI_BE_SQUARE's enabled */ + if (efa_s1) UVsToTransData(td++, td2d++, tf->uv[0], 1); + if (efa_s2) UVsToTransData(td++, td2d++, tf->uv[1], 1); + if (efa_s3) UVsToTransData(td++, td2d++, tf->uv[2], 1); + } + } + } + } else { + for (efa= em->faces.first; efa; efa= efa->next) { + /*tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (simaFaceDraw_Check(efa, tf)) {*/ + if ((tf=(MTFace *)efa->tmp.p)) { + if (propmode) { + UVsToTransData(td++, td2d++, tf->uv[0], simaUVSel_Check(efa, tf, 0)); + UVsToTransData(td++, td2d++, tf->uv[1], simaUVSel_Check(efa, tf, 1)); + UVsToTransData(td++, td2d++, tf->uv[2], simaUVSel_Check(efa, tf, 2)); + if(efa->v4) + UVsToTransData(td++, td2d++, tf->uv[3], simaUVSel_Check(efa, tf, 3)); + } else { + if(simaUVSel_Check(efa, tf, 0)) UVsToTransData(td++, td2d++, tf->uv[0], 1); + if(simaUVSel_Check(efa, tf, 1)) UVsToTransData(td++, td2d++, tf->uv[1], 1); + if(simaUVSel_Check(efa, tf, 2)) UVsToTransData(td++, td2d++, tf->uv[2], 1); + if(efa->v4 && simaUVSel_Check(efa, tf, 3)) UVsToTransData(td++, td2d++, tf->uv[3], 1); + } + } + } + } + + if (G.sima->flag & SI_LIVE_UNWRAP) + unwrap_lscm_live_begin(); +#endif +} + +void flushTransUVs(TransInfo *t) +{ +#if 0 // TRANSFORM_FIX_ME + TransData2D *td; + int a, width, height; + Object *ob= OBACT; + EditMesh *em = G.editMesh; + float aspx, aspy, invx, invy; + + transform_aspect_ratio_tface_uv(&aspx, &aspy); + transform_width_height_tface_uv(&width, &height); + invx= 1.0f/aspx; + invy= 1.0f/aspy; + + /* flush to 2d vector from internally used 3d vector */ + for(a=0, td= t->data2d; a<t->total; a++, td++) { + td->loc2d[0]= td->loc[0]*invx; + td->loc2d[1]= td->loc[1]*invy; + + if((G.sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)) { + td->loc2d[0]= (float)floor(width*td->loc2d[0] + 0.5f)/width; + td->loc2d[1]= (float)floor(height*td->loc2d[1] + 0.5f)/height; + } + } + + if((G.sima->flag & SI_BE_SQUARE) && (t->flag & T_PROP_EDIT)==0 && (t->state != TRANS_CANCEL)) + be_square_tface_uv(em); + + /* this is overkill if G.sima->lock is not set, but still needed */ + object_uvs_changed(ob); +#endif +} + +int clipUVTransform(TransInfo *t, float *vec, int resize) +{ +#if 0 // TRANSFORM_FIX_ME + TransData *td; + int a, clipx=1, clipy=1; + float aspx, aspy, min[2], max[2]; + + transform_aspect_ratio_tface_uv(&aspx, &aspy); + min[0]= min[1]= 0.0f; + max[0]= aspx; max[1]= aspy; + + for(a=0, td= t->data; a<t->total; a++, td++) { + DO_MINMAX2(td->loc, min, max); + } + + if(resize) { + if(min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < aspx*0.5f) + vec[0] *= t->center[0]/(t->center[0] - min[0]); + else if(max[0] > aspx && t->center[0] < aspx) + vec[0] *= (t->center[0] - aspx)/(t->center[0] - max[0]); + else + clipx= 0; + + if(min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < aspy*0.5f) + vec[1] *= t->center[1]/(t->center[1] - min[1]); + else if(max[1] > aspy && t->center[1] < aspy) + vec[1] *= (t->center[1] - aspy)/(t->center[1] - max[1]); + else + clipy= 0; + } + else { + if(min[0] < 0.0f) + vec[0] -= min[0]; + else if(max[0] > aspx) + vec[0] -= max[0]-aspx; + else + clipx= 0; + + if(min[1] < 0.0f) + vec[1] -= min[1]; + else if(max[1] > aspy) + vec[1] -= max[1]-aspy; + else + clipy= 0; + } + + return (clipx || clipy); +#endif +return 0; +} + +/* ********************* IPO EDITOR ************************* */ + +/* for IPO Editor transform - but actual creation of transform structures is not performed here + * due to bad globals that would need to be imported specially for this + */ +static void createTransIpoData(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + /* in editipo.c due to some globals that are defined in that file... */ + make_ipo_transdata(t); +#endif +} + +/* this function is called on recalcData to apply the transforms applied + * to the transdata on to the actual keyframe data + */ +void flushTransIpoData(TransInfo *t) +{ +#if 0 // TRANSFORM_FIX_ME + TransData2D *td; + int a; + + /* flush to 2d vector from internally used 3d vector */ + for (a=0, td= t->data2d; a<t->total; a++, td++) { + /* we need to unapply the nla-scaling from the time in some situations */ + if (NLA_IPO_SCALED) + td->loc2d[0]= get_action_frame(OBACT, td->loc[0]); + else + td->loc2d[0]= td->loc[0]; + + /* when the icu that point comes from is a bitflag holder, don't allow adjusting values */ + if ((t->data[a].flag & TD_TIMEONLY)==0) + td->loc2d[1]= td->loc[1]; + } +#endif +} + +/* ********************* ACTION/NLA EDITOR ****************** */ + +/* Called by special_aftertrans_update to make sure selected gp-frames replace + * any other gp-frames which may reside on that frame (that are not selected). + * It also makes sure gp-frames are still stored in chronological order after + * transform. + */ +static void posttrans_gpd_clean (bGPdata *gpd) +{ + bGPDlayer *gpl; + + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + ListBase sel_buffer = {NULL, NULL}; + bGPDframe *gpf, *gpfn; + bGPDframe *gfs, *gfsn; + + /* loop 1: loop through and isolate selected gp-frames to buffer + * (these need to be sorted as they are isolated) + */ + for (gpf= gpl->frames.first; gpf; gpf= gpfn) { + short added= 0; + gpfn= gpf->next; + + if (gpf->flag & GP_FRAME_SELECT) { + BLI_remlink(&gpl->frames, gpf); + + /* find place to add them in buffer + * - go backwards as most frames will still be in order, + * so doing it this way will be faster + */ + for (gfs= sel_buffer.last; gfs; gfs= gfs->prev) { + /* if current (gpf) occurs after this one in buffer, add! */ + if (gfs->framenum < gpf->framenum) { + BLI_insertlinkafter(&sel_buffer, gfs, gpf); + added= 1; + break; + } + } + if (added == 0) + BLI_addhead(&sel_buffer, gpf); + } + } + + /* error checking: it is unlikely, but may be possible to have none selected */ + if (sel_buffer.first == NULL) + continue; + + /* if all were selected (i.e. gpl->frames is empty), then just transfer sel-buf over */ + if (gpl->frames.first == NULL) { + gpl->frames.first= sel_buffer.first; + gpl->frames.last= sel_buffer.last; + + continue; + } + + /* loop 2: remove duplicates of frames in buffers */ + for (gpf= gpl->frames.first; gpf && sel_buffer.first; gpf= gpfn) { + gpfn= gpf->next; + + /* loop through sel_buffer, emptying stuff from front of buffer if ok */ + for (gfs= sel_buffer.first; gfs && gpf; gfs= gfsn) { + gfsn= gfs->next; + + /* if this buffer frame needs to go before current, add it! */ + if (gfs->framenum < gpf->framenum) { + /* transfer buffer frame to frames list (before current) */ + BLI_remlink(&sel_buffer, gfs); + BLI_insertlinkbefore(&gpl->frames, gpf, gfs); + } + /* if this buffer frame is on same frame, replace current with it and stop */ + else if (gfs->framenum == gpf->framenum) { + /* transfer buffer frame to frames list (before current) */ + BLI_remlink(&sel_buffer, gfs); + BLI_insertlinkbefore(&gpl->frames, gpf, gfs); + + /* get rid of current frame */ + // TRANSFORM_FIX_ME + //gpencil_layer_delframe(gpl, gpf); + } + } + } + + /* if anything is still in buffer, append to end */ + for (gfs= sel_buffer.first; gfs; gfs= gfsn) { + gfsn= gfs->next; + + BLI_remlink(&sel_buffer, gfs); + BLI_addtail(&gpl->frames, gfs); + } + } +} + +/* Called by special_aftertrans_update to make sure selected keyframes replace + * any other keyframes which may reside on that frame (that is not selected). + */ +static void posttrans_ipo_clean (Ipo *ipo) +{ +#if 0 // TRANSFORM_FIX_ME + IpoCurve *icu; + int i; + + /* delete any keyframes that occur on same frame as selected keyframe, but is not selected */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + float *selcache; /* cache for frame numbers of selected frames (icu->totvert*sizeof(float)) */ + int len, index; /* number of frames in cache, item index */ + + /* allocate memory for the cache */ + // TODO: investigate using GHash for this instead? + if (icu->totvert == 0) + continue; + selcache= MEM_callocN(sizeof(float)*icu->totvert, "IcuSelFrameNums"); + len= 0; + index= 0; + + /* We do 2 loops, 1 for marking keyframes for deletion, one for deleting + * as there is no guarantee what order the keyframes are exactly, even though + * they have been sorted by time. + */ + + /* Loop 1: find selected keyframes */ + for (i = 0; i < icu->totvert; i++) { + BezTriple *bezt= &icu->bezt[i]; + + if (BEZSELECTED(bezt)) { + selcache[index]= bezt->vec[1][0]; + index++; + len++; + } + } + + /* Loop 2: delete unselected keyframes on the same frames (if any keyframes were found) */ + if (len) { + for (i = 0; i < icu->totvert; i++) { + BezTriple *bezt= &icu->bezt[i]; + + if (BEZSELECTED(bezt) == 0) { + /* check beztriple should be removed according to cache */ + for (index= 0; index < len; index++) { + if (IS_EQ(bezt->vec[1][0], selcache[index])) { + delete_icu_key(icu, i, 0); + break; + } + else if (bezt->vec[1][0] > selcache[index]) + break; + } + } + } + + testhandles_ipocurve(icu); + } + + /* free cache */ + MEM_freeN(selcache); + } +#endif +} + +/* Called by special_aftertrans_update to make sure selected keyframes replace + * any other keyframes which may reside on that frame (that is not selected). + * remake_action_ipos should have already been called + */ +static void posttrans_action_clean (bAction *act) +{ +#if 0 // TRANSFORM_FIX_ME + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + int filter; + + /* filter data */ + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + actdata_filter(&act_data, filter, act, ACTCONT_ACTION); + + /* loop through relevant data, removing keyframes from the ipo-blocks that were attached + * - all keyframes are converted in/out of global time + */ + for (ale= act_data.first; ale; ale= ale->next) { + if (NLA_ACTION_SCALED) { + actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); + posttrans_ipo_clean(ale->key_data); + actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); + } + else + posttrans_ipo_clean(ale->key_data); + } + + /* free temp data */ + BLI_freelistN(&act_data); +#endif +} + +/* Called by special_aftertrans_update to make sure selected keyframes replace + * any other keyframes which may reside on that frame (that is not selected). + * remake_all_ipos should have already been called + */ +static void posttrans_nla_clean (TransInfo *t) +{ +#if 0 // TRANSFORM_FIX_ME + Base *base; + Object *ob; + bActionStrip *strip; + bActionChannel *achan; + bConstraintChannel *conchan; + float cfra; + char side; + int i; + + /* which side of the current frame should be allowed */ + if (t->mode == TFM_TIME_EXTEND) { + /* only side on which mouse is gets transformed */ + float xmouse, ymouse; + + areamouseco_to_ipoco(G.v2d, t->imval, &xmouse, &ymouse); + side = (xmouse > CFRA) ? 'R' : 'L'; + } + else { + /* normal transform - both sides of current frame are considered */ + side = 'B'; + } + + /* only affect keyframes */ + for (base=G.scene->base.first; base; base=base->next) { + ob= base->object; + + /* Check object ipos */ + i= count_ipo_keys(ob->ipo, side, (float)CFRA); + if (i) posttrans_ipo_clean(ob->ipo); + + /* Check object constraint ipos */ + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) { + i= count_ipo_keys(conchan->ipo, side, (float)CFRA); + if (i) posttrans_ipo_clean(conchan->ipo); + } + + /* skip actions and nlastrips if object is collapsed */ + if (ob->nlaflag & OB_NLA_COLLAPSED) + continue; + + /* Check action ipos */ + if (ob->action) { + /* exclude if strip is selected too */ + for (strip=ob->nlastrips.first; strip; strip=strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + if (strip->act == ob->action) + break; + } + } + if (strip==NULL) { + cfra = get_action_frame(ob, (float)CFRA); + + for (achan=ob->action->chanbase.first; achan; achan=achan->next) { + if (EDITABLE_ACHAN(achan)) { + i= count_ipo_keys(achan->ipo, side, cfra); + if (i) { + actstrip_map_ipo_keys(ob, achan->ipo, 0, 1); + posttrans_ipo_clean(achan->ipo); + actstrip_map_ipo_keys(ob, achan->ipo, 1, 1); + } + + /* Check action constraint ipos */ + if (EXPANDED_ACHAN(achan) && FILTER_CON_ACHAN(achan)) { + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) { + if (EDITABLE_CONCHAN(conchan)) { + i = count_ipo_keys(conchan->ipo, side, cfra); + if (i) { + actstrip_map_ipo_keys(ob, conchan->ipo, 0, 1); + posttrans_ipo_clean(conchan->ipo); + actstrip_map_ipo_keys(ob, conchan->ipo, 1, 1); + } + } + } + } + } + } + } + } + } +#endif +} + +/* ----------------------------- */ + +/* This function tests if a point is on the "mouse" side of the cursor/frame-marking */ +static short FrameOnMouseSide(char side, float frame, float cframe) +{ + /* both sides, so it doesn't matter */ + if (side == 'B') return 1; + + /* only on the named side */ + if (side == 'R') + return (frame >= cframe) ? 1 : 0; + else + return (frame <= cframe) ? 1 : 0; +} + +/* fully select selected beztriples, but only include if it's on the right side of cfra */ +static int count_ipo_keys(Ipo *ipo, char side, float cfra) +{ + IpoCurve *icu; + BezTriple *bezt; + int i, count = 0; + + if (ipo == NULL) + return count; + + /* only include points that occur on the right side of cfra */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) { + if (bezt->f2 & SELECT) { + /* fully select the other two keys */ + bezt->f1 |= SELECT; + bezt->f3 |= SELECT; + + /* increment by 3, as there are 3 points (3 * x-coordinates) that need transform */ + if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) + count += 3; + } + } + } + + return count; +} + +/* fully select selected beztriples, but only include if it's on the right side of cfra */ +static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) +{ + bGPDframe *gpf; + int count = 0; + + if (gpl == NULL) + return count; + + /* only include points that occur on the right side of cfra */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) + count++; + } + } + + return count; +} + +/* This function assigns the information to transdata */ +static void TimeToTransData(TransData *td, float *time, Object *ob) +{ + /* memory is calloc'ed, so that should zero everything nicely for us */ + td->val = time; + td->ival = *(time); + + /* store the Object where this keyframe exists as a keyframe of the + * active action as td->ob. Usually, this member is only used for constraints + * drawing + */ + td->ob= ob; +} + +/* This function advances the address to which td points to, so it must return + * the new address so that the next time new transform data is added, it doesn't + * overwrite the existing ones... i.e. td = IpoToTransData(td, ipo, ob, side, cfra); + * + * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data + * on the named side are used. + */ +static TransData *IpoToTransData(TransData *td, Ipo *ipo, Object *ob, char side, float cfra) +{ +#if 0 // TRANSFORM_FIX_ME + IpoCurve *icu; + BezTriple *bezt; + int i; + + if (ipo == NULL) + return td; + + for (icu= ipo->curve.first; icu; icu= icu->next) { + for (i=0, bezt=icu->bezt; i < icu->totvert; i++, bezt++) { + /* only add selected keyframes (for now, proportional edit is not enabled) */ + if (BEZSELECTED(bezt)) { + /* only add if on the right 'side' of the current frame */ + if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { + /* each control point needs to be added separetely */ + TimeToTransData(td, bezt->vec[0], ob); + td++; + + TimeToTransData(td, bezt->vec[1], ob); + td++; + + TimeToTransData(td, bezt->vec[2], ob); + td++; + } + } + } + } + + return td; +#endif +return NULL; +} + +/* helper struct for gp-frame transforms (only used here) */ +typedef struct tGPFtransdata { + float val; /* where transdata writes transform */ + int *sdata; /* pointer to gpf->framenum */ +} tGPFtransdata; + +/* This function helps flush transdata written to tempdata into the gp-frames */ +void flushTransGPactionData (TransInfo *t) +{ + tGPFtransdata *tfd; + int i; + + /* find the first one to start from */ + if (t->mode == TFM_TIME_SLIDE) + tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 ); + else + tfd= (tGPFtransdata *)(t->customData); + + /* flush data! */ + for (i = 0; i < t->total; i++, tfd++) { + *(tfd->sdata)= (int)floor(tfd->val + 0.5); + } +} + +/* This function advances the address to which td points to, so it must return + * the new address so that the next time new transform data is added, it doesn't + * overwrite the existing ones... i.e. td = GPLayerToTransData(td, ipo, ob, side, cfra); + * + * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data + * on the named side are used. + */ +static int GPLayerToTransData (TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra) +{ + bGPDframe *gpf; + int count= 0; + + /* check for select frames on right side of current frame */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { + /* memory is calloc'ed, so that should zero everything nicely for us */ + td->val= &tfd->val; + td->ival= (float)gpf->framenum; + + tfd->val= (float)gpf->framenum; + tfd->sdata= &gpf->framenum; + + /* advance td now */ + td++; + tfd++; + count++; + } + } + } + + return count; +} + +static void createTransActionData(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + TransData *td = NULL; + tGPFtransdata *tfd = NULL; + Object *ob= NULL; + + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + void *data; + short datatype; + int filter; + + int count=0; + float cfra; + char side; + + /* determine what type of data we are operating on */ + data = get_action_context(&datatype); + if (data == NULL) return; + + /* filter data */ + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + actdata_filter(&act_data, filter, data, datatype); + + /* is the action scaled? if so, the it should belong to the active object */ + if (NLA_ACTION_SCALED) + ob= OBACT; + + /* which side of the current frame should be allowed */ + if (t->mode == TFM_TIME_EXTEND) { + /* only side on which mouse is gets transformed */ + float xmouse, ymouse; + + areamouseco_to_ipoco(G.v2d, t->imval, &xmouse, &ymouse); + side = (xmouse > CFRA) ? 'R' : 'L'; + } + else { + /* normal transform - both sides of current frame are considered */ + side = 'B'; + } + + /* convert current-frame to action-time (slightly less accurate, espcially under + * higher scaling ratios, but is faster than converting all points) + */ + if (ob) + cfra = get_action_frame(ob, (float)CFRA); + else + cfra = (float)CFRA; + + /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ + for (ale= act_data.first; ale; ale= ale->next) { + if (ale->type == ACTTYPE_GPLAYER) + count += count_gplayer_frames(ale->data, side, cfra); + else + count += count_ipo_keys(ale->key_data, side, cfra); + } + + /* stop if trying to build list if nothing selected */ + if (count == 0) { + /* cleanup temp list */ + BLI_freelistN(&act_data); + return; + } + + /* allocate memory for data */ + t->total= count; + + t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(Action Editor)"); + td= t->data; + + if (datatype == ACTCONT_GPENCIL) { + if (t->mode == TFM_TIME_SLIDE) { + t->customData= MEM_callocN((sizeof(float)*2)+(sizeof(tGPFtransdata)*count), "TimeSlide + tGPFtransdata"); + tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 ); + } + else { + t->customData= MEM_callocN(sizeof(tGPFtransdata)*count, "tGPFtransdata"); + tfd= (tGPFtransdata *)(t->customData); + } + } + else if (t->mode == TFM_TIME_SLIDE) + t->customData= MEM_callocN(sizeof(float)*2, "TimeSlide Min/Max"); + + /* loop 2: build transdata array */ + for (ale= act_data.first; ale; ale= ale->next) { + if (ale->type == ACTTYPE_GPLAYER) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + int i; + + i = GPLayerToTransData(td, tfd, gpl, side, cfra); + td += i; + tfd += i; + } + else { + Ipo *ipo= (Ipo *)ale->key_data; + + td= IpoToTransData(td, ipo, ob, side, cfra); + } + } + + /* check if we're supposed to be setting minx/maxx for TimeSlide */ + if (t->mode == TFM_TIME_SLIDE) { + float min=999999999.0f, max=-999999999.0f; + int i; + + td= (t->data + 1); + for (i=1; i < count; i+=3, td+=3) { + if (min > *(td->val)) min= *(td->val); + if (max < *(td->val)) max= *(td->val); + } + + /* minx/maxx values used by TimeSlide are stored as a + * calloced 2-float array in t->customData. This gets freed + * in postTrans (T_FREE_CUSTOMDATA). + */ + *((float *)(t->customData)) = min; + *((float *)(t->customData) + 1) = max; + } + + /* cleanup temp list */ + BLI_freelistN(&act_data); +#endif +} + +static void createTransNlaData(bContext *C, TransInfo *t) +{ + // TRANSFORM_FIX_ME +#if 0 + Base *base; + bActionStrip *strip; + bActionChannel *achan; + bConstraintChannel *conchan; + + TransData *td = NULL; + int count=0, i; + float cfra; + char side; + + /* which side of the current frame should be allowed */ + if (t->mode == TFM_TIME_EXTEND) { + /* only side on which mouse is gets transformed */ + float xmouse, ymouse; + + areamouseco_to_ipoco(G.v2d, t->imval, &xmouse, &ymouse); + side = (xmouse > CFRA) ? 'R' : 'L'; + } + else { + /* normal transform - both sides of current frame are considered */ + side = 'B'; + } + + /* Ensure that partial selections result in beztriple selections */ + for (base=G.scene->base.first; base; base=base->next) { + /* Check object ipos */ + i= count_ipo_keys(base->object->ipo, side, (float)CFRA); + if (i) base->flag |= BA_HAS_RECALC_OB; + count += i; + + /* Check object constraint ipos */ + for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) + count += count_ipo_keys(conchan->ipo, side, (float)CFRA); + + /* skip actions and nlastrips if object is collapsed */ + if (base->object->nlaflag & OB_NLA_COLLAPSED) + continue; + + /* Check action ipos */ + if (base->object->action) { + /* exclude if strip is selected too */ + for (strip=base->object->nlastrips.first; strip; strip=strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + if (strip->act == base->object->action) + break; + } + } + if (strip==NULL) { + cfra = get_action_frame(base->object, (float)CFRA); + + for (achan=base->object->action->chanbase.first; achan; achan=achan->next) { + if (EDITABLE_ACHAN(achan)) { + i= count_ipo_keys(achan->ipo, side, cfra); + if (i) base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA; + count += i; + + /* Check action constraint ipos */ + if (EXPANDED_ACHAN(achan) && FILTER_CON_ACHAN(achan)) { + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) { + if (EDITABLE_CONCHAN(conchan)) + count += count_ipo_keys(conchan->ipo, side, cfra); + } + } + } + } + } + } + + /* Check nlastrips */ + for (strip=base->object->nlastrips.first; strip; strip=strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + base->flag |= BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA; + + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) count++; + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) count++; + } + } + } + + /* If nothing is selected, bail out */ + if (count == 0) + return; + + /* allocate memory for data */ + t->total= count; + t->data= MEM_callocN(t->total*sizeof(TransData), "TransData (NLA Editor)"); + + /* build the transdata structure */ + td= t->data; + for (base=G.scene->base.first; base; base=base->next) { + /* Manipulate object ipos */ + /* - no scaling of keyframe times is allowed here */ + td= IpoToTransData(td, base->object->ipo, NULL, side, (float)CFRA); + + /* Manipulate object constraint ipos */ + /* - no scaling of keyframe times is allowed here */ + for (conchan=base->object->constraintChannels.first; conchan; conchan=conchan->next) + td= IpoToTransData(td, conchan->ipo, NULL, side, (float)CFRA); + + /* skip actions and nlastrips if object collapsed */ + if (base->object->nlaflag & OB_NLA_COLLAPSED) + continue; + + /* Manipulate action ipos */ + if (base->object->action) { + /* exclude if strip that active action belongs to is selected too */ + for (strip=base->object->nlastrips.first; strip; strip=strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + if (strip->act == base->object->action) + break; + } + } + + /* can include if no strip found */ + if (strip==NULL) { + cfra = get_action_frame(base->object, (float)CFRA); + + for (achan=base->object->action->chanbase.first; achan; achan=achan->next) { + if (EDITABLE_ACHAN(achan)) { + td= IpoToTransData(td, achan->ipo, base->object, side, cfra); + + /* Manipulate action constraint ipos */ + if (EXPANDED_ACHAN(achan) && FILTER_CON_ACHAN(achan)) { + for (conchan=achan->constraintChannels.first; conchan; conchan=conchan->next) { + if (EDITABLE_CONCHAN(conchan)) + td= IpoToTransData(td, conchan->ipo, base->object, side, cfra); + } + } + } + } + } + } + + /* Manipulate nlastrips */ + for (strip=base->object->nlastrips.first; strip; strip=strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + /* first TransData is the start, second is the end */ + if (FrameOnMouseSide(side, strip->start, (float)CFRA)) { + td->val = &strip->start; + td->ival = strip->start; + td++; + } + if (FrameOnMouseSide(side, strip->end, (float)CFRA)) { + td->val = &strip->end; + td->ival = strip->end; + td++; + } + } + } + } +#endif +} + +/* **************** IpoKey stuff, for Object TransData ********** */ + +/* storage of bezier triple. thats why -3 and +3! */ +static void set_tdi_old(float *old, float *poin) +{ + old[0]= *(poin); + old[3]= *(poin-3); + old[6]= *(poin+3); +} + +/* while transforming */ +void add_tdi_poin(float *poin, float *old, float delta) +{ + if(poin) { + poin[0]= old[0]+delta; + poin[-3]= old[3]+delta; + poin[3]= old[6]+delta; + } +} + +/* fill ipokey transdata with old vals and pointers */ +#if 0 // TRANSFORM_FIX_ME +static void ipokey_to_transdata(IpoKey *ik, TransData *td) +{ + extern int ob_ar[]; // blenkernel ipo.c + TransDataIpokey *tdi= td->tdi; + BezTriple *bezt; + int a, delta= 0; + + td->val= NULL; // is read on ESC + + for(a=0; a<OB_TOTIPO; a++) { + if(ik->data[a]) { + bezt= ik->data[a]; + + switch( ob_ar[a] ) { + case OB_LOC_X: + case OB_DLOC_X: + tdi->locx= &(bezt->vec[1][1]); break; + case OB_LOC_Y: + case OB_DLOC_Y: + tdi->locy= &(bezt->vec[1][1]); break; + case OB_LOC_Z: + case OB_DLOC_Z: + tdi->locz= &(bezt->vec[1][1]); break; + + case OB_DROT_X: + delta= 1; + case OB_ROT_X: + tdi->rotx= &(bezt->vec[1][1]); break; + case OB_DROT_Y: + delta= 1; + case OB_ROT_Y: + tdi->roty= &(bezt->vec[1][1]); break; + case OB_DROT_Z: + delta= 1; + case OB_ROT_Z: + tdi->rotz= &(bezt->vec[1][1]); break; + + case OB_SIZE_X: + case OB_DSIZE_X: + tdi->sizex= &(bezt->vec[1][1]); break; + case OB_SIZE_Y: + case OB_DSIZE_Y: + tdi->sizey= &(bezt->vec[1][1]); break; + case OB_SIZE_Z: + case OB_DSIZE_Z: + tdi->sizez= &(bezt->vec[1][1]); break; + } + } + } + + /* oldvals for e.g. undo */ + if(tdi->locx) set_tdi_old(tdi->oldloc, tdi->locx); + if(tdi->locy) set_tdi_old(tdi->oldloc+1, tdi->locy); + if(tdi->locz) set_tdi_old(tdi->oldloc+2, tdi->locz); + + /* remember, for mapping curves ('1'=10 degrees) */ + if(tdi->rotx) set_tdi_old(tdi->oldrot, tdi->rotx); + if(tdi->roty) set_tdi_old(tdi->oldrot+1, tdi->roty); + if(tdi->rotz) set_tdi_old(tdi->oldrot+2, tdi->rotz); + + /* this is not allowed to be dsize! */ + if(tdi->sizex) set_tdi_old(tdi->oldsize, tdi->sizex); + if(tdi->sizey) set_tdi_old(tdi->oldsize+1, tdi->sizey); + if(tdi->sizez) set_tdi_old(tdi->oldsize+2, tdi->sizez); + + tdi->flag= TOB_IPO; + if(delta) tdi->flag |= TOB_IPODROT; +} +#endif + +/* *************************** Object Transform data ******************* */ + +/* Little helper function for ObjectToTransData used to give certain + * constraints (ChildOf, FollowPath, and others that may be added) + * inverse corrections for transform, so that they aren't in CrazySpace. + * These particular constraints benefit from this, but others don't, hence + * this semi-hack ;-) - Aligorith + */ +static short constraints_list_needinv(TransInfo *t, ListBase *list) +{ + bConstraint *con; + + /* loop through constraints, checking if there's one of the mentioned + * constraints needing special crazyspace corrections + */ + if (list) { + for (con= list->first; con; con=con->next) { + /* only consider constraint if it is enabled, and has influence on result */ + if ((con->flag & CONSTRAINT_DISABLE)==0 && (con->enforce!=0.0)) { + /* (affirmative) returns for specific constraints here... */ + /* constraints that require this regardless */ + if (con->type == CONSTRAINT_TYPE_CHILDOF) return 1; + if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) return 1; + if (con->type == CONSTRAINT_TYPE_CLAMPTO) return 1; + + /* constraints that require this only under special conditions */ + if (con->type == CONSTRAINT_TYPE_ROTLIKE) { + /* CopyRot constraint only does this when rotating, and offset is on */ + bRotateLikeConstraint *data = (bRotateLikeConstraint *)con->data; + + if ((data->flag & ROTLIKE_OFFSET) && (t->mode == TFM_ROTATION)) + return 1; + } + } + } + } + + /* no appropriate candidates found */ + return 0; +} + +/* transcribe given object into TransData for Transforming */ +static void ObjectToTransData(bContext *C, TransInfo *t, TransData *td, Object *ob) +{ + Scene *scene = CTX_data_scene(C); + Object *track; + ListBase fakecons = {NULL, NULL}; + float obmtx[3][3]; + short constinv; + short skip_invert = 0; + + /* axismtx has the real orientation */ + Mat3CpyMat4(td->axismtx, ob->obmat); + Mat3Ortho(td->axismtx); + + td->con= ob->constraints.first; + + /* hack: tempolarily disable tracking and/or constraints when getting + * object matrix, if tracking is on, or if constraints don't need + * inverse correction to stop it from screwing up space conversion + * matrix later + */ + constinv = constraints_list_needinv(t, &ob->constraints); + + /* disable constraints inversion for dummy pass */ + if (t->mode == TFM_DUMMY) + skip_invert = 1; + + if (skip_invert == 0 && (ob->track || constinv==0)) { + track= ob->track; + ob->track= NULL; + + if (constinv == 0) { + fakecons.first = ob->constraints.first; + fakecons.last = ob->constraints.last; + ob->constraints.first = ob->constraints.last = NULL; + } + + where_is_object(ob); + + if (constinv == 0) { + ob->constraints.first = fakecons.first; + ob->constraints.last = fakecons.last; + } + + ob->track= track; + } + else + where_is_object(ob); + + td->ob = ob; + + td->loc = ob->loc; + VECCOPY(td->iloc, td->loc); + + td->ext->rot = ob->rot; + VECCOPY(td->ext->irot, ob->rot); + VECCOPY(td->ext->drot, ob->drot); + + td->ext->size = ob->size; + VECCOPY(td->ext->isize, ob->size); + VECCOPY(td->ext->dsize, ob->dsize); + + VECCOPY(td->center, ob->obmat[3]); + + Mat4CpyMat4(td->ext->obmat, ob->obmat); + + /* is there a need to set the global<->data space conversion matrices? */ + if (ob->parent || constinv) { + float totmat[3][3], obinv[3][3]; + + /* Get the effect of parenting, and/or certain constraints. + * NOTE: some Constraints, and also Tracking should never get this + * done, as it doesn't work well. + */ + object_to_mat3(ob, obmtx); + Mat3CpyMat4(totmat, ob->obmat); + Mat3Inv(obinv, totmat); + Mat3MulMat3(td->smtx, obmtx, obinv); + Mat3Inv(td->mtx, td->smtx); + } + else { + /* no conversion to/from dataspace */ + Mat3One(td->smtx); + Mat3One(td->mtx); + } + + /* set active flag */ + if (ob == OBACT) + { + td->flag |= TD_ACTIVE; + } + +#ifdef WITH_VERSE + if(ob->vnode) { + td->verse = (void*)ob; + td->flag |= TD_VERSE_OBJECT; + } + else + td->flag &= ~TD_VERSE_OBJECT; +#endif +} + + +/* sets flags in Bases to define whether they take part in transform */ +/* it deselects Bases, so we have to call the clear function always after */ +static void set_trans_object_base_flags(bContext *C, TransInfo *t) +{ + Scene *sce = CTX_data_scene(C); + View3D *v3d = t->view; + + /* + if Base selected and has parent selected: + base->flag= BA_WAS_SEL + */ + Base *base; + + /* don't do it if we're not actually going to recalculate anything */ + if(t->mode == TFM_DUMMY) + return; + + /* makes sure base flags and object flags are identical */ + copy_baseflags(); + + /* handle pending update events, otherwise they got copied below */ + for (base= sce->base.first; base; base= base->next) { + if(base->object->recalc) + object_handle_update(base->object); + } + + for (base= sce->base.first; base; base= base->next) { + base->flag &= ~BA_WAS_SEL; + + if(TESTBASELIB(v3d, base)) { + Object *ob= base->object; + Object *parsel= ob->parent; + + /* if parent selected, deselect */ + while(parsel) { + if(parsel->flag & SELECT) break; + parsel= parsel->parent; + } + + if(parsel) + { + if (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL) + { + base->flag |= BA_TRANSFORM_CHILD; + } + else + { + base->flag &= ~SELECT; + base->flag |= BA_WAS_SEL; + } + } + /* used for flush, depgraph will change recalcs if needed :) */ + ob->recalc |= OB_RECALC_OB; + } + } + + /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ + DAG_scene_flush_update(G.scene, -1, 0); + + /* and we store them temporal in base (only used for transform code) */ + /* this because after doing updates, the object->recalc is cleared */ + for (base= sce->base.first; base; base= base->next) { + if(base->object->recalc & OB_RECALC_OB) + base->flag |= BA_HAS_RECALC_OB; + if(base->object->recalc & OB_RECALC_DATA) + base->flag |= BA_HAS_RECALC_DATA; + } +} + +static void clear_trans_object_base_flags(TransInfo *t) +{ + Scene *sce = t->scene; + Base *base; + + for (base= sce->base.first; base; base = base->next) + { + if(base->flag & BA_WAS_SEL) + base->flag |= SELECT; + + base->flag &= ~(BA_WAS_SEL|BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA|BA_DO_IPO|BA_TRANSFORM_CHILD); + } +} + +/* auto-keyframing feature - checks for whether anything should be done for the current frame */ +short autokeyframe_cfra_can_key(Object *ob) +{ +#if 0 // TRANSFORM_FIX_ME + ListBase keys = {NULL, NULL}; + ActKeyColumn *ak; + float cfra; + short found= 0; + + /* only filter if auto-key mode requires this */ + if (IS_AUTOKEY_ON == 0) + return 0; + else if (IS_AUTOKEY_MODE(NORMAL)) + return 1; + + /* sanity check */ + if (ob == NULL) + return 0; + + /* get keyframes that object has (bone anim is stored on ob too) */ + if (ob->action) + action_to_keylist(ob->action, &keys, NULL, NULL); + else if (ob->ipo) + ipo_to_keylist(ob->ipo, &keys, NULL, NULL); + else + return 0; + + /* get current frame (will apply nla-scaling as necessary) */ + // ack... this is messy... + cfra= frame_to_float(CFRA); + cfra= get_action_frame(ob, cfra); + + /* check if a keyframe occurs on current frame */ + for (ak= keys.first; ak; ak= ak->next) { + if (IS_EQ(cfra, ak->cfra)) { + found= 1; + break; + } + } + + /* free temp list */ + BLI_freelistN(&keys); + + return found; +#endif +return 0; +} + +/* auto-keyframing feature - for objects + * tmode: should be a transform mode + */ +void autokeyframe_ob_cb_func(Object *ob, int tmode) +{ +#if 0 // TRANSFORM_FIX_ME + ID *id= (ID *)(ob); + IpoCurve *icu; + + if (autokeyframe_cfra_can_key(ob)) { + char *actname = NULL; + short flag = 0; + + if (ob->ipoflag & OB_ACTION_OB) + actname= "Object"; + + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) + flag |= INSERTKEY_NEEDED; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) + flag |= INSERTKEY_MATRIX; + + if (IS_AUTOKEY_FLAG(INSERTAVAIL)) { + /* only key on available channels */ + if ((ob->ipo) || (ob->action)) { + if (ob->action && actname) { + bActionChannel *achan; + achan= get_action_channel(ob->action, actname); + + if (achan && achan->ipo) + icu= achan->ipo->curve.first; + else + icu= NULL; + } + else + icu= ob->ipo->curve.first; + + for (; icu; icu= icu->next) { + icu->flag &= ~IPO_SELECT; + insertkey(id, ID_OB, actname, NULL, icu->adrcode, flag); + } + } + } + else if (IS_AUTOKEY_FLAG(INSERTNEEDED)) { + short doLoc=0, doRot=0, doScale=0; + + /* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */ + if (tmode == TFM_TRANSLATION) { + doLoc = 1; + } + else if (tmode == TFM_ROTATION) { + if (G.vd->around == V3D_ACTIVE) { + if (ob != OBACT) + doLoc = 1; + } + else if (G.vd->around == V3D_CURSOR) + doLoc = 1; + + if ((G.vd->flag & V3D_ALIGN)==0) + doRot = 1; + } + else if (tmode == TFM_RESIZE) { + if (G.vd->around == V3D_ACTIVE) { + if (ob != OBACT) + doLoc = 1; + } + else if (G.vd->around == V3D_CURSOR) + doLoc = 1; + + if ((G.vd->flag & V3D_ALIGN)==0) + doScale = 1; + } + + if (doLoc) { + insertkey(id, ID_OB, actname, NULL, OB_LOC_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_LOC_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_LOC_Z, flag); + } + if (doRot) { + insertkey(id, ID_OB, actname, NULL, OB_ROT_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_ROT_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_ROT_Z, flag); + } + if (doScale) { + insertkey(id, ID_OB, actname, NULL, OB_SIZE_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_SIZE_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_SIZE_Z, flag); + } + } + else { + insertkey(id, ID_OB, actname, NULL, OB_LOC_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_LOC_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_LOC_Z, flag); + + insertkey(id, ID_OB, actname, NULL, OB_ROT_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_ROT_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_ROT_Z, flag); + + insertkey(id, ID_OB, actname, NULL, OB_SIZE_X, flag); + insertkey(id, ID_OB, actname, NULL, OB_SIZE_Y, flag); + insertkey(id, ID_OB, actname, NULL, OB_SIZE_Z, flag); + } + + remake_object_ipos(ob); + allqueue(REDRAWMARKER, 0); + allqueue(REDRAWOOPS, 0); + } +#endif +} + +/* auto-keyframing feature - for poses/pose-channels + * tmode: should be a transform mode + * targetless_ik: has targetless ik been done on any channels? + */ +void autokeyframe_pose_cb_func(Object *ob, int tmode, short targetless_ik) +{ +#if 0 // TRANSFORM_FIX_ME + ID *id= (ID *)(ob); + bArmature *arm= ob->data; + bAction *act; + bPose *pose; + bPoseChannel *pchan; + IpoCurve *icu; + + pose= ob->pose; + act= ob->action; + + if (autokeyframe_cfra_can_key(ob)) { + short flag= 0; + + if (act == NULL) + act= ob->action= add_empty_action("Action"); + + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) + flag |= INSERTKEY_NEEDED; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) + flag |= INSERTKEY_MATRIX; + + for (pchan=pose->chanbase.first; pchan; pchan=pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { + /* clear any 'unkeyed' flag it may have */ + pchan->bone->flag &= ~BONE_UNKEYED; + + /* only insert into available channels? */ + if (IS_AUTOKEY_FLAG(INSERTAVAIL)) { + bActionChannel *achan; + + achan= get_action_channel(act, pchan->name); + if (achan && achan->ipo) { + for (icu= achan->ipo->curve.first; icu; icu= icu->next) + insertkey(id, ID_PO, pchan->name, NULL, icu->adrcode, flag); + } + } + /* only insert keyframe if needed? */ + else if (IS_AUTOKEY_FLAG(INSERTNEEDED)) { + short doLoc=0, doRot=0, doScale=0; + + /* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */ + if (tmode == TFM_TRANSLATION) { + if (targetless_ik) + doRot= 1; + else + doLoc = 1; + } + else if (tmode == TFM_ROTATION) { + if (ELEM(G.vd->around, V3D_CURSOR, V3D_ACTIVE)) + doLoc = 1; + + if ((G.vd->flag & V3D_ALIGN)==0) + doRot = 1; + } + else if (tmode == TFM_RESIZE) { + if (ELEM(G.vd->around, V3D_CURSOR, V3D_ACTIVE)) + doLoc = 1; + + if ((G.vd->flag & V3D_ALIGN)==0) + doScale = 1; + } + + if (doLoc) { + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z, flag); + } + if (doRot) { + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z, flag); + } + if (doScale) { + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z, flag); + } + } + /* insert keyframe in any channel that's appropriate */ + else { + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z, flag); + + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z, flag); + + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y, flag); + insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z, flag); + } + } + } + + remake_action_ipos(act); + allqueue(REDRAWMARKER, 0); + allqueue(REDRAWOOPS, 0); + + /* locking can be disabled */ + ob->pose->flag &= ~(POSE_DO_UNLOCK|POSE_LOCKED); + + /* do the bone paths */ + if (arm->pathflag & ARM_PATH_ACFRA) { + //pose_clear_paths(ob); + pose_recalculate_paths(ob); + } + } + else { + /* tag channels that should have unkeyed data */ + for (pchan=pose->chanbase.first; pchan; pchan=pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { + /* tag this channel */ + pchan->bone->flag |= BONE_UNKEYED; + } + } + } +#endif +} + +/* very bad call!!! - copied from editnla.c! */ +static void recalc_all_ipos(void) +{ + Ipo *ipo; + IpoCurve *icu; + + /* Go to each ipo */ + for (ipo=G.main->ipo.first; ipo; ipo=ipo->id.next){ + for (icu = ipo->curve.first; icu; icu=icu->next){ + sort_time_ipocurve(icu); + testhandles_ipocurve(icu); + } + } +} + +/* inserting keys, refresh ipo-keys, pointcache, redraw events... (ton) */ +/* note: transdata has been freed already! */ +void special_aftertrans_update(TransInfo *t) +{ + Object *ob; + Base *base; + short redrawipo=0, resetslowpar=1; + int cancelled= (t->state == TRANS_CANCEL); + short duplicate= (t->undostr && strstr(t->undostr, "Duplicate")) ? 1 : 0; + + if (t->spacetype==SPACE_VIEW3D) { + if (G.obedit) { + if (cancelled==0) { +#if 0 // TRANSFORM_FIX_ME + EM_automerge(1); + /* when snapping, delay retopo until after automerge */ + if (G.qual & LR_CTRLKEY) { + retopo_do_all(); + } +#endif + } + } + } +#if 0 // TRANSFORM_FIX_ME + if (t->spacetype == SPACE_ACTION) { + void *data; + short datatype; + + /* determine what type of data we are operating on */ + data = get_action_context(&datatype); + if (data == NULL) return; + ob = OBACT; + + if (datatype == ACTCONT_ACTION) { + /* Depending on the lock status, draw necessary views */ + if (ob) { + ob->ctime= -1234567.0f; + + if(ob->pose || ob_get_key(ob)) + DAG_object_flush_update(G.scene, ob, OB_RECALC); + else + DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); + } + + /* Do curve cleanups? */ + if ( (G.saction->flag & SACTION_NOTRANSKEYCULL)==0 && + ((cancelled == 0) || (duplicate)) ) + { + posttrans_action_clean((bAction *)data); + } + + /* Do curve updates */ + remake_action_ipos((bAction *)data); + } + else if (datatype == ACTCONT_SHAPEKEY) { + /* fix up the Ipocurves and redraw stuff */ + Key *key= (Key *)data; + if (key->ipo) { + IpoCurve *icu; + + if ( (G.saction->flag & SACTION_NOTRANSKEYCULL)==0 && + ((cancelled == 0) || (duplicate)) ) + { + posttrans_ipo_clean(key->ipo); + } + + for (icu = key->ipo->curve.first; icu; icu=icu->next) { + sort_time_ipocurve(icu); + testhandles_ipocurve(icu); + } + } + + DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA); + } + else if (datatype == ACTCONT_GPENCIL) { + /* remove duplicate frames and also make sure points are in order! */ + if ((cancelled == 0) || (duplicate)) + { + ScrArea *sa; + + /* BAD... we need to loop over all screen areas for current screen... + * - sync this with actdata_filter_gpencil() in editaction.c + */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { + bGPdata *gpd= gpencil_data_getactive(sa); + + if (gpd) + posttrans_gpd_clean(gpd); + } + } + } + + G.saction->flag &= ~SACTION_MOVING; + } + else if (t->spacetype == SPACE_NLA) { + recalc_all_ipos(); // bad + synchronize_action_strips(); + + /* cleanup */ + for (base=G.scene->base.first; base; base=base->next) + base->flag &= ~(BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA); + + /* after transform, remove duplicate keyframes on a frame that resulted from transform */ + if ( (G.snla->flag & SNLA_NOTRANSKEYCULL)==0 && + ((cancelled == 0) || (duplicate)) ) + { + posttrans_nla_clean(t); + } + } + else if (t->spacetype == SPACE_IPO) { + // FIXME! is there any code from the old transform_ipo that needs to be added back? + + /* after transform, remove duplicate keyframes on a frame that resulted from transform */ + if (G.sipo->ipo) + { + if ( (G.sipo->flag & SIPO_NOTRANSKEYCULL)==0 && + (cancelled == 0) ) + { + /* NOTE: no need to do NLA scaling stuff here, as when there is NLA scaling, + * the transformed handles will get moved wrong (seem to match wrong repeat cycle) + */ + posttrans_ipo_clean(G.sipo->ipo); + } + } + + /* resetting slow-parents isn't really necessary when editing sequence ipo's */ + if (G.sipo->blocktype==ID_SEQ) + resetslowpar= 0; + } + else if (G.obedit) { + if (t->mode==TFM_BONESIZE || t->mode==TFM_BONE_ENVELOPE) + allqueue(REDRAWBUTSEDIT, 0); + + /* table needs to be created for each edit command, since vertices can move etc */ + mesh_octree_table(G.obedit, NULL, 'e'); + } + else if ((t->flag & T_POSE) && (t->poseobj)) { + bArmature *arm; + bPose *pose; + bPoseChannel *pchan; + short targetless_ik= 0; + + ob= t->poseobj; + arm= ob->data; + pose= ob->pose; + + /* this signal does one recalc on pose, then unlocks, so ESC or edit will work */ + pose->flag |= POSE_DO_UNLOCK; + + /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */ + if (!cancelled && t->mode==TFM_TRANSLATION) + targetless_ik= apply_targetless_ik(ob); + else { + /* not forget to clear the auto flag */ + for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { + bKinematicConstraint *data= has_targetless_ik(pchan); + if(data) data->flag &= ~CONSTRAINT_IK_AUTO; + } + } + + if (t->mode==TFM_TRANSLATION) + pose_grab_with_ik_clear(ob); + + /* automatic inserting of keys and unkeyed tagging - only if transform wasn't cancelled (or TFM_DUMMY) */ + if (!cancelled && (t->mode != TFM_DUMMY)) { + autokeyframe_pose_cb_func(ob, t->mode, targetless_ik); + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + else if (arm->flag & ARM_DELAYDEFORM) { + /* old optimize trick... this enforces to bypass the depgraph */ + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + ob->recalc= 0; // is set on OK position already by recalcData() + } + else + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + + if (t->mode==TFM_BONESIZE || t->mode==TFM_BONE_ENVELOPE) + allqueue(REDRAWBUTSEDIT, 0); + + } + else if(G.f & G_PARTICLEEDIT) { + ; + } + else { + base= FIRSTBASE; + + while (base) { + + if(base->flag & BA_DO_IPO) redrawipo= 1; + + ob= base->object; + + if(base->flag & SELECT && (t->mode != TFM_DUMMY)) { + if(BKE_ptcache_object_reset(ob, PTCACHE_RESET_DEPSGRAPH)) + ob->recalc |= OB_RECALC_DATA; + + /* Set autokey if necessary */ + if (!cancelled) + autokeyframe_ob_cb_func(ob, t->mode); + } + + base= base->next; + } + + } +#endif + + clear_trans_object_base_flags(t); + +#if 0 // TRANSFORM_FIX_ME + if (redrawipo) { + allqueue(REDRAWNLA, 0); + allqueue(REDRAWACTION, 0); + allqueue(REDRAWIPO, 0); + } + + if(resetslowpar) + reset_slowparents(); + + /* note; should actually only be done for all objects when a lamp is moved... (ton) */ + if(t->spacetype==SPACE_VIEW3D && G.vd->drawtype == OB_SHADED) + reshadeall_displist(); +#endif +} + +static void createTransObject(bContext *C, TransInfo *t) +{ + Scene *sce = CTX_data_scene(C); + View3D *v3d = t->view; + + TransData *td = NULL; + TransDataExtension *tx; + Object *ob; + Base *base; +// IpoKey *ik; +// ListBase elems; + + set_trans_object_base_flags(C, t); + + /* count */ + for(base = sce->base.first; base; base= base->next) { + if TESTBASE(v3d, base) { + ob= base->object; + +#if 0 // TRANSFORM_FIX_ME + /* store ipo keys? */ + if ((ob->id.lib == 0) && (ob->ipo) && (ob->ipo->showkey) && (ob->ipoflag & OB_DRAWKEY)) { + elems.first= elems.last= NULL; + make_ipokey_transform(ob, &elems, 1); /* '1' only selected keys */ + + pushdata(&elems, sizeof(ListBase)); + + for(ik= elems.first; ik; ik= ik->next) + t->total++; + + if(elems.first==NULL) + t->total++; + } +#endif +// else { + t->total++; +// } + } + } + + if(!t->total) { + /* clear here, main transform function escapes too */ + clear_trans_object_base_flags(t); + return; + } + + td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransOb"); + tx = t->ext = MEM_callocN(t->total*sizeof(TransDataExtension), "TransObExtension"); + + for(base = sce->base.first; base; base= base->next) { + if TESTBASE(v3d, base) { + ob= base->object; + + td->flag = TD_SELECTED; + td->protectflag= ob->protectflag; + td->ext = tx; + + if (base->flag & BA_TRANSFORM_CHILD) + { + td->flag |= TD_NOCENTER; + td->flag |= TD_NO_LOC; + } + + /* select linked objects, but skip them later */ + if (ob->id.lib != 0) { + td->flag |= TD_SKIP; + } + + /* store ipo keys? */ + // TRANSFORM_FIX_ME +#if 0 + if((ob->id.lib == 0) && (ob->ipo) && (ob->ipo->showkey) && (ob->ipoflag & OB_DRAWKEY)) { + + popfirst(&elems); // bring back pushed listbase + + if(elems.first) { + int cfraont; + int ipoflag; + + base->flag |= BA_DO_IPO+BA_WAS_SEL; + base->flag &= ~SELECT; + + cfraont= CFRA; + set_no_parent_ipo(1); + ipoflag= ob->ipoflag; + ob->ipoflag &= ~OB_OFFS_OB; + + /* + * This is really EVIL code that pushes down Object values + * (loc, dloc, orig, size, dsize, rot, drot) + * */ + + pushdata((void*)ob->loc, 7 * 3 * sizeof(float)); // tsk! tsk! + + for(ik= elems.first; ik; ik= ik->next) { + + /* weak... this doesn't correct for floating values, giving small errors */ + CFRA= (int)(ik->val/G.scene->r.framelen); + + do_ob_ipo(ob); + ObjectToTransData(C, t, td, ob); // does where_is_object() + + td->flag= TD_SELECTED; + + td->tdi= MEM_callocN(sizeof(TransDataIpokey), "TransDataIpokey"); + /* also does tdi->flag and oldvals, needs to be after ob_to_transob()! */ + ipokey_to_transdata(ik, td); + + td++; + tx++; + if(ik->next) td->ext= tx; // prevent corrupting mem! + } + free_ipokey(&elems); + + poplast(ob->loc); + set_no_parent_ipo(0); + + CFRA= cfraont; + ob->ipoflag= ipoflag; + + where_is_object(ob); // restore + } + else { + ObjectToTransData(C, t, td, ob); + td->tdi = NULL; + td->val = NULL; + td++; + tx++; + } + } +#endif +// else { + ObjectToTransData(C, t, td, ob); + td->tdi = NULL; + td->val = NULL; + td++; + tx++; +// } + } + } +} + +void createTransData(bContext *C, TransInfo *t) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = OBACT; + + if (t->context == CTX_TEXTURE) { + t->flag |= T_TEXTURE; + createTransTexspace(C, t); + } + else if (t->context == CTX_EDGE) { + t->ext = NULL; + t->flag |= T_EDIT; + createTransEdge(C, t); + if(t->data && t->flag & T_PROP_EDIT) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } + else if (t->context == CTX_BMESH) { + // TRANSFORM_FIX_ME + //createTransBMeshVerts(t, G.editBMesh->bm, G.editBMesh->td); + } + else if (t->spacetype == SPACE_IMAGE) { + t->flag |= T_POINTS|T_2D_EDIT; + createTransUVs(C, t); + if(t->data && (t->flag & T_PROP_EDIT)) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } + else if (t->spacetype == SPACE_ACTION) { + t->flag |= T_POINTS|T_2D_EDIT; + createTransActionData(C, t); + } + else if (t->spacetype == SPACE_NLA) { + t->flag |= T_POINTS|T_2D_EDIT; + createTransNlaData(C, t); + } + else if (t->spacetype == SPACE_IPO) { + t->flag |= T_POINTS|T_2D_EDIT; + createTransIpoData(C, t); + if (t->data && (t->flag & T_PROP_EDIT)) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } + else if (0) { // // TRANSFORM_FIX_ME (G.obedit) { + Object *obedit = NULL; // TRANSFORM_FIX_ME + + t->ext = NULL; + if (obedit->type == OB_MESH) { + createTransEditVerts(C, t); + } + else if ELEM(obedit->type, OB_CURVE, OB_SURF) { + createTransCurveVerts(C, t); + } + else if (obedit->type==OB_LATTICE) { + createTransLatticeVerts(C, t); + } + else if (obedit->type==OB_MBALL) { + createTransMBallVerts(C, t); + } + else if (obedit->type==OB_ARMATURE) { + t->flag &= ~T_PROP_EDIT; + createTransArmatureVerts(C, t); + } + else { + printf("edit type not implemented!\n"); + } + + if(t->data && t->flag & T_PROP_EDIT) { + if (ELEM(obedit->type, OB_CURVE, OB_MESH)) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 0); + sort_trans_data_dist(t); + } + else { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } + + t->flag |= T_EDIT|T_POINTS; + + /* exception... hackish, we want bonesize to use bone orientation matrix (ton) */ + if(t->mode==TFM_BONESIZE) { + t->flag &= ~(T_EDIT|T_POINTS); + t->flag |= T_POSE; + t->poseobj = ob; /* <- tsk tsk, this is going to give issues one day */ + } + } + else if (ob && (ob->flag & OB_POSEMODE)) { + createTransPose(C, t, OBACT); + } + else if (G.f & G_WEIGHTPAINT) { + /* exception, we look for the one selected armature */ + CTX_DATA_BEGIN(C, Object*, ob_armature, selected_objects) + { + if(ob_armature->type==OB_ARMATURE) + { + if(ob_armature->flag & OB_POSEMODE) + { + createTransPose(C, t, ob_armature); + break; + } + } + } + CTX_DATA_END; + } +#if 0 // TRANSFORM_FIX_ME + else if (G.f & G_PARTICLEEDIT && PE_can_edit(PE_get_current(ob))) { + createTransParticleVerts(C, t); + + if(t->data && t->flag & T_PROP_EDIT) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + + t->flag |= T_POINTS; + } +#endif + else { + View3D *v3d = t->view; + + t->flag &= ~T_PROP_EDIT; /* no proportional edit in object mode */ + createTransObject(C, t); + t->flag |= T_OBJECT; + + if((t->flag & T_OBJECT) && v3d->camera == OBACT && v3d->persp==V3D_CAMOB) + { + t->flag |= T_CAMERA; + } + } + +// TRANSFORM_FIX_ME +// /* temporal...? */ +// G.scene->recalc |= SCE_PRV_CHANGED; /* test for 3d preview */ +} + + + + diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c new file mode 100644 index 00000000000..2073033ebb6 --- /dev/null +++ b/source/blender/editors/transform/transform_generics.c @@ -0,0 +1,1235 @@ +/** + * $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 <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLO_sys_types.h" // for intptr_t support + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_curve_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_nla_types.h" +#include "DNA_object_types.h" +#include "DNA_object_force.h" +#include "DNA_particle_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +//#include "BIF_screen.h" +//#include "BIF_resources.h" +//#include "BIF_mywindow.h" +#include "BIF_gl.h" +//#include "BIF_editaction.h" +#include "BIF_editarmature.h" +//#include "BIF_editmesh.h" +//#include "BIF_editnla.h" +//#include "BIF_editsima.h" +//#include "BIF_editparticle.h" +//#include "BIF_meshtools.h" +#include "BIF_retopo.h" + +//#include "BSE_editipo.h" +//#include "BSE_editipo_types.h" + +#ifdef WITH_VERSE +#include "BIF_verse.h" +#endif + +#include "BKE_action.h" +#include "BKE_anim.h" +#include "BKE_armature.h" +#include "BKE_cloth.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_displist.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_group.h" +#include "BKE_ipo.h" +#include "BKE_lattice.h" +#include "BKE_key.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_utildefines.h" +#include "BKE_context.h" + +#ifdef WITH_VERSE +#include "BKE_verse.h" +#endif + +#include "ED_view3d.h" + +//#include "BSE_editaction_types.h" +//#include "BDR_unwrapper.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" +#include "BLI_rand.h" + +#include "WM_types.h" + +//#include "blendef.h" +// +//#include "mydevice.h" + +#include "transform.h" + +extern ListBase editNurb; +extern ListBase editelems; + +extern TransInfo Trans; /* From transform.c */ + +/* ************************** Functions *************************** */ + +void getViewVector(TransInfo *t, float coord[3], float vec[3]) +{ + if (t->persp != V3D_ORTHO) + { + float p1[4], p2[4]; + + VECCOPY(p1, coord); + p1[3] = 1.0f; + VECCOPY(p2, p1); + p2[3] = 1.0f; + Mat4MulVec4fl(t->viewmat, p2); + + p2[0] = 2.0f * p2[0]; + p2[1] = 2.0f * p2[1]; + p2[2] = 2.0f * p2[2]; + + Mat4MulVec4fl(t->viewinv, p2); + + VecSubf(vec, p1, p2); + } + else { + VECCOPY(vec, t->viewinv[2]); + } + Normalize(vec); +} + +/* ************************** GENERICS **************************** */ + +static void clipMirrorModifier(TransInfo *t, Object *ob) +{ + ModifierData *md= ob->modifiers.first; + float tolerance[3] = {0.0f, 0.0f, 0.0f}; + int axis = 0; + + for (; md; md=md->next) { + if (md->type==eModifierType_Mirror) { + MirrorModifierData *mmd = (MirrorModifierData*) md; + + if(mmd->flag & MOD_MIR_CLIPPING) { + axis = 0; + if(mmd->flag & MOD_MIR_AXIS_X) { + axis |= 1; + tolerance[0] = mmd->tolerance; + } + if(mmd->flag & MOD_MIR_AXIS_Y) { + axis |= 2; + tolerance[1] = mmd->tolerance; + } + if(mmd->flag & MOD_MIR_AXIS_Z) { + axis |= 4; + tolerance[2] = mmd->tolerance; + } + if (axis) { + float mtx[4][4], imtx[4][4]; + int i; + TransData *td = t->data; + + if (mmd->mirror_ob) { + float obinv[4][4]; + + Mat4Invert(obinv, mmd->mirror_ob->obmat); + Mat4MulMat4(mtx, ob->obmat, obinv); + Mat4Invert(imtx, mtx); + } + + for(i = 0 ; i < t->total; i++, td++) { + int clip; + float loc[3], iloc[3]; + + if (td->flag & TD_NOACTION) + break; + if (td->loc==NULL) + break; + + if (td->flag & TD_SKIP) + continue; + + VecCopyf(loc, td->loc); + VecCopyf(iloc, td->iloc); + + if (mmd->mirror_ob) { + VecMat4MulVecfl(loc, mtx, loc); + VecMat4MulVecfl(iloc, mtx, iloc); + } + + clip = 0; + if(axis & 1) { + if(fabs(iloc[0])<=tolerance[0] || + loc[0]*iloc[0]<0.0f) { + loc[0]= 0.0f; + clip = 1; + } + } + + if(axis & 2) { + if(fabs(iloc[1])<=tolerance[1] || + loc[1]*iloc[1]<0.0f) { + loc[1]= 0.0f; + clip = 1; + } + } + if(axis & 4) { + if(fabs(iloc[2])<=tolerance[2] || + loc[2]*iloc[2]<0.0f) { + loc[2]= 0.0f; + clip = 1; + } + } + if (clip) { + if (mmd->mirror_ob) { + VecMat4MulVecfl(loc, imtx, loc); + } + VecCopyf(td->loc, loc); + } + } + } + + } + } + } +} + +/* assumes G.obedit set to mesh object */ +static void editmesh_apply_to_mirror(TransInfo *t) +{ + TransData *td = t->data; + EditVert *eve; + int i; + + for(i = 0 ; i < t->total; i++, td++) { + if (td->flag & TD_NOACTION) + break; + if (td->loc==NULL) + break; + if (td->flag & TD_SKIP) + continue; + + eve = td->extra; + if(eve) { + eve->co[0]= -td->loc[0]; + eve->co[1]= td->loc[1]; + eve->co[2]= td->loc[2]; + } + } +} + +/* called for updating while transform acts, once per redraw */ +void recalcData(TransInfo *t) +{ + Scene *scene = t->scene; + Base *base; +#ifdef WITH_VERSE + struct TransData *td; +#endif + +#if 0 // TRANSFORM_FIX_ME + if (t->spacetype == SPACE_ACTION) { + Object *ob= OBACT; + void *data; + short context; + + /* determine what type of data we are operating on */ + data = get_action_context(&context); + if (data == NULL) return; + + /* always flush data if gpencil context */ + if (context == ACTCONT_GPENCIL) { + flushTransGPactionData(t); + } + + if (G.saction->lock) { + if (context == ACTCONT_ACTION) { + if(ob) { + ob->ctime= -1234567.0f; + if(ob->pose || ob_get_key(ob)) + DAG_object_flush_update(G.scene, ob, OB_RECALC); + else + DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); + } + } + else if (context == ACTCONT_SHAPEKEY) { + DAG_object_flush_update(G.scene, OBACT, OB_RECALC_OB|OB_RECALC_DATA); + } + } + } + else if (t->spacetype == SPACE_NLA) { + if (G.snla->lock) { + for (base=G.scene->base.first; base; base=base->next) { + if (base->flag & BA_HAS_RECALC_OB) + base->object->recalc |= OB_RECALC_OB; + if (base->flag & BA_HAS_RECALC_DATA) + base->object->recalc |= OB_RECALC_DATA; + + if (base->object->recalc) + base->object->ctime= -1234567.0f; // eveil! + + /* recalculate scale of selected nla-strips */ + if (base->object->nlastrips.first) { + Object *bob= base->object; + bActionStrip *strip; + + for (strip= bob->nlastrips.first; strip; strip= strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + float actlen= strip->actend - strip->actstart; + float len= strip->end - strip->start; + + strip->scale= len / (actlen * strip->repeat); + } + } + } + } + + DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); + } + else { + for (base=G.scene->base.first; base; base=base->next) { + /* recalculate scale of selected nla-strips */ + if (base->object && base->object->nlastrips.first) { + Object *bob= base->object; + bActionStrip *strip; + + for (strip= bob->nlastrips.first; strip; strip= strip->next) { + if (strip->flag & ACTSTRIP_SELECT) { + float actlen= strip->actend - strip->actstart; + float len= strip->end - strip->start; + + /* prevent 'negative' scaling */ + if (len < 0) { + SWAP(float, strip->start, strip->end); + len= fabs(len); + } + + /* calculate new scale */ + strip->scale= len / (actlen * strip->repeat); + } + } + } + } + } + } + else if (t->spacetype == SPACE_IPO) { + EditIpo *ei; + int dosort = 0; + int a; + + /* do the flush first */ + flushTransIpoData(t); + + /* now test if there is a need to re-sort */ + ei= G.sipo->editipo; + for (a=0; a<G.sipo->totipo; a++, ei++) { + if (ISPOIN(ei, flag & IPO_VISIBLE, icu)) { + + /* watch it: if the time is wrong: do not correct handles */ + if (test_time_ipocurve(ei->icu)) { + dosort++; + } else { + calchandles_ipocurve(ei->icu); + } + } + } + + /* do resort and other updates? */ + if (dosort) remake_ipo_transdata(t); + if (G.sipo->showkey) update_ipokey_val(); + + calc_ipo(G.sipo->ipo, (float)CFRA); + + /* update realtime - not working? */ + if (G.sipo->lock) { + if (G.sipo->blocktype==ID_MA || G.sipo->blocktype==ID_TE) { + do_ipo(G.sipo->ipo); + } + else if(G.sipo->blocktype==ID_CA) { + do_ipo(G.sipo->ipo); + } + else if(G.sipo->blocktype==ID_KE) { + Object *ob= OBACT; + if(ob) { + ob->shapeflag &= ~OB_SHAPE_TEMPLOCK; + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + } + else if(G.sipo->blocktype==ID_PO) { + Object *ob= OBACT; + if(ob && ob->pose) { + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); + } + } + else if(G.sipo->blocktype==ID_OB) { + Object *ob= OBACT; + Base *base= FIRSTBASE; + + /* only if this if active object has this ipo in an action (assumes that current ipo is in action) */ + if ((ob) && (ob->ipoflag & OB_ACTION_OB) && (G.sipo->pin==0)) { + ob->ctime= -1234567.0f; + DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); + } + + while(base) { + if(base->object->ipo==G.sipo->ipo) { + do_ob_ipo(base->object); + base->object->recalc |= OB_RECALC_OB; + } + base= base->next; + } + DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); + } + } + } + else if (G.obedit) { + if (G.obedit->type == OB_MESH) { + if(t->spacetype==SPACE_IMAGE) { + flushTransUVs(t); + if (G.sima->flag & SI_LIVE_UNWRAP) + unwrap_lscm_live_re_solve(); + } else { + /* mirror modifier clipping? */ + if(t->state != TRANS_CANCEL) { + if ((G.qual & LR_CTRLKEY)==0) { + /* Only retopo if not snapping, Note, this is the only case of G.qual being used, but we have no T_SHIFT_MOD - Campbell */ + retopo_do_all(); + } + clipMirrorModifier(t, G.obedit); + } + if((t->context & CTX_NO_MIRROR) == 0 && (G.scene->toolsettings->editbutflag & B_MESH_X_MIRROR)) + editmesh_apply_to_mirror(t); + + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); /* sets recalc flags */ + + recalc_editnormals(); + } + } + else if ELEM(G.obedit->type, OB_CURVE, OB_SURF) { + Nurb *nu= editNurb.first; + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); /* sets recalc flags */ + + if (t->state == TRANS_CANCEL) { + while(nu) { + calchandlesNurb(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */ + nu= nu->next; + } + } else { + /* Normal updating */ + while(nu) { + test2DNurb(nu); + calchandlesNurb(nu); + nu= nu->next; + } + retopo_do_all(); + } + } + else if(G.obedit->type==OB_ARMATURE){ /* no recalc flag, does pose */ + bArmature *arm= G.obedit->data; + EditBone *ebo; + TransData *td = t->data; + int i; + + /* Ensure all bones are correctly adjusted */ + for (ebo=G.edbo.first; ebo; ebo=ebo->next){ + + if ((ebo->flag & BONE_CONNECTED) && ebo->parent){ + /* If this bone has a parent tip that has been moved */ + if (ebo->parent->flag & BONE_TIPSEL){ + VECCOPY (ebo->head, ebo->parent->tail); + if(t->mode==TFM_BONE_ENVELOPE) ebo->rad_head= ebo->parent->rad_tail; + } + /* If this bone has a parent tip that has NOT been moved */ + else{ + VECCOPY (ebo->parent->tail, ebo->head); + if(t->mode==TFM_BONE_ENVELOPE) ebo->parent->rad_tail= ebo->rad_head; + } + } + + /* on extrude bones, oldlength==0.0f, so we scale radius of points */ + ebo->length= VecLenf(ebo->head, ebo->tail); + if(ebo->oldlength==0.0f) { + ebo->rad_head= 0.25f*ebo->length; + ebo->rad_tail= 0.10f*ebo->length; + ebo->dist= 0.25f*ebo->length; + if(ebo->parent) { + if(ebo->rad_head > ebo->parent->rad_tail) + ebo->rad_head= ebo->parent->rad_tail; + } + } + else if(t->mode!=TFM_BONE_ENVELOPE) { + /* if bones change length, lets do that for the deform distance as well */ + ebo->dist*= ebo->length/ebo->oldlength; + ebo->rad_head*= ebo->length/ebo->oldlength; + ebo->rad_tail*= ebo->length/ebo->oldlength; + ebo->oldlength= ebo->length; + } + } + + + if (t->mode != TFM_BONE_ROLL) + { + /* fix roll */ + for(i = 0; i < t->total; i++, td++) + { + if (td->extra) + { + float vec[3], up_axis[3]; + float qrot[4]; + + ebo = td->extra; + VECCOPY(up_axis, td->axismtx[2]); + + if (t->mode != TFM_ROTATION) + { + VecSubf(vec, ebo->tail, ebo->head); + Normalize(vec); + RotationBetweenVectorsToQuat(qrot, td->axismtx[1], vec); + QuatMulVecf(qrot, up_axis); + } + else + { + Mat3MulVecfl(t->mat, up_axis); + } + + ebo->roll = rollBoneToVector(ebo, up_axis); + } + } + } + + if(arm->flag & ARM_MIRROR_EDIT) + transform_armature_mirror_update(); + + } + else if(G.obedit->type==OB_LATTICE) { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); /* sets recalc flags */ + + if(editLatt->flag & LT_OUTSIDE) outside_lattice(editLatt); + } + else { + DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA); /* sets recalc flags */ + } + } + else if( (t->flag & T_POSE) && t->poseobj) { + Object *ob= t->poseobj; + bArmature *arm= ob->data; + + /* old optimize trick... this enforces to bypass the depgraph */ + if (!(arm->flag & ARM_DELAYDEFORM)) { + DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); /* sets recalc flags */ + } + else + where_is_pose(ob); + } + else if(G.f & G_PARTICLEEDIT) { + flushTransParticles(t); + } +#endif + if (1) { + //else { + for(base= FIRSTBASE; base; base= base->next) { + Object *ob= base->object; + + /* this flag is from depgraph, was stored in initialize phase, handled in drawview.c */ + if(base->flag & BA_HAS_RECALC_OB) + ob->recalc |= OB_RECALC_OB; + if(base->flag & BA_HAS_RECALC_DATA) + ob->recalc |= OB_RECALC_DATA; + + /* thanks to ob->ctime usage, ipos are not called in where_is_object, + unless we edit ipokeys */ + if(base->flag & BA_DO_IPO) { + if(ob->ipo) { + IpoCurve *icu; + + ob->ctime= -1234567.0; + + icu= ob->ipo->curve.first; + while(icu) { + calchandles_ipocurve(icu); + icu= icu->next; + } + } + } + + /* proxy exception */ + if(ob->proxy) + ob->proxy->recalc |= ob->recalc; + if(ob->proxy_group) + group_tag_recalc(ob->proxy_group->dup_group); + } + } + +#ifdef WITH_VERSE + for (td = t->data; td < t->data + t->total; td++) { + if(td->flag & TD_VERSE_VERT) { + if(td->verse) + send_versevert_pos((VerseVert*)td->verse); + } + else if(td->flag & TD_VERSE_OBJECT) + if(td->verse) b_verse_send_transformation((Object*)td->verse); + } +#endif + + /* update shaded drawmode while transform */ + if(t->spacetype==SPACE_VIEW3D && ((View3D*)t->view)->drawtype == OB_SHADED) + reshadeall_displist(); +} + +void drawLine(float *center, float *dir, char axis, short options) +{ +#if 0 // TRANSFORM_FIX_ME + extern void make_axis_color(char *col, char *col2, char axis); // drawview.c + float v1[3], v2[3], v3[3]; + char col[3], col2[3]; + + //if(G.obedit) mymultmatrix(G.obedit->obmat); // sets opengl viewing + + VecCopyf(v3, dir); + VecMulf(v3, G.vd->far); + + VecSubf(v2, center, v3); + VecAddf(v1, center, v3); + + if (options & DRAWLIGHT) { + col[0] = col[1] = col[2] = 220; + } + else { + BIF_GetThemeColor3ubv(TH_GRID, col); + } + make_axis_color(col, col2, axis); + glColor3ubv((GLubyte *)col2); + + setlinestyle(0); + glBegin(GL_LINE_STRIP); + glVertex3fv(v1); + glVertex3fv(v2); + glEnd(); + + myloadmatrix(G.vd->viewmat); +#endif +} + +void resetTransRestrictions(TransInfo *t) +{ + t->flag &= ~T_ALL_RESTRICTIONS; +} + +void initTransInfo (bContext *C, TransInfo *t, wmEvent *event) +{ + Scene *sce = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + ScrArea *sa = CTX_wm_area(C); + + /* moving: is shown in drawobject() (transform color) */ +// TRANSFORM_FIX_ME +// if(G.obedit || (t->flag & T_POSE) ) G.moving= G_TRANSFORM_EDIT; +// else if(G.f & G_PARTICLEEDIT) G.moving= G_TRANSFORM_PARTICLE; +// else G.moving= G_TRANSFORM_OBJ; + + t->scene = sce; + t->sa = sa; + t->ar = ar; + + t->data = NULL; + t->ext = NULL; + + t->flag = 0; + + /* setting PET flag */ + if ((t->context & CTX_NO_PET) == 0 && (sce->proportional)) { + t->flag |= T_PROP_EDIT; + + if(sce->proportional == 2) + t->flag |= T_PROP_CONNECTED; // yes i know, has to become define + } + + t->imval[0] = event->x - t->ar->winrct.xmin; + t->imval[1] = event->y - t->ar->winrct.ymin; + + t->con.imval[0] = t->imval[0]; + t->con.imval[1] = t->imval[1]; + + t->mval[0] = t->imval[0]; + t->mval[1] = t->imval[1]; + + t->transform = NULL; + t->handleEvent = NULL; + + t->total = 0; + + t->val = 0.0f; + + t->vec[0] = + t->vec[1] = + t->vec[2] = 0.0f; + + t->center[0] = + t->center[1] = + t->center[2] = 0.0f; + + Mat3One(t->mat); + + t->spacetype = sa->spacetype; + if(t->spacetype == SPACE_VIEW3D) + { + View3D *v3d = sa->spacedata.first; + + t->view = v3d; + + if(v3d->flag & V3D_ALIGN) t->flag |= T_V3D_ALIGN; + t->around = v3d->around; + } + else if(t->spacetype==SPACE_IMAGE) + { + View2D *v2d = sa->spacedata.first; + + t->view = v2d; + + t->around = v2d->around; + } + else + { + t->view = NULL; + + t->around = V3D_CENTER; + } + + setTransformViewMatrices(t); + initNumInput(&t->num); + initNDofInput(&t->ndof); +} + +/* Here I would suggest only TransInfo related issues, like free data & reset vars. Not redraws */ +void postTrans (TransInfo *t) +{ + TransData *td; + + G.moving = 0; // Set moving flag off (display as usual) +#ifdef WITH_VERSE + + for (td = t->data; td < t->data + t->total; td++) { + if(td->flag & TD_VERSE_VERT) { + if(td->verse) send_versevert_pos((VerseVert*)td->verse); + } + else if(td->flag & TD_VERSE_OBJECT) { + if(td->verse) { + struct VNode *vnode; + vnode = (VNode*)((Object*)td->verse)->vnode; + ((VObjectData*)vnode->data)->flag |= POS_SEND_READY; + ((VObjectData*)vnode->data)->flag |= ROT_SEND_READY; + ((VObjectData*)vnode->data)->flag |= SCALE_SEND_READY; + b_verse_send_transformation((Object*)td->verse); + } + } + } +#endif + + stopConstraint(t); + + /* postTrans can be called when nothing is selected, so data is NULL already */ + if (t->data) { + int a; + + /* since ipokeys are optional on objects, we mallocced them per trans-data */ + for(a=0, td= t->data; a<t->total; a++, td++) { + if(td->tdi) MEM_freeN(td->tdi); + if (td->flag & TD_BEZTRIPLE) MEM_freeN(td->hdata); + } + MEM_freeN(t->data); + } + + if (t->ext) MEM_freeN(t->ext); + if (t->data2d) { + MEM_freeN(t->data2d); + t->data2d= NULL; + } + + if(t->spacetype==SPACE_IMAGE) { +#if 0 // TRANSFORM_FIX_ME + if (G.sima->flag & SI_LIVE_UNWRAP) + unwrap_lscm_live_end(t->state == TRANS_CANCEL); +#endif + } + else if(t->spacetype==SPACE_ACTION) { + if (t->customData) + MEM_freeN(t->customData); + } +} + +void applyTransObjects(TransInfo *t) +{ + TransData *td; + + for (td = t->data; td < t->data + t->total; td++) { + VECCOPY(td->iloc, td->loc); + if (td->ext->rot) { + VECCOPY(td->ext->irot, td->ext->rot); + } + if (td->ext->size) { + VECCOPY(td->ext->isize, td->ext->size); + } + } + recalcData(t); +} + +/* helper for below */ +static void restore_ipokey(float *poin, float *old) +{ + if(poin) { + poin[0]= old[0]; + poin[-3]= old[3]; + poin[3]= old[6]; + } +} + +static void restoreElement(TransData *td) { + /* TransData for crease has no loc */ + if (td->loc) { + VECCOPY(td->loc, td->iloc); + } + if (td->val) { + *td->val = td->ival; + } + if (td->ext && (td->flag&TD_NO_EXT)==0) { + if (td->ext->rot) { + VECCOPY(td->ext->rot, td->ext->irot); + } + if (td->ext->size) { + VECCOPY(td->ext->size, td->ext->isize); + } + if(td->flag & TD_USEQUAT) { + if (td->ext->quat) { + QUATCOPY(td->ext->quat, td->ext->iquat); + } + } + } + + if (td->flag & TD_BEZTRIPLE) { + *(td->hdata->h1) = td->hdata->ih1; + *(td->hdata->h2) = td->hdata->ih2; + } + + if(td->tdi) { + TransDataIpokey *tdi= td->tdi; + + restore_ipokey(tdi->locx, tdi->oldloc); + restore_ipokey(tdi->locy, tdi->oldloc+1); + restore_ipokey(tdi->locz, tdi->oldloc+2); + + restore_ipokey(tdi->rotx, tdi->oldrot); + restore_ipokey(tdi->roty, tdi->oldrot+1); + restore_ipokey(tdi->rotz, tdi->oldrot+2); + + restore_ipokey(tdi->sizex, tdi->oldsize); + restore_ipokey(tdi->sizey, tdi->oldsize+1); + restore_ipokey(tdi->sizez, tdi->oldsize+2); + } +} + +void restoreTransObjects(TransInfo *t) +{ + TransData *td; + + for (td = t->data; td < t->data + t->total; td++) { + restoreElement(td); +#ifdef WITH_VERSE + /* position of vertexes and object transformation matrix is sent + * extra, becuase blender uses synchronous sending of vertexes + * position as well object trans. matrix and it isn't possible to + * send it in recalcData sometimes */ + if(td->flag & TD_VERSE_VERT) { + if(td->verse) { + ((VerseVert*)td->verse)->flag |= VERT_POS_OBSOLETE; + } + } + else if(td->flag & TD_VERSE_OBJECT) + if(td->verse) { + struct VNode *vnode; + vnode = (VNode*)((Object*)td->verse)->vnode; + ((VObjectData*)vnode->data)->flag |= POS_SEND_READY; + ((VObjectData*)vnode->data)->flag |= ROT_SEND_READY; + ((VObjectData*)vnode->data)->flag |= SCALE_SEND_READY; + } +#endif + } + + Mat3One(t->mat); + + recalcData(t); +} + +void calculateCenter2D(TransInfo *t) +{ + if (t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + float vec[3]; + + VECCOPY(vec, t->center); + Mat4MulVecfl(ob->obmat, vec); + projectIntView(t, vec, t->center2d); + } + else { + projectIntView(t, t->center, t->center2d); + } +} + +void calculateCenterCursor(TransInfo *t) +{ + float *cursor; + + cursor = give_cursor(t->scene, t->view); + VECCOPY(t->center, cursor); + + /* If edit or pose mode, move cursor in local space */ + if (t->flag & (T_EDIT|T_POSE)) { + Object *ob = G.obedit?G.obedit:t->poseobj; + float mat[3][3], imat[3][3]; + + VecSubf(t->center, t->center, ob->obmat[3]); + Mat3CpyMat4(mat, ob->obmat); + Mat3Inv(imat, mat); + Mat3MulVecfl(imat, t->center); + } + + calculateCenter2D(t); +} + +void calculateCenterCursor2D(TransInfo *t) +{ +#if 0 // TRANSFORM_FIX_ME + float aspx=1.0, aspy=1.0; + + if(t->spacetype==SPACE_IMAGE) /* only space supported right now but may change */ + transform_aspect_ratio_tface_uv(&aspx, &aspy); + if (G.v2d) { + t->center[0] = G.v2d->cursor[0] * aspx; + t->center[1] = G.v2d->cursor[1] * aspy; + } +#endif + calculateCenter2D(t); +} + +void calculateCenterMedian(TransInfo *t) +{ + float partial[3] = {0.0f, 0.0f, 0.0f}; + int total = 0; + int i; + + for(i = 0; i < t->total; i++) { + if (t->data[i].flag & TD_SELECTED) { + if (!(t->data[i].flag & TD_NOCENTER)) + { + VecAddf(partial, partial, t->data[i].center); + total++; + } + } + else { + /* + All the selected elements are at the head of the array + which means we can stop when it finds unselected data + */ + break; + } + } + if(i) + VecMulf(partial, 1.0f / total); + VECCOPY(t->center, partial); + + calculateCenter2D(t); +} + +void calculateCenterBound(TransInfo *t) +{ + float max[3]; + float min[3]; + int i; + for(i = 0; i < t->total; i++) { + if (i) { + if (t->data[i].flag & TD_SELECTED) { + if (!(t->data[i].flag & TD_NOCENTER)) + MinMax3(min, max, t->data[i].center); + } + else { + /* + All the selected elements are at the head of the array + which means we can stop when it finds unselected data + */ + break; + } + } + else { + VECCOPY(max, t->data[i].center); + VECCOPY(min, t->data[i].center); + } + } + VecAddf(t->center, min, max); + VecMulf(t->center, 0.5); + + calculateCenter2D(t); +} + +void calculateCenter(TransInfo *t) +{ + switch(t->around) { + case V3D_CENTER: + calculateCenterBound(t); + break; + case V3D_CENTROID: + calculateCenterMedian(t); + break; + case V3D_CURSOR: + if(t->spacetype==SPACE_IMAGE) + calculateCenterCursor2D(t); + else + calculateCenterCursor(t); + break; + case V3D_LOCAL: + /* Individual element center uses median center for helpline and such */ + calculateCenterMedian(t); + break; + case V3D_ACTIVE: + { + /* set median, and if if if... do object center */ + EditSelection ese; + /* EDIT MODE ACTIVE EDITMODE ELEMENT */ + +#if 0 // TRANSFORM_FIX_ME + if (G.obedit && G.obedit->type == OB_MESH && EM_get_actSelection(&ese)) { + EM_editselection_center(t->center, &ese); + calculateCenter2D(t); + break; + } /* END EDIT MODE ACTIVE ELEMENT */ +#endif + + calculateCenterMedian(t); + if((t->flag & (T_EDIT|T_POSE))==0) + { + Scene *scene = t->scene; + Object *ob= OBACT; + if(ob) + { + VECCOPY(t->center, ob->obmat[3]); + projectIntView(t, t->center, t->center2d); + } + } + + } + } + + /* setting constraint center */ + VECCOPY(t->con.center, t->center); + if(t->flag & (T_EDIT|T_POSE)) + { + Object *ob= G.obedit?G.obedit:t->poseobj; + Mat4MulVecfl(ob->obmat, t->con.center); + } + + /* voor panning from cameraview */ + if(t->flag & T_OBJECT) + { + if(t->spacetype==SPACE_VIEW3D) + { + View3D *v3d = t->view; + Scene *scene = t->scene; + + if(v3d->camera == OBACT && v3d->persp==V3D_CAMOB) + { + float axis[3]; + /* persinv is nasty, use viewinv instead, always right */ + VECCOPY(axis, t->viewinv[2]); + Normalize(axis); + + /* 6.0 = 6 grid units */ + axis[0]= t->center[0]- 6.0f*axis[0]; + axis[1]= t->center[1]- 6.0f*axis[1]; + axis[2]= t->center[2]- 6.0f*axis[2]; + + projectIntView(t, axis, t->center2d); + + /* rotate only needs correct 2d center, grab needs initgrabz() value */ + if(t->mode==TFM_TRANSLATION) + { + VECCOPY(t->center, axis); + VECCOPY(t->con.center, t->center); + } + } + } + } + + if(t->spacetype==SPACE_VIEW3D) + initgrabz(t->view, t->center[0], t->center[1], t->center[2]); +} + +void calculatePropRatio(TransInfo *t) +{ + TransData *td = t->data; + int i; + float dist; + short connected = t->flag & T_PROP_CONNECTED; + + if (t->flag & T_PROP_EDIT) { + for(i = 0 ; i < t->total; i++, td++) { + if (td->flag & TD_SELECTED) { + td->factor = 1.0f; + } + else if ((connected && + (td->flag & TD_NOTCONNECTED || td->dist > t->propsize)) + || + (connected == 0 && + td->rdist > t->propsize)) { + /* + The elements are sorted according to their dist member in the array, + that means we can stop when it finds one element outside of the propsize. + */ + td->flag |= TD_NOACTION; + td->factor = 0.0f; + restoreElement(td); + } + else { + /* Use rdist for falloff calculations, it is the real distance */ + td->flag &= ~TD_NOACTION; + dist= (t->propsize-td->rdist)/t->propsize; + + /* + * Clamp to positive numbers. + * Certain corner cases with connectivity and individual centers + * can give values of rdist larger than propsize. + */ + if (dist < 0.0f) + dist = 0.0f; + + switch(G.scene->prop_mode) { + case PROP_SHARP: + td->factor= dist*dist; + break; + case PROP_SMOOTH: + td->factor= 3.0f*dist*dist - 2.0f*dist*dist*dist; + break; + case PROP_ROOT: + td->factor = (float)sqrt(dist); + break; + case PROP_LIN: + td->factor = dist; + break; + case PROP_CONST: + td->factor = 1.0f; + break; + case PROP_SPHERE: + td->factor = (float)sqrt(2*dist - dist * dist); + break; + case PROP_RANDOM: + BLI_srand( BLI_rand() ); /* random seed */ + td->factor = BLI_frand()*dist; + break; + default: + td->factor = 1; + } + } + } + switch(G.scene->prop_mode) { + case PROP_SHARP: + strcpy(t->proptext, "(Sharp)"); + break; + case PROP_SMOOTH: + strcpy(t->proptext, "(Smooth)"); + break; + case PROP_ROOT: + strcpy(t->proptext, "(Root)"); + break; + case PROP_LIN: + strcpy(t->proptext, "(Linear)"); + break; + case PROP_CONST: + strcpy(t->proptext, "(Constant)"); + break; + case PROP_SPHERE: + strcpy(t->proptext, "(Sphere)"); + break; + case PROP_RANDOM: + strcpy(t->proptext, "(Random)"); + break; + default: + strcpy(t->proptext, ""); + } + } + else { + for(i = 0 ; i < t->total; i++, td++) { + td->factor = 1.0; + } + strcpy(t->proptext, ""); + } +} + +TransInfo *BIF_GetTransInfo() +{ + return NULL; +} + +float get_drawsize(View3D *v3d, ScrArea *sa, float *co) +{ + float size, vec[3], len1, len2; + + /* size calculus, depending ortho/persp settings, like initgrabz() */ + size= v3d->persmat[0][3]*co[0]+ v3d->persmat[1][3]*co[1]+ v3d->persmat[2][3]*co[2]+ v3d->persmat[3][3]; + + VECCOPY(vec, v3d->persinv[0]); + len1= Normalize(vec); + VECCOPY(vec, v3d->persinv[1]); + len2= Normalize(vec); + + size*= 0.01f*(len1>len2?len1:len2); + + /* correct for window size to make widgets appear fixed size */ + if(sa->winx > sa->winy) size*= 1000.0f/(float)sa->winx; + else size*= 1000.0f/(float)sa->winy; + + return size; +} diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c new file mode 100644 index 00000000000..31d90fba099 --- /dev/null +++ b/source/blender/editors/transform/transform_manipulator.c @@ -0,0 +1,1683 @@ +/** +* $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) 2005 Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +// TRANSFORM_FIX_ME +// Disable everything here, don't need it for now +#if 0 + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef WIN32 +#include <unistd.h> +#else +#include <io.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_armature_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_lattice_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_armature.h" +#include "BKE_global.h" +#include "BKE_lattice.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +#include "BIF_editarmature.h" +#include "BIF_gl.h" +#include "BIF_mywindow.h" +#include "BIF_resources.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_transform.h" +#include "BIF_editmesh.h" +#include "BIF_editparticle.h" + +#include "BSE_edit.h" +#include "BSE_view.h" +#include "BDR_drawobject.h" + +#include "blendef.h" +#include "transform.h" + +/* return codes for select, and drawing flags */ + +#define MAN_TRANS_X 1 +#define MAN_TRANS_Y 2 +#define MAN_TRANS_Z 4 +#define MAN_TRANS_C 7 + +#define MAN_ROT_X 8 +#define MAN_ROT_Y 16 +#define MAN_ROT_Z 32 +#define MAN_ROT_V 64 +#define MAN_ROT_T 128 +#define MAN_ROT_C 248 + +#define MAN_SCALE_X 256 +#define MAN_SCALE_Y 512 +#define MAN_SCALE_Z 1024 +#define MAN_SCALE_C 1792 + +/* color codes */ + +#define MAN_RGB 0 +#define MAN_GHOST 1 +#define MAN_MOVECOL 2 + +/* GLOBAL VARIABLE THAT SHOULD MOVED TO SCREEN MEMBER OR SOMETHING */ +extern TransInfo Trans; + + +static int is_mat4_flipped(float mat[][4]) +{ + float vec[3]; + + Crossf(vec, mat[0], mat[1]); + if( Inpf(vec, mat[2]) < 0.0 ) return 1; + return 0; +} + +/* transform widget center calc helper for below */ +static void calc_tw_center(float *co) +{ + float *twcent= G.scene->twcent; + float *min= G.scene->twmin; + float *max= G.scene->twmax; + + DO_MINMAX(co, min, max); + VecAddf(twcent, twcent, co); +} + +static void protectflag_to_drawflags(short protectflag, short *drawflags) +{ + if(protectflag & OB_LOCK_LOCX) + *drawflags &= ~MAN_TRANS_X; + if(protectflag & OB_LOCK_LOCY) + *drawflags &= ~MAN_TRANS_Y; + if(protectflag & OB_LOCK_LOCZ) + *drawflags &= ~MAN_TRANS_Z; + + if(protectflag & OB_LOCK_ROTX) + *drawflags &= ~MAN_ROT_X; + if(protectflag & OB_LOCK_ROTY) + *drawflags &= ~MAN_ROT_Y; + if(protectflag & OB_LOCK_ROTZ) + *drawflags &= ~MAN_ROT_Z; + + if(protectflag & OB_LOCK_SCALEX) + *drawflags &= ~MAN_SCALE_X; + if(protectflag & OB_LOCK_SCALEY) + *drawflags &= ~MAN_SCALE_Y; + if(protectflag & OB_LOCK_SCALEZ) + *drawflags &= ~MAN_SCALE_Z; +} + +/* for pose mode */ +static void stats_pose(View3D *v3d, bPoseChannel *pchan) +{ + Bone *bone= pchan->bone; + + if(bone) { + if (bone->flag & BONE_TRANSFORM) { + calc_tw_center(pchan->pose_head); + protectflag_to_drawflags(pchan->protectflag, &v3d->twdrawflag); + } + } +} + +/* for editmode*/ +static void stats_editbone(View3D *v3d, EditBone *ebo) +{ + if (ebo->flag & BONE_EDITMODE_LOCKED) + protectflag_to_drawflags(OB_LOCK_LOC|OB_LOCK_ROT|OB_LOCK_SCALE, &v3d->twdrawflag); +} + +/* only counts the parent selection, and tags transform flag */ +/* bad call... should re-use method from transform_conversion once */ +static void count_bone_select(TransInfo *t, bArmature *arm, ListBase *lb, int do_it) +{ + Bone *bone; + int do_next; + + for(bone= lb->first; bone; bone= bone->next) { + bone->flag &= ~BONE_TRANSFORM; + do_next= do_it; + if(do_it) { + if(bone->layer & arm->layer) { + if (bone->flag & BONE_SELECTED) { + /* We don't let connected children get "grabbed" */ + if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_CONNECTED)==0 ) { + bone->flag |= BONE_TRANSFORM; + t->total++; + do_next= 0; // no transform on children if one parent bone is selected + } + } + } + } + count_bone_select(t, arm, &bone->childbase, do_next); + } +} + +/* centroid, boundbox, of selection */ +/* returns total items selected */ +int calc_manipulator_stats(ScrArea *sa) +{ + extern ListBase editNurb; + TransInfo *t; + View3D *v3d= sa->spacedata.first; + Base *base; + Object *ob= OBACT; + float normal[3]={0.0, 0.0, 0.0}; + float plane[3]={0.0, 0.0, 0.0}; + int a, totsel=0; + + t = BIF_GetTransInfo(); + + /* transform widget matrix */ + Mat4One(v3d->twmat); + + v3d->twdrawflag= 0xFFFF; + + /* transform widget centroid/center */ + G.scene->twcent[0]= G.scene->twcent[1]= G.scene->twcent[2]= 0.0f; + INIT_MINMAX(G.scene->twmin, G.scene->twmax); + + if(G.obedit) { + ob= G.obedit; + if((ob->lay & G.vd->lay)==0) return 0; + + if(G.obedit->type==OB_MESH) { + EditMesh *em = G.editMesh; + EditVert *eve; + EditSelection ese; + float vec[3]= {0,0,0}; + + /* USE LAST SELECTE WITH ACTIVE */ + if (G.vd->around==V3D_ACTIVE && EM_get_actSelection(&ese)) { + EM_editselection_center(vec, &ese); + calc_tw_center(vec); + totsel= 1; + } else { + /* do vertices for center, and if still no normal found, use vertex normals */ + for(eve= em->verts.first; eve; eve= eve->next) { + if(eve->f & SELECT) { + totsel++; + calc_tw_center(eve->co); + } + } + } + } /* end editmesh */ + else if (G.obedit->type==OB_ARMATURE){ + bArmature *arm= G.obedit->data; + EditBone *ebo; + for (ebo=G.edbo.first;ebo;ebo=ebo->next){ + if(ebo->layer & arm->layer) { + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(ebo->tail); + totsel++; + } + if (ebo->flag & BONE_ROOTSEL) { + calc_tw_center(ebo->head); + totsel++; + } + if (ebo->flag & BONE_SELECTED) { + stats_editbone(v3d, ebo); + } + } + } + } + else if ELEM3(G.obedit->type, OB_CURVE, OB_SURF, OB_FONT) { + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + + nu= editNurb.first; + while(nu) { + if((nu->type & 7)==CU_BEZIER) { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) { + /* exceptions + * if handles are hidden then only check the center points. + * If 2 or more are selected then only use the center point too. + */ + if (G.f & G_HIDDENHANDLES) { + if (bezt->f2 & SELECT) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + } + else if ( (bezt->f1 & SELECT) + (bezt->f2 & SELECT) + (bezt->f3 & SELECT) > SELECT ) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + else { + if(bezt->f1) { + calc_tw_center(bezt->vec[0]); + totsel++; + } + if(bezt->f2) { + calc_tw_center(bezt->vec[1]); + totsel++; + } + if(bezt->f3) { + calc_tw_center(bezt->vec[2]); + totsel++; + } + } + bezt++; + } + } + else { + bp= nu->bp; + a= nu->pntsu*nu->pntsv; + while(a--) { + if(bp->f1 & SELECT) { + calc_tw_center(bp->vec); + totsel++; + } + bp++; + } + } + nu= nu->next; + } + } + else if(G.obedit->type==OB_MBALL) { + /* editmball.c */ + extern ListBase editelems; /* go away ! */ + MetaElem *ml, *ml_sel=NULL; + + ml= editelems.first; + while(ml) { + if(ml->flag & SELECT) { + calc_tw_center(&ml->x); + ml_sel = ml; + totsel++; + } + ml= ml->next; + } + } + else if(G.obedit->type==OB_LATTICE) { + BPoint *bp; + bp= editLatt->def; + + a= editLatt->pntsu*editLatt->pntsv*editLatt->pntsw; + while(a--) { + if(bp->f1 & SELECT) { + calc_tw_center(bp->vec); + totsel++; + } + bp++; + } + } + + /* selection center */ + if(totsel) { + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + Mat4MulVecfl(G.obedit->obmat, G.scene->twcent); + Mat4MulVecfl(G.obedit->obmat, G.scene->twmin); + Mat4MulVecfl(G.obedit->obmat, G.scene->twmax); + } + } + else if(ob && (ob->flag & OB_POSEMODE)) { + bArmature *arm = ob->data; + bPoseChannel *pchan; + int mode; + + if((ob->lay & G.vd->lay)==0) return 0; + + mode = Trans.mode; + Trans.mode = TFM_ROTATION; // mislead counting bones... bah + + /* count total, we use same method as transform will do */ + Trans.total= 0; + count_bone_select(&Trans, arm, &arm->bonebase, 1); + totsel = Trans.total; + if(totsel) { + /* use channels to get stats */ + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + stats_pose(v3d, pchan); + } + + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + Mat4MulVecfl(ob->obmat, G.scene->twcent); + Mat4MulVecfl(ob->obmat, G.scene->twmin); + Mat4MulVecfl(ob->obmat, G.scene->twmax); + } + /* restore, mode can be TFM_INIT */ + Trans.mode = mode; + } + else if(G.f & (G_VERTEXPAINT + G_TEXTUREPAINT + G_WEIGHTPAINT + G_SCULPTMODE)) { + ; + } + else if(G.f & G_PARTICLEEDIT) { + ParticleSystem *psys=PE_get_current(OBACT); + ParticleData *pa = psys->particles; + ParticleEditKey *ek; + int k; + + if(psys->edit){ + for(a=0; a<psys->totpart; a++,pa++){ + if(pa->flag & PARS_HIDE) continue; + for(k=0, ek=psys->edit->keys[a]; k<pa->totkey; k++,ek++){ + if(ek->flag & PEK_SELECT){ + calc_tw_center(ek->world_co); + totsel++; + } + } + } + /* selection center */ + if(totsel) + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + } + } + else { + + /* we need the one selected object, if its not active */ + ob= OBACT; + if(ob && !(ob->flag & SELECT)) ob= NULL; + + for(base= G.scene->base.first; base; base= base->next) { + if TESTBASELIB(base) { + if(ob==NULL) + ob= base->object; + calc_tw_center(base->object->obmat[3]); + protectflag_to_drawflags(base->object->protectflag, &v3d->twdrawflag); + totsel++; + } + } + + /* selection center */ + if(totsel) { + VecMulf(G.scene->twcent, 1.0f/(float)totsel); // centroid! + } + } + + /* global, local or normal orientation? */ + if(ob && totsel) { + + switch(v3d->twmode) { + case V3D_MANIP_GLOBAL: + strcpy(t->spacename, "global"); + break; + + case V3D_MANIP_NORMAL: + if(G.obedit || ob->flag & OB_POSEMODE) { + float mat[3][3]; + int type; + + strcpy(t->spacename, "normal"); + + type = getTransformOrientation(normal, plane, (G.vd->around == V3D_ACTIVE)); + + switch (type) + { + case ORIENTATION_NORMAL: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_VERT: + if (createSpaceNormal(mat, normal) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_EDGE: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + case ORIENTATION_FACE: + if (createSpaceNormalTangent(mat, normal, plane) == 0) + { + type = ORIENTATION_NONE; + } + break; + } + + if (type == ORIENTATION_NONE) + { + Mat4One(v3d->twmat); + } + else + { + Mat4CpyMat3(v3d->twmat, mat); + } + break; + } + /* no break we define 'normal' as 'local' in Object mode */ + case V3D_MANIP_LOCAL: + strcpy(t->spacename, "local"); + Mat4CpyMat4(v3d->twmat, ob->obmat); + Mat4Ortho(v3d->twmat); + break; + + case V3D_MANIP_VIEW: + { + float mat[3][3]; + strcpy(t->spacename, "view"); + Mat3CpyMat4(mat, v3d->viewinv); + Mat3Ortho(mat); + Mat4CpyMat3(v3d->twmat, mat); + } + break; + default: /* V3D_MANIP_CUSTOM */ + applyTransformOrientation(); + break; + } + + } + + return totsel; +} + +/* ******************** DRAWING STUFFIES *********** */ + +static float screen_aligned(float mat[][4]) +{ + float vec[3], size; + + VECCOPY(vec, mat[0]); + size= Normalize(vec); + + glTranslatef(mat[3][0], mat[3][1], mat[3][2]); + + /* sets view screen aligned */ + glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]); + + return size; +} + + +/* radring = radius of donut rings + radhole = radius hole + start = starting segment (based on nrings) + end = end segment + nsides = amount of points in ring + nrigns = amount of rings +*/ +static void partial_donut(float radring, float radhole, int start, int end, int nsides, int nrings) +{ + float theta, phi, theta1; + float cos_theta, sin_theta; + float cos_theta1, sin_theta1; + float ring_delta, side_delta; + int i, j, docaps= 1; + + if(start==0 && end==nrings) docaps= 0; + + ring_delta= 2.0f*(float)M_PI/(float)nrings; + side_delta= 2.0f*(float)M_PI/(float)nsides; + + theta= (float)M_PI+0.5f*ring_delta; + cos_theta= (float)cos(theta); + sin_theta= (float)sin(theta); + + for(i= nrings - 1; i >= 0; i--) { + theta1= theta + ring_delta; + cos_theta1= (float)cos(theta1); + sin_theta1= (float)sin(theta1); + + if(docaps && i==start) { // cap + glBegin(GL_POLYGON); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + } + glEnd(); + } + if(i>=start && i<=end) { + glBegin(GL_QUAD_STRIP); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi += side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + if(docaps && i==end) { // cap + glBegin(GL_POLYGON); + phi= 0.0; + for(j= nsides; j >= 0; j--) { + float cos_phi, sin_phi, dist; + + phi -= side_delta; + cos_phi= (float)cos(phi); + sin_phi= (float)sin(phi); + dist= radhole + radring * cos_phi; + + glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); + } + glEnd(); + } + + + theta= theta1; + cos_theta= cos_theta1; + sin_theta= sin_theta1; + } +} + +/* three colors can be set; + grey for ghosting + moving: in transform theme color + else the red/green/blue +*/ +static void manipulator_setcolor(char axis, int colcode) +{ + float vec[4]; + char col[4]; + + vec[3]= 0.7f; // alpha set on 0.5, can be glEnabled or not + + if(colcode==MAN_GHOST) { + glColor4ub(0, 0, 0, 70); + } + else if(colcode==MAN_MOVECOL) { + BIF_GetThemeColor3ubv(TH_TRANSFORM, col); + glColor4ub(col[0], col[1], col[2], 128); + } + else { + switch(axis) { + case 'c': + BIF_GetThemeColor3ubv(TH_TRANSFORM, col); + if(G.vd->twmode == V3D_MANIP_LOCAL) { + col[0]= col[0]>200?255:col[0]+55; + col[1]= col[1]>200?255:col[1]+55; + col[2]= col[2]>200?255:col[2]+55; + } + else if(G.vd->twmode == V3D_MANIP_NORMAL) { + col[0]= col[0]<55?0:col[0]-55; + col[1]= col[1]<55?0:col[1]-55; + col[2]= col[2]<55?0:col[2]-55; + } + glColor4ub(col[0], col[1], col[2], 128); + break; + case 'x': + glColor4ub(220, 0, 0, 128); + break; + case 'y': + glColor4ub(0, 220, 0, 128); + break; + case 'z': + glColor4ub(30, 30, 220, 128); + break; + } + } +} + +/* viewmatrix should have been set OK, also no shademode! */ +static void draw_manipulator_axes(int colcode, int flagx, int flagy, int flagz) +{ + + /* axes */ + if(flagx) { + manipulator_setcolor('x', colcode); + if(flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X); + else if(flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X); + glBegin(GL_LINES); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + glEnd(); + } + if(flagy) { + if(flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y); + else if(flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y); + manipulator_setcolor('y', colcode); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + glEnd(); + } + if(flagz) { + if(flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z); + else if(flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z); + manipulator_setcolor('z', colcode); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + } +} + +/* only called while G.moving */ +static void draw_manipulator_rotate_ghost(float mat[][4], int drawflags) +{ + GLUquadricObj *qobj; + float size, phi, startphi, vec[3], svec[3], matt[4][4], cross[3], tmat[3][3]; + int arcs= (G.rt!=2); + + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + glColor4ub(0,0,0,64); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* we need both [4][4] transforms, Trans.mat seems to be premul, not post for mat[][4] */ + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + + /* prepare for screen aligned draw */ + glPushMatrix(); + size= screen_aligned(mat); + + vec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + + startphi= saacos( vec[1] ); + if(vec[0]<0.0) startphi= -startphi; + + phi= (float)fmod(180.0*Trans.val/M_PI, 360.0); + if(phi > 180.0) phi-= 360.0; + else if(phi<-180.0) phi+= 360.0; + + gluPartialDisk(qobj, 0.0, size, 32, 1, 180.0*startphi/M_PI, phi); + + glPopMatrix(); + } + else if(arcs) { + float imat[3][3], ivmat[3][3]; + /* try to get the start rotation */ + + svec[0]= (float)(Trans.con.imval[0] - Trans.center2d[0]); + svec[1]= (float)(Trans.con.imval[1] - Trans.center2d[1]); + svec[2]= 0.0f; + + /* screen aligned vec transform back to manipulator space */ + Mat3CpyMat4(ivmat, G.vd->viewinv); + Mat3CpyMat4(tmat, mat); + Mat3Inv(imat, tmat); + Mat3MulMat3(tmat, imat, ivmat); + + Mat3MulVecfl(tmat, svec); // tmat is used further on + Normalize(svec); + } + + mymultmatrix(mat); // aligns with original widget + + /* Z disk */ + if(drawflags & MAN_ROT_Z) { + if(arcs) { + /* correct for squeezed arc */ + svec[0]+= tmat[2][0]; + svec[1]+= tmat[2][1]; + Normalize(svec); + + startphi= (float)atan2(svec[0], svec[1]); + } + else startphi= 0.5f*(float)M_PI; + + VECCOPY(vec, mat[0]); // use x axis to detect rotation + Normalize(vec); + Normalize(matt[0]); + phi= saacos( Inpf(vec, matt[0]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[0]); // results in z vector + if(Inpf(cross, mat[2]) > 0.0) phi= -phi; + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*(phi)/M_PI); + } + } + /* X disk */ + if(drawflags & MAN_ROT_X) { + if(arcs) { + /* correct for squeezed arc */ + svec[1]+= tmat[2][1]; + svec[2]+= tmat[2][2]; + Normalize(svec); + + startphi= (float)(M_PI + atan2(svec[2], -svec[1])); + } + else startphi= 0.0f; + + VECCOPY(vec, mat[1]); // use y axis to detect rotation + Normalize(vec); + Normalize(matt[1]); + phi= saacos( Inpf(vec, matt[1]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[1]); // results in x vector + if(Inpf(cross, mat[0]) > 0.0) phi= -phi; + glRotatef(90.0, 0.0, 1.0, 0.0); + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(arcs) { + /* correct for squeezed arc */ + svec[0]+= tmat[2][0]; + svec[2]+= tmat[2][2]; + Normalize(svec); + + startphi= (float)(M_PI + atan2(-svec[0], svec[2])); + } + else startphi= (float)M_PI; + + VECCOPY(vec, mat[2]); // use z axis to detect rotation + Normalize(vec); + Normalize(matt[2]); + phi= saacos( Inpf(vec, matt[2]) ); + if(phi!=0.0) { + Crossf(cross, vec, matt[2]); // results in y vector + if(Inpf(cross, mat[1]) > 0.0) phi= -phi; + glRotatef(-90.0, 1.0, 0.0, 0.0); + gluPartialDisk(qobj, 0.0, 1.0, 32, 1, 180.0*startphi/M_PI, 180.0*phi/M_PI); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + } + + glDisable(GL_BLEND); + myloadmatrix(G.vd->viewmat); +} + +static void draw_manipulator_rotate(float mat[][4], int moving, int drawflags, int combo) +{ + GLUquadricObj *qobj; + double plane[4]; + float size, vec[3], unitmat[4][4]; + float cywid= 0.33f*0.01f*(float)U.tw_handlesize; + float cusize= cywid*0.65f; + int arcs= (G.rt!=2); + int colcode; + + if(moving) colcode= MAN_MOVECOL; + else colcode= MAN_RGB; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_ROT_C)==0) return; + + /* Init stuff */ + glDisable(GL_DEPTH_TEST); + Mat4One(unitmat); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + /* prepare for screen aligned draw */ + VECCOPY(vec, mat[0]); + size= Normalize(vec); + glPushMatrix(); + glTranslatef(mat[3][0], mat[3][1], mat[3][2]); + + if(arcs) { + /* clipplane makes nice handles, calc here because of multmatrix but with translate! */ + VECCOPY(plane, G.vd->viewinv[2]); + plane[3]= -0.02*size; // clip just a bit more + glClipPlane(GL_CLIP_PLANE0, plane); + } + /* sets view screen aligned */ + glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]); + + /* Screen aligned help circle */ + if(arcs) { + if((G.f & G_PICKSEL)==0) { + BIF_ThemeColorShade(TH_BACK, -30); + drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat); + } + } + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V); + BIF_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat); + + if(moving) { + float vec[3]; + vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + VecMulf(vec, 1.2f*size); + glBegin(GL_LINES); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + /* apply the transform delta */ + if(moving) { + float matt[4][4]; + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + mymultmatrix(matt); + glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW); + } + else { + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + mymultmatrix(mat); + } + + /* axes */ + if(arcs==0) { + if(!(G.f & G_PICKSEL)) { + if( (combo & V3D_MANIP_SCALE)==0) { + /* axis */ + glBegin(GL_LINES); + if( (drawflags & MAN_ROT_X) || (moving && (drawflags & MAN_ROT_Z)) ) { + manipulator_setcolor('x', colcode); + glVertex3f(0.2f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + } + if( (drawflags & MAN_ROT_Y) || (moving && (drawflags & MAN_ROT_X)) ) { + manipulator_setcolor('y', colcode); + glVertex3f(0.0f, 0.2f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + } + if( (drawflags & MAN_ROT_Z) || (moving && (drawflags & MAN_ROT_Y)) ) { + manipulator_setcolor('z', colcode); + glVertex3f(0.0f, 0.0f, 0.2f); + glVertex3f(0.0f, 0.0f, 1.0f); + } + glEnd(); + } + } + } + + if(arcs==0 && moving) { + + /* Z circle */ + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + } + /* X circle */ + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + + if(arcs) glDisable(GL_CLIP_PLANE0); + } + // donut arcs + if(arcs) { + glEnable(GL_CLIP_PLANE0); + + /* Z circle */ + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + } + /* X circle */ + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y circle */ + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + partial_donut(cusize/4.0f, 1.0f, 0, 48, 8, 48); + glRotatef(90.0, 1.0, 0.0, 0.0); + } + + glDisable(GL_CLIP_PLANE0); + } + + if(arcs==0) { + + /* Z handle on X axis */ + if(drawflags & MAN_ROT_Z) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + /* Y handle on X axis */ + if(drawflags & MAN_ROT_Y) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + manipulator_setcolor('y', colcode); + + glRotatef(90.0, 1.0, 0.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + /* X handle on Z axis */ + if(drawflags & MAN_ROT_X) { + glPushMatrix(); + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + manipulator_setcolor('x', colcode); + + glRotatef(-90.0, 0.0, 1.0, 0.0); + glRotatef(90.0, 0.0, 0.0, 1.0); + partial_donut(0.7f*cusize, 1.0f, 31, 33, 8, 64); + + glPopMatrix(); + } + + } + + /* restore */ + myloadmatrix(G.vd->viewmat); + gluDeleteQuadric(qobj); + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void draw_manipulator_scale(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + float cywid= 0.25f*0.01f*(float)U.tw_handlesize; + float cusize= cywid*0.75f, dz; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_SCALE_C)==0) return; + + glDisable(GL_DEPTH_TEST); + + /* not in combo mode */ + if( (combo & (V3D_MANIP_TRANSLATE|V3D_MANIP_ROTATE))==0) { + float size, unitmat[4][4]; + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_SCALE_C); + + manipulator_setcolor('c', colcode); + glPushMatrix(); + size= screen_aligned(mat); + Mat4One(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat); + glPopMatrix(); + + dz= 1.0; + } + else dz= 1.0f-4.0f*cusize; + + if(moving) { + float matt[4][4]; + + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + Mat4MulMat34(matt, Trans.mat, mat); + mymultmatrix(matt); + glFrontFace( is_mat4_flipped(matt)?GL_CW:GL_CCW); + } + else { + mymultmatrix(mat); + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + } + + /* axis */ + + /* in combo mode, this is always drawn as first type */ + draw_manipulator_axes(colcode, drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z); + + /* Z cube */ + glTranslatef(0.0, 0.0, dz); + if(drawflags & MAN_SCALE_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Z); + manipulator_setcolor('z', colcode); + drawsolidcube(cusize); + } + /* X cube */ + glTranslatef(dz, 0.0, -dz); + if(drawflags & MAN_SCALE_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_X); + manipulator_setcolor('x', colcode); + drawsolidcube(cusize); + } + /* Y cube */ + glTranslatef(-dz, dz, 0.0); + if(drawflags & MAN_SCALE_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_SCALE_Y); + manipulator_setcolor('y', colcode); + drawsolidcube(cusize); + } + + /* if shiftkey, center point as last, for selectbuffer order */ + if(G.f & G_PICKSEL) { + if(G.qual & LR_SHIFTKEY) { + glTranslatef(0.0, -dz, 0.0); + glLoadName(MAN_SCALE_C); + glBegin(GL_POINTS); + glVertex3f(0.0, 0.0, 0.0); + glEnd(); + } + } + + /* restore */ + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + glFrontFace(GL_CCW); +} + + +static void draw_cone(GLUquadricObj *qobj, float len, float width) +{ + glTranslatef(0.0, 0.0, -0.5f*len); + gluCylinder(qobj, width, 0.0, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, 0.5f*len); +} + +static void draw_cylinder(GLUquadricObj *qobj, float len, float width) +{ + + width*= 0.8f; // just for beauty + + glTranslatef(0.0, 0.0, -0.5f*len); + gluCylinder(qobj, width, width, len, 8, 1); + gluQuadricOrientation(qobj, GLU_INSIDE); + gluDisk(qobj, 0.0, width, 8, 1); + gluQuadricOrientation(qobj, GLU_OUTSIDE); + glTranslatef(0.0, 0.0, len); + gluDisk(qobj, 0.0, width, 8, 1); + glTranslatef(0.0, 0.0, -0.5f*len); +} + + +static void draw_manipulator_translate(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + GLUquadricObj *qobj; + float cylen= 0.01f*(float)U.tw_handlesize; + float cywid= 0.25f*cylen, dz, size; + float unitmat[4][4]; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_TRANS_C)==0) return; + + if(moving) glTranslatef(Trans.vec[0], Trans.vec[1], Trans.vec[2]); + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + gluQuadricDrawStyle(qobj, GLU_FILL); + + /* center circle, do not add to selection when shift is pressed (planar constraint) */ + if( (G.f & G_PICKSEL) && (G.qual & LR_SHIFTKEY)==0) glLoadName(MAN_TRANS_C); + + manipulator_setcolor('c', colcode); + glPushMatrix(); + size= screen_aligned(mat); + Mat4One(unitmat); + drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f*size, unitmat); + glPopMatrix(); + + /* and now apply matrix, we move to local matrix drawing */ + mymultmatrix(mat); + + /* axis */ + glLoadName(-1); + + // translate drawn as last, only axis when no combo with scale, or for ghosting + if((combo & V3D_MANIP_SCALE)==0 || colcode==MAN_GHOST) + draw_manipulator_axes(colcode, drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z); + + + /* offset in combo mode, for rotate a bit more */ + if(combo & (V3D_MANIP_ROTATE)) dz= 1.0f+2.0f*cylen; + else if(combo & (V3D_MANIP_SCALE)) dz= 1.0f+0.5f*cylen; + else dz= 1.0f; + + /* Z Cone */ + glTranslatef(0.0, 0.0, dz); + if(drawflags & MAN_TRANS_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Z); + manipulator_setcolor('z', colcode); + draw_cone(qobj, cylen, cywid); + } + /* X Cone */ + glTranslatef(dz, 0.0, -dz); + if(drawflags & MAN_TRANS_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + draw_cone(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y Cone */ + glTranslatef(-dz, dz, 0.0); + if(drawflags & MAN_TRANS_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_TRANS_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + draw_cone(qobj, cylen, cywid); + } + + gluDeleteQuadric(qobj); + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + +static void draw_manipulator_rotate_cyl(float mat[][4], int moving, int drawflags, int combo, int colcode) +{ + GLUquadricObj *qobj; + float size; + float cylen= 0.01f*(float)U.tw_handlesize; + float cywid= 0.25f*cylen; + + /* when called while moving in mixed mode, do not draw when... */ + if((drawflags & MAN_ROT_C)==0) return; + + /* prepare for screen aligned draw */ + glPushMatrix(); + size= screen_aligned(mat); + + glDisable(GL_DEPTH_TEST); + + qobj= gluNewQuadric(); + + /* Screen aligned view rot circle */ + if(drawflags & MAN_ROT_V) { + float unitmat[4][4]; + Mat4One(unitmat); + + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_V); + BIF_ThemeColor(TH_TRANSFORM); + drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f*size, unitmat); + + if(moving) { + float vec[3]; + vec[0]= (float)(Trans.imval[0] - Trans.center2d[0]); + vec[1]= (float)(Trans.imval[1] - Trans.center2d[1]); + vec[2]= 0.0f; + Normalize(vec); + VecMulf(vec, 1.2f*size); + glBegin(GL_LINES); + glVertex3f(0.0, 0.0, 0.0); + glVertex3fv(vec); + glEnd(); + } + } + glPopMatrix(); + + /* apply the transform delta */ + if(moving) { + float matt[4][4]; + Mat4CpyMat4(matt, mat); // to copy the parts outside of [3][3] + if (Trans.flag & T_USES_MANIPULATOR) { + Mat4MulMat34(matt, Trans.mat, mat); + } + mymultmatrix(matt); + } + else { + mymultmatrix(mat); + } + + glFrontFace( is_mat4_flipped(mat)?GL_CW:GL_CCW); + + /* axis */ + if( (G.f & G_PICKSEL)==0 ) { + + // only draw axis when combo didn't draw scale axes + if((combo & V3D_MANIP_SCALE)==0) + draw_manipulator_axes(colcode, drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z); + + /* only has to be set when not in picking */ + gluQuadricDrawStyle(qobj, GLU_FILL); + } + + /* Z cyl */ + glTranslatef(0.0, 0.0, 1.0); + if(drawflags & MAN_ROT_Z) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Z); + manipulator_setcolor('z', colcode); + draw_cylinder(qobj, cylen, cywid); + } + /* X cyl */ + glTranslatef(1.0, 0.0, -1.0); + if(drawflags & MAN_ROT_X) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_X); + glRotatef(90.0, 0.0, 1.0, 0.0); + manipulator_setcolor('x', colcode); + draw_cylinder(qobj, cylen, cywid); + glRotatef(-90.0, 0.0, 1.0, 0.0); + } + /* Y cylinder */ + glTranslatef(-1.0, 1.0, 0.0); + if(drawflags & MAN_ROT_Y) { + if(G.f & G_PICKSEL) glLoadName(MAN_ROT_Y); + glRotatef(-90.0, 1.0, 0.0, 0.0); + manipulator_setcolor('y', colcode); + draw_cylinder(qobj, cylen, cywid); + } + + /* restore */ + + gluDeleteQuadric(qobj); + myloadmatrix(G.vd->viewmat); + + if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); + +} + + +/* ********************************************* */ + +static float get_manipulator_drawsize(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + float size = get_drawsize(v3d, v3d->twmat[3]); + + size*= (float)U.tw_size; + + return size; +} + +/* exported to transform_constraints.c */ +/* mat, vec = default orientation and location */ +/* type = transform type */ +/* axis = x, y, z, c */ +/* col: 0 = colored, 1 = moving, 2 = ghost */ +void draw_manipulator_ext(ScrArea *sa, int type, char axis, int col, float vec[3], float mat[][3]) +{ + int drawflags= 0; + float mat4[4][4]; + int colcode; + + Mat4CpyMat3(mat4, mat); + VECCOPY(mat4[3], vec); + + Mat4MulFloat3((float *)mat4, get_manipulator_drawsize(sa)); + + glEnable(GL_BLEND); // let's do it transparent by default + if(col==0) colcode= MAN_RGB; + else if(col==1) colcode= MAN_MOVECOL; + else colcode= MAN_GHOST; + + + if(type==TFM_ROTATION) { + if(axis=='x') drawflags= MAN_ROT_X; + else if(axis=='y') drawflags= MAN_ROT_Y; + else if(axis=='z') drawflags= MAN_ROT_Z; + else drawflags= MAN_ROT_C; + + draw_manipulator_rotate_cyl(mat4, col, drawflags, V3D_MANIP_ROTATE, colcode); + } + else if(type==TFM_RESIZE) { + if(axis=='x') drawflags= MAN_SCALE_X; + else if(axis=='y') drawflags= MAN_SCALE_Y; + else if(axis=='z') drawflags= MAN_SCALE_Z; + else drawflags= MAN_SCALE_C; + + draw_manipulator_scale(mat4, col, drawflags, V3D_MANIP_SCALE, colcode); + } + else { + if(axis=='x') drawflags= MAN_TRANS_X; + else if(axis=='y') drawflags= MAN_TRANS_Y; + else if(axis=='z') drawflags= MAN_TRANS_Z; + else drawflags= MAN_TRANS_C; + + draw_manipulator_translate(mat4, 0, drawflags, V3D_MANIP_TRANSLATE, colcode); + } + + + glDisable(GL_BLEND); +} + +/* main call, does calc centers & orientation too */ +/* uses global G.moving */ +static int drawflags= 0xFFFF; // only for the calls below, belongs in scene...? +void BIF_draw_manipulator(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + int totsel; + + if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return; + if(G.moving && (G.moving & G_TRANSFORM_MANIP)==0) return; + + if(G.moving==0) { + v3d->twflag &= ~V3D_DRAW_MANIPULATOR; + + totsel= calc_manipulator_stats(sa); + if(totsel==0) return; + drawflags= v3d->twdrawflag; /* set in calc_manipulator_stats */ + + v3d->twflag |= V3D_DRAW_MANIPULATOR; + + /* now we can define center */ + switch(v3d->around) { + case V3D_CENTER: + case V3D_ACTIVE: + v3d->twmat[3][0]= (G.scene->twmin[0] + G.scene->twmax[0])/2.0f; + v3d->twmat[3][1]= (G.scene->twmin[1] + G.scene->twmax[1])/2.0f; + v3d->twmat[3][2]= (G.scene->twmin[2] + G.scene->twmax[2])/2.0f; + if(v3d->around==V3D_ACTIVE && G.obedit==NULL) { + Object *ob= OBACT; + if(ob && !(ob->flag & OB_POSEMODE)) + VECCOPY(v3d->twmat[3], ob->obmat[3]); + } + break; + case V3D_LOCAL: + case V3D_CENTROID: + VECCOPY(v3d->twmat[3], G.scene->twcent); + break; + case V3D_CURSOR: + VECCOPY(v3d->twmat[3], give_cursor()); + break; + } + + Mat4MulFloat3((float *)v3d->twmat, get_manipulator_drawsize(sa)); + } + + if(v3d->twflag & V3D_DRAW_MANIPULATOR) { + + if(v3d->twtype & V3D_MANIP_ROTATE) { + + /* rotate has special ghosting draw, for pie chart */ + if(G.moving) draw_manipulator_rotate_ghost(v3d->twmat, drawflags); + + if(G.moving) glEnable(GL_BLEND); + + if(G.rt==3) { + if(G.moving) draw_manipulator_rotate_cyl(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + else draw_manipulator_rotate_cyl(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + else + draw_manipulator_rotate(v3d->twmat, G.moving, drawflags, v3d->twtype); + + glDisable(GL_BLEND); + } + if(v3d->twtype & V3D_MANIP_SCALE) { + if(G.moving) { + glEnable(GL_BLEND); + draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST); + draw_manipulator_scale(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + glDisable(GL_BLEND); + } + else draw_manipulator_scale(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + if(v3d->twtype & V3D_MANIP_TRANSLATE) { + if(G.moving) { + glEnable(GL_BLEND); + draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_GHOST); + draw_manipulator_translate(v3d->twmat, 1, drawflags, v3d->twtype, MAN_MOVECOL); + glDisable(GL_BLEND); + } + else draw_manipulator_translate(v3d->twmat, 0, drawflags, v3d->twtype, MAN_RGB); + } + } +} + +static int manipulator_selectbuf(ScrArea *sa, float hotspot) +{ + View3D *v3d= sa->spacedata.first; + rctf rect; + GLuint buffer[64]; // max 4 items per select, so large enuf + short hits, mval[2]; + + G.f |= G_PICKSEL; + + getmouseco_areawin(mval); + rect.xmin= mval[0]-hotspot; + rect.xmax= mval[0]+hotspot; + rect.ymin= mval[1]-hotspot; + rect.ymax= mval[1]+hotspot; + + /* get rid of overlay button matrix */ + persp(PERSP_VIEW); + + setwinmatrixview3d(sa->winx, sa->winy, &rect); + Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat); + + glSelectBuffer( 64, buffer); + glRenderMode(GL_SELECT); + glInitNames(); /* these two calls whatfor? It doesnt work otherwise */ + glPushName(-2); + + /* do the drawing */ + if(v3d->twtype & V3D_MANIP_ROTATE) { + if(G.rt==3) draw_manipulator_rotate_cyl(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + else draw_manipulator_rotate(v3d->twmat, 0, MAN_ROT_C & v3d->twdrawflag, v3d->twtype); + } + if(v3d->twtype & V3D_MANIP_SCALE) + draw_manipulator_scale(v3d->twmat, 0, MAN_SCALE_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + if(v3d->twtype & V3D_MANIP_TRANSLATE) + draw_manipulator_translate(v3d->twmat, 0, MAN_TRANS_C & v3d->twdrawflag, v3d->twtype, MAN_RGB); + + glPopName(); + hits= glRenderMode(GL_RENDER); + + G.f &= ~G_PICKSEL; + setwinmatrixview3d(sa->winx, sa->winy, NULL); + Mat4MulMat4(v3d->persmat, v3d->viewmat, sa->winmat); + + persp(PERSP_WIN); + + if(hits==1) return buffer[3]; + else if(hits>1) { + GLuint val, dep, mindep=0, mindeprot=0, minval=0, minvalrot=0; + int a; + + /* we compare the hits in buffer, but value centers highest */ + /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */ + + for(a=0; a<hits; a++) { + dep= buffer[4*a + 1]; + val= buffer[4*a + 3]; + + if(val==MAN_TRANS_C) return MAN_TRANS_C; + else if(val==MAN_SCALE_C) return MAN_SCALE_C; + else { + if(val & MAN_ROT_C) { + if(minvalrot==0 || dep<mindeprot) { + mindeprot= dep; + minvalrot= val; + } + } + else { + if(minval==0 || dep<mindep) { + mindep= dep; + minval= val; + } + } + } + } + + if(minval) + return minval; + else + return minvalrot; + } + return 0; +} + +/* return 0; nothing happened */ +int BIF_do_manipulator(ScrArea *sa) +{ + View3D *v3d= sa->spacedata.first; + int val; + + if(!(v3d->twflag & V3D_USE_MANIPULATOR)) return 0; + if(!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return 0; + + // find the hotspots first test narrow hotspot + val= manipulator_selectbuf(sa, 0.5f*(float)U.tw_hotspot); + if(val) { + checkFirstTime(); // TEMPORARY, check this before doing any transform call. + // drawflags still global, for drawing call above + drawflags= manipulator_selectbuf(sa, 0.2f*(float)U.tw_hotspot); + if(drawflags==0) drawflags= val; + + if (drawflags & MAN_TRANS_C) { + initManipulator(TFM_TRANSLATION); + switch(drawflags) { + case MAN_TRANS_C: + break; + case MAN_TRANS_X: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_Y|MAN_TRANS_Z; + BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_TRANS_Y: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_X|MAN_TRANS_Z; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_TRANS_Z: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_TRANS_X|MAN_TRANS_Y; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + else if (drawflags & MAN_SCALE_C) { + initManipulator(TFM_RESIZE); + switch(drawflags) { + case MAN_SCALE_X: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_Y|MAN_SCALE_Z; + BIF_setDualAxisConstraint(v3d->twmat[1], v3d->twmat[2], " Y+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_SCALE_Y: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_X|MAN_SCALE_Z; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[2], " X+Z"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_SCALE_Z: + if(G.qual & LR_SHIFTKEY) { + drawflags= MAN_SCALE_X|MAN_SCALE_Y; + BIF_setDualAxisConstraint(v3d->twmat[0], v3d->twmat[1], " X+Y"); + } + else + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + else if (drawflags == MAN_ROT_T) { /* trackbal need special case, init is different */ + initManipulator(TFM_TRACKBALL); + ManipulatorTransform(); + } + else if (drawflags & MAN_ROT_C) { + initManipulator(TFM_ROTATION); + switch(drawflags) { + case MAN_ROT_X: + BIF_setSingleAxisConstraint(v3d->twmat[0], " X"); + break; + case MAN_ROT_Y: + BIF_setSingleAxisConstraint(v3d->twmat[1], " Y"); + break; + case MAN_ROT_Z: + BIF_setSingleAxisConstraint(v3d->twmat[2], " Z"); + break; + } + ManipulatorTransform(); + } + } + /* after transform, restore drawflags */ + drawflags= 0xFFFF; + + return val; +} + +#endif diff --git a/source/blender/editors/transform/transform_ndofinput.c b/source/blender/editors/transform/transform_ndofinput.c new file mode 100644 index 00000000000..c52492ebd6b --- /dev/null +++ b/source/blender/editors/transform/transform_ndofinput.c @@ -0,0 +1,155 @@ +/** + * $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: all of this file. + * + * Contributor(s): Martin Poirier + * + * ***** END GPL LICENSE BLOCK ***** + */ + + #include <math.h> /* fabs */ +#include <stdio.h> /* for sprintf */ + +#include "BKE_global.h" /* for G */ +#include "BKE_utildefines.h" /* ABS */ + +#include "DNA_view3d_types.h" /* for G.vd (view3d) */ + +#include "WM_types.h" + +#include "transform.h" + +static int updateNDofMotion(NDofInput *n); // return 0 when motion is null +static void resetNDofInput(NDofInput *n); + +void initNDofInput(NDofInput *n) +{ + int i; + + n->flag = 0; + n->axis = 0; + + resetNDofInput(n); + + for(i = 0; i < 3; i++) + { + n->factor[i] = 1.0f; + } +} + +static void resetNDofInput(NDofInput *n) +{ + int i; + for(i = 0; i < 6; i++) + { + n->fval[i] = 0.0f; + } +} + + +int handleNDofInput(NDofInput *n, wmEvent *event) +{ + int retval = 0; + // TRANSFORM_FIX_ME +#if 0 + switch(event) + { + case NDOFMOTION: + if (updateNDofMotion(n) == 0) + { + retval = NDOF_NOMOVE; + } + else + { + retval = NDOF_REFRESH; + } + break; + case NDOFBUTTON: + if (val == 1) + { + retval = NDOF_CONFIRM; + } + else if (val == 2) + { + retval = NDOF_CANCEL; + resetNDofInput(n); + n->flag &= ~NDOF_INIT; + } + break; + } +#endif + return retval; +} + +int hasNDofInput(NDofInput *n) +{ + return (n->flag & NDOF_INIT) == NDOF_INIT; +} + +void applyNDofInput(NDofInput *n, float *vec) +{ + if (hasNDofInput(n)) + { + int i, j; + + for (i = 0, j = 0; i < 6; i++) + { + if (n->axis & (1 << i)) + { + vec[j] = n->fval[i] * n->factor[j]; + j++; + } + } + } +} + + +static int updateNDofMotion(NDofInput *n) +{ + float fval[7]; + int i; + int retval = 0; + +// TRANSFORM_FIX_ME +#if 0 + getndof(fval); + + if (G.vd->ndoffilter) + filterNDOFvalues(fval); +#endif + + for(i = 0; i < 6; i++) + { + if (!retval && fval[i] != 0.0f) + { + retval = 1; + } + + n->fval[i] += fval[i] / 1024.0f; + } + + n->flag |= NDOF_INIT; + + return retval; +} + + + + diff --git a/source/blender/editors/transform/transform_numinput.c b/source/blender/editors/transform/transform_numinput.c new file mode 100644 index 00000000000..d4384f2cf2f --- /dev/null +++ b/source/blender/editors/transform/transform_numinput.c @@ -0,0 +1,258 @@ +/** + * $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 <math.h> /* fabs */ +#include <stdio.h> /* for sprintf */ + +#include "BKE_global.h" /* for G */ +#include "BKE_utildefines.h" /* ABS */ + +#include "WM_types.h" + +#include "transform.h" + +/* ************************** Functions *************************** */ + +/* ************************** NUMINPUT **************************** */ + +void initNumInput(NumInput *n) +{ + n->flag = + n->idx = + n->idx_max = + n->ctrl[0] = + n->ctrl[1] = + n->ctrl[2] = 0; + + n->val[0] = + n->val[1] = + n->val[2] = 0.0f; +} + +void outputNumInput(NumInput *n, char *str) +{ + char cur; + short i, j; + + for (j=0; j<=n->idx_max; j++) { + /* if AFFECTALL and no number typed and cursor not on number, use first number */ + if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0) + i = 0; + else + i = j; + + if (n->idx != i) + cur = ' '; + else + cur = '|'; + + if( n->val[i] > 1e10 || n->val[i] < -1e10 ) + sprintf(&str[j*20], "%.4e%c", n->val[i], cur); + else + switch (n->ctrl[i]) { + case 0: + sprintf(&str[j*20], "NONE%c", cur); + break; + case 1: + case -1: + sprintf(&str[j*20], "%.0f%c", n->val[i], cur); + break; + case 10: + case -10: + sprintf(&str[j*20], "%.f.%c", n->val[i], cur); + break; + case 100: + case -100: + sprintf(&str[j*20], "%.1f%c", n->val[i], cur); + break; + case 1000: + case -1000: + sprintf(&str[j*20], "%.2f%c", n->val[i], cur); + break; + case 10000: + case -10000: + sprintf(&str[j*20], "%.3f%c", n->val[i], cur); + break; + default: + sprintf(&str[j*20], "%.4e%c", n->val[i], cur); + } + } +} + +short hasNumInput(NumInput *n) +{ + short i; + + for (i=0; i<=n->idx_max; i++) { + if (n->ctrl[i]) + return 1; + } + + return 0; +} + +void applyNumInput(NumInput *n, float *vec) +{ + short i, j; + float val[3]; + + if (hasNumInput(n)) { + convertDisplayNumToVec(n->val, val); + + for (j=0; j<=n->idx_max; j++) { + /* if AFFECTALL and no number typed and cursor not on number, use first number */ + if (n->flag & NUM_AFFECT_ALL && n->idx != j && n->ctrl[j] == 0) + i = 0; + else + i = j; + + if (n->ctrl[i] == 0 && n->flag & NUM_NULL_ONE) { + vec[j] = 1.0f; + } + else if (val[i] == 0.0f && n->flag & NUM_NO_ZERO) { + vec[j] = 0.0001f; + } + else { + vec[j] = val[i]; + } + } + } +} + +char handleNumInput(NumInput *n, wmEvent *event) +{ + float Val = 0; + short idx = n->idx, idx_max = n->idx_max; + + switch (event->type) { + case BACKSPACEKEY: + if (n->ctrl[idx] == 0) { + n->val[0] = + n->val[1] = + n->val[2] = 0.0f; + n->ctrl[0] = + n->ctrl[1] = + n->ctrl[2] = 0; + } + else { + n->val[idx] = 0.0f; + n->ctrl[idx] = 0; + } + break; + case PERIODKEY: + case PADPERIOD: + if (n->flag & NUM_NO_FRACTION) + break; + + switch (n->ctrl[idx]) + { + case 0: + case 1: + n->ctrl[idx] = 10; + break; + case -1: + n->ctrl[idx] = -10; + } + break; + case PADMINUS: + if(event->alt) + break; + case MINUSKEY: + if (n->flag & NUM_NO_NEGATIVE) + break; + + if (n->ctrl[idx]) { + n->ctrl[idx] *= -1; + n->val[idx] *= -1; + } + else + n->ctrl[idx] = -1; + break; + case TABKEY: + idx++; + if (idx > idx_max) + idx = 0; + n->idx = idx; + break; + case PAD9: + case NINEKEY: + Val += 1.0f; + case PAD8: + case EIGHTKEY: + Val += 1.0f; + case PAD7: + case SEVENKEY: + Val += 1.0f; + case PAD6: + case SIXKEY: + Val += 1.0f; + case PAD5: + case FIVEKEY: + Val += 1.0f; + case PAD4: + case FOURKEY: + Val += 1.0f; + case PAD3: + case THREEKEY: + Val += 1.0f; + case PAD2: + case TWOKEY: + Val += 1.0f; + case PAD1: + case ONEKEY: + Val += 1.0f; + case PAD0: + case ZEROKEY: + if (!n->ctrl[idx]) + n->ctrl[idx] = 1; + + if (fabs(n->val[idx]) > 9999999.0f); + else if (n->ctrl[idx] == 1) { + n->val[idx] *= 10; + n->val[idx] += Val; + } + else if (n->ctrl[idx] == -1) { + n->val[idx] *= 10; + n->val[idx] -= Val; + } + else { + /* float resolution breaks when over six digits after comma */ + if( ABS(n->ctrl[idx]) < 10000000) { + n->val[idx] += Val / (float)n->ctrl[idx]; + n->ctrl[idx] *= 10; + } + } + break; + default: + return 0; + } + + /* REDRAW SINCE NUMBERS HAVE CHANGED */ + return 1; +} diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c new file mode 100644 index 00000000000..12a9c0e7214 --- /dev/null +++ b/source/blender/editors/transform/transform_orientations.c @@ -0,0 +1,781 @@ +/** + * $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. + * + * Contributor(s): Martin Poirier + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_armature_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_listBase.h" +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_armature.h" +#include "BKE_context.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" + +//#include "BIF_editmesh.h" +#include "BIF_editarmature.h" +//#include "BIF_interface.h" +//#include "BIF_space.h" +//#include "BIF_toolbox.h" + +#include "transform.h" + +#if 0 // TRANSFORM_FIX_ME + +/* *********************** TransSpace ************************** */ + +void BIF_clearTransformOrientation(void) +{ + ListBase *transform_spaces = &G.scene->transform_spaces; + BLI_freelistN(transform_spaces); + + // TRANSFORM_FIX_ME + // Need to loop over all view3d +// if (G.vd->twmode >= V3D_MANIP_CUSTOM) +// G.vd->twmode = V3D_MANIP_GLOBAL; /* fallback to global */ +} + +void BIF_manageTransformOrientation(int confirm, int set) { + Object *ob = OBACT; + int index = -1; + + if (G.obedit) { + if (G.obedit->type == OB_MESH) + index = manageMeshSpace(confirm, set); + else if (G.obedit->type == OB_ARMATURE) + index = manageBoneSpace(confirm, set); + } + else if (ob && (ob->flag & OB_POSEMODE)) { + index = manageBoneSpace(confirm, set); + } + else { + index = manageObjectSpace(confirm, set); + } + + if (set && index != -1) + { + BIF_selectTransformOrientationFromIndex(index); + } +} + +int manageObjectSpace(int confirm, int set) { + Base *base = BASACT; + + if (base == NULL) + return -1; + + if (confirm == 0) { + if (set && pupmenu("Custom Orientation %t|Add and Use Active Object%x1") != 1) { + return -1; + } + else if (set == 0 && pupmenu("Custom Orientation %t|Add Active Object%x1") != 1) { + return -1; + } + } + + return addObjectSpace(base->object); +} + +/* return 1 on confirm */ +int confirmSpace(int set, char text[]) +{ + char menu[64]; + + if (set) { + sprintf(menu, "Custom Orientation %%t|Add and Use %s%%x1", text); + } + else { + sprintf(menu, "Custom Orientation %%t|Add %s%%x1", text); + } + + if (pupmenu(menu) == 1) { + return 1; + } + else { + return 0; + } +} + +int manageBoneSpace(int confirm, int set) { + float mat[3][3]; + float normal[3], plane[3]; + char name[36] = ""; + int index; + + getTransformOrientation(normal, plane, 0); + + if (confirm == 0 && confirmSpace(set, "Bone") == 0) { + return -1; + } + + if (createSpaceNormalTangent(mat, normal, plane) == 0) { + error("Cannot use zero-length bone"); + return -1; + } + + strcpy(name, "Bone"); + + /* Input name */ + sbutton(name, 1, 35, "name: "); + + index = addMatrixSpace(mat, name); + return index; +} + +int manageMeshSpace(int confirm, int set) { + float mat[3][3]; + float normal[3], plane[3]; + char name[36] = ""; + int index; + int type; + + type = getTransformOrientation(normal, plane, 0); + + switch (type) + { + case ORIENTATION_VERT: + if (confirm == 0 && confirmSpace(set, "vertex") == 0) { + return -1; + } + + if (createSpaceNormal(mat, normal) == 0) { + error("Cannot use vertex with zero-length normal"); + return -1; + } + + strcpy(name, "Vertex"); + break; + case ORIENTATION_EDGE: + if (confirm == 0 && confirmSpace(set, "Edge") == 0) { + return -1; + } + + if (createSpaceNormalTangent(mat, normal, plane) == 0) { + error("Cannot use zero-length edge"); + return -1; + } + + strcpy(name, "Edge"); + break; + case ORIENTATION_FACE: + if (confirm == 0 && confirmSpace(set, "Face") == 0) { + return -1; + } + + if (createSpaceNormalTangent(mat, normal, plane) == 0) { + error("Cannot use zero-area face"); + return -1; + } + + strcpy(name, "Face"); + break; + default: + return -1; + break; + } + + /* Input name */ + sbutton(name, 1, 35, "name: "); + + index = addMatrixSpace(mat, name); + return index; +} + +int createSpaceNormal(float mat[3][3], float normal[3]) +{ + float tangent[3] = {0.0f, 0.0f, 1.0f}; + + VECCOPY(mat[2], normal); + if (Normalize(mat[2]) == 0.0f) { + return 0; /* error return */ + } + + Crossf(mat[0], mat[2], tangent); + if (Inpf(mat[0], mat[0]) == 0.0f) { + tangent[0] = 1.0f; + tangent[1] = tangent[2] = 0.0f; + Crossf(mat[0], tangent, mat[2]); + } + + Crossf(mat[1], mat[2], mat[0]); + + Mat3Ortho(mat); + + return 1; +} + +int createSpaceNormalTangent(float mat[3][3], float normal[3], float tangent[3]) +{ + VECCOPY(mat[2], normal); + if (Normalize(mat[2]) == 0.0f) { + return 0; /* error return */ + } + + /* preempt zero length tangent from causing trouble */ + if (tangent[0] == 0 && tangent[1] == 0 && tangent[2] == 0) + { + tangent[2] = 1; + } + + Crossf(mat[0], mat[2], tangent); + if (Normalize(mat[0]) == 0.0f) { + return 0; /* error return */ + } + + Crossf(mat[1], mat[2], mat[0]); + + Mat3Ortho(mat); + + return 1; +} + + +int addObjectSpace(Object *ob) { + float mat[3][3]; + char name[36] = ""; + + Mat3CpyMat4(mat, ob->obmat); + Mat3Ortho(mat); + + strncpy(name, ob->id.name+2, 35); + + /* Input name */ + sbutton(name, 1, 35, "name: "); + + return addMatrixSpace(mat, name); +} + +int addMatrixSpace(float mat[3][3], char name[]) { + ListBase *transform_spaces = &G.scene->transform_spaces; + TransformOrientation *ts; + int index = 0; + + /* if name is found in list, reuse that transform space */ + for (index = 0, ts = transform_spaces->first; ts; ts = ts->next, index++) { + if (strncmp(ts->name, name, 35) == 0) { + break; + } + } + + /* if not, create a new one */ + if (ts == NULL) + { + ts = MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix"); + BLI_addtail(transform_spaces, ts); + strncpy(ts->name, name, 35); + } + + /* copy matrix into transform space */ + Mat3CpyMat3(ts->mat, mat); + + BIF_undo_push("Add/Update Transform Orientation"); + + return index; +} + +void BIF_removeTransformOrientation(TransformOrientation *target) { + ListBase *transform_spaces = &G.scene->transform_spaces; + TransformOrientation *ts = transform_spaces->first; + int selected_index = (G.vd->twmode - V3D_MANIP_CUSTOM); + int i; + + for (i = 0, ts = transform_spaces->first; ts; ts = ts->next, i++) { + if (ts == target) { + if (selected_index == i) { + G.vd->twmode = V3D_MANIP_GLOBAL; /* fallback to global */ + } + else if (selected_index > i) + G.vd->twmode--; + + BLI_freelinkN(transform_spaces, ts); + break; + } + } + BIF_undo_push("Remove Transform Orientation"); +} + +void BIF_selectTransformOrientation(TransformOrientation *target) { + ListBase *transform_spaces = &G.scene->transform_spaces; + TransformOrientation *ts = transform_spaces->first; + int i; + + for (i = 0, ts = transform_spaces->first; ts; ts = ts->next, i++) { + if (ts == target) { + G.vd->twmode = V3D_MANIP_CUSTOM + i; + break; + } + } +} + +void BIF_selectTransformOrientationFromIndex(int index) { + G.vd->twmode = V3D_MANIP_CUSTOM + index; +} + +char * BIF_menustringTransformOrientation(char *title) { + char menu[] = "%t|Global%x0|Local%x1|Normal%x2|View%x3"; + ListBase *transform_spaces = &G.scene->transform_spaces; + TransformOrientation *ts; + int i = V3D_MANIP_CUSTOM; + char *str_menu, *p; + + + str_menu = MEM_callocN(strlen(menu) + strlen(title) + 1 + 40 * BIF_countTransformOrientation(), "UserTransSpace from matrix"); + p = str_menu; + + p += sprintf(str_menu, "%s", title); + p += sprintf(p, "%s", menu); + + for (ts = transform_spaces->first; ts; ts = ts->next) { + p += sprintf(p, "|%s%%x%d", ts->name, i++); + } + + return str_menu; +} + +int BIF_countTransformOrientation() { + ListBase *transform_spaces = &G.scene->transform_spaces; + TransformOrientation *ts; + int count = 0; + + for (ts = transform_spaces->first; ts; ts = ts->next) { + count++; + } + + return count; +} + +void applyTransformOrientation() { + TransInfo *t = BIF_GetTransInfo(); + TransformOrientation *ts; + int selected_index = (G.vd->twmode - V3D_MANIP_CUSTOM); + int i; + + if (selected_index >= 0) { + for (i = 0, ts = G.scene->transform_spaces.first; ts; ts = ts->next, i++) { + if (selected_index == i) { + strcpy(t->spacename, ts->name); + Mat3CpyMat3(t->spacemtx, ts->mat); + Mat4CpyMat3(G.vd->twmat, ts->mat); + break; + } + } + } +} + +#endif // TRANSFORM_FIX_ME + +static int count_bone_select(bArmature *arm, ListBase *lb, int do_it) +{ + Bone *bone; + int do_next; + int total = 0; + + for(bone= lb->first; bone; bone= bone->next) { + bone->flag &= ~BONE_TRANSFORM; + do_next = do_it; + if(do_it) { + if(bone->layer & arm->layer) { + if (bone->flag & BONE_SELECTED) { + bone->flag |= BONE_TRANSFORM; + total++; + do_next= 0; // no transform on children if one parent bone is selected + } + } + } + total += count_bone_select(arm, &bone->childbase, do_next); + } + + return total; +} + +int getTransformOrientation(bContext *C, float normal[3], float plane[3], int activeOnly) +{ + Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + Base *base; + Object *ob = OBACT; + int result = ORIENTATION_NONE; + + normal[0] = normal[1] = normal[2] = 0; + plane[0] = plane[1] = plane[2] = 0; + + if(G.obedit) + { + float imat[3][3], mat[3][3]; + + /* we need the transpose of the inverse for a normal... */ + Mat3CpyMat4(imat, ob->obmat); + + Mat3Inv(mat, imat); + Mat3Transp(mat); + + ob= G.obedit; + + if(G.obedit->type==OB_MESH) + { + EditMesh *em = G.editMesh; + EditVert *eve; + EditSelection ese; + float vec[3]= {0,0,0}; + + /* USE LAST SELECTED WITH ACTIVE */ + if (activeOnly && EM_get_actSelection(&ese)) + { + EM_editselection_normal(normal, &ese); + EM_editselection_plane(plane, &ese); + + switch (ese.type) + { + case EDITVERT: + result = ORIENTATION_VERT; + break; + case EDITEDGE: + result = ORIENTATION_EDGE; + break; + case EDITFACE: + result = ORIENTATION_FACE; + break; + } + } + else + { + if (G.totfacesel >= 1) + { + EditFace *efa; + + for(efa= em->faces.first; efa; efa= efa->next) + { + if(efa->f & SELECT) + { + VECADD(normal, normal, efa->n); + VecSubf(vec, efa->v2->co, efa->v1->co); + VECADD(plane, plane, vec); + } + } + + result = ORIENTATION_FACE; + } + else if (G.totvertsel == 3) + { + EditVert *v1 = NULL, *v2 = NULL, *v3 = NULL; + float cotangent[3]; + + for (eve = em->verts.first; eve; eve = eve->next) + { + if ( eve->f & SELECT ) { + if (v1 == NULL) { + v1 = eve; + } + else if (v2 == NULL) { + v2 = eve; + } + else { + v3 = eve; + + VecSubf(plane, v2->co, v1->co); + VecSubf(cotangent, v3->co, v2->co); + Crossf(normal, cotangent, plane); + break; + } + } + } + + /* if there's an edge available, use that for the tangent */ + if (G.totedgesel >= 1) + { + EditEdge *eed = NULL; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f & SELECT) { + VecSubf(plane, eed->v2->co, eed->v1->co); + break; + } + } + } + + result = ORIENTATION_FACE; + } + else if (G.totedgesel == 1) + { + EditEdge *eed; + + for(eed= em->edges.first; eed; eed= eed->next) { + if(eed->f & SELECT) { + /* use average vert normals as plane and edge vector as normal */ + VECCOPY(plane, eed->v1->no); + VECADD(plane, plane, eed->v2->no); + VecSubf(normal, eed->v2->co, eed->v1->co); + break; + } + } + result = ORIENTATION_EDGE; + } + else if (G.totvertsel == 2) + { + EditVert *v1 = NULL, *v2 = NULL; + + for (eve = em->verts.first; eve; eve = eve->next) + { + if ( eve->f & SELECT ) { + if (v1 == NULL) { + v1 = eve; + } + else { + v2 = eve; + + VECCOPY(plane, v1->no); + VECADD(plane, plane, v2->no); + VecSubf(normal, v2->co, v1->co); + break; + } + } + } + result = ORIENTATION_EDGE; + } + else if (G.totvertsel == 1) + { + for (eve = em->verts.first; eve; eve = eve->next) + { + if ( eve->f & SELECT ) { + VECCOPY(normal, eve->no); + break; + } + } + result = ORIENTATION_VERT; + } + else if (G.totvertsel > 3) + { + normal[0] = normal[1] = normal[2] = 0; + + for (eve = em->verts.first; eve; eve = eve->next) + { + if ( eve->f & SELECT ) { + VecAddf(normal, normal, eve->no); + } + } + Normalize(normal); + result = ORIENTATION_VERT; + } + } + } /* end editmesh */ + else if ELEM3(G.obedit->type, OB_CURVE, OB_SURF, OB_FONT) + { + extern ListBase editNurb; /* BOOO! go away stupid extern */ + Nurb *nu; + BezTriple *bezt; + int a; + + for (nu = editNurb.first; nu; nu = nu->next) + { + /* only bezier has a normal */ + if((nu->type & 7) == CU_BEZIER) + { + bezt= nu->bezt; + a= nu->pntsu; + while(a--) + { + /* exception */ + if ( (bezt->f1 & SELECT) + (bezt->f2 & SELECT) + (bezt->f3 & SELECT) > SELECT ) + { + VecSubf(normal, bezt->vec[0], bezt->vec[2]); + } + else + { + if(bezt->f1) + { + VecSubf(normal, bezt->vec[0], bezt->vec[1]); + } + if(bezt->f2) + { + VecSubf(normal, bezt->vec[0], bezt->vec[2]); + } + if(bezt->f3) + { + VecSubf(normal, bezt->vec[1], bezt->vec[2]); + } + } + bezt++; + } + } + } + + if (normal[0] != 0 || normal[1] != 0 || normal[2] != 0) + { + result = ORIENTATION_NORMAL; + } + } + else if(G.obedit->type==OB_MBALL) + { + /* editmball.c */ + extern ListBase editelems; /* go away ! */ + MetaElem *ml, *ml_sel = NULL; + + /* loop and check that only one element is selected */ + for (ml = editelems.first; ml; ml = ml->next) + { + if (ml->flag & SELECT) { + if (ml_sel == NULL) + { + ml_sel = ml; + } + else + { + ml_sel = NULL; + break; + } + } + } + + if (ml_sel) + { + float mat[4][4]; + + /* Rotation of MetaElem is stored in quat */ + QuatToMat4(ml_sel->quat, mat); + + VECCOPY(normal, mat[2]); + VECCOPY(plane, mat[1]); + + VecMulf(plane, -1.0); + + result = ORIENTATION_NORMAL; + } + } + else if (G.obedit->type == OB_ARMATURE) + { + bArmature *arm = G.obedit->data; + EditBone *ebone; + + for (ebone = G.edbo.first; ebone; ebone=ebone->next) { + if (arm->layer & ebone->layer) + { + if (ebone->flag & BONE_SELECTED) + { + float mat[3][3]; + float vec[3]; + VecSubf(vec, ebone->tail, ebone->head); + Normalize(vec); + VecAddf(normal, normal, vec); + + vec_roll_to_mat3(vec, ebone->roll, mat); + VecAddf(plane, plane, mat[2]); + } + } + } + + Normalize(normal); + Normalize(plane); + + if (plane[0] != 0 || plane[1] != 0 || plane[2] != 0) + { + result = ORIENTATION_EDGE; + } + + } + + /* Vectors from edges don't need the special transpose inverse multiplication */ + if (result == ORIENTATION_EDGE) + { + Mat4Mul3Vecfl(ob->obmat, normal); + Mat4Mul3Vecfl(ob->obmat, plane); + } + else + { + Mat3MulVecfl(mat, normal); + Mat3MulVecfl(mat, plane); + } + } + else if(ob && (ob->flag & OB_POSEMODE)) + { + bArmature *arm= ob->data; + bPoseChannel *pchan; + int totsel; + + totsel = count_bone_select(arm, &arm->bonebase, 1); + if(totsel) { + float imat[3][3], mat[3][3]; + + /* use channels to get stats */ + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) { + VecAddf(normal, normal, pchan->pose_mat[2]); + VecAddf(plane, plane, pchan->pose_mat[1]); + } + } + VecMulf(plane, -1.0); + + /* we need the transpose of the inverse for a normal... */ + Mat3CpyMat4(imat, ob->obmat); + + Mat3Inv(mat, imat); + Mat3Transp(mat); + Mat3MulVecfl(mat, normal); + Mat3MulVecfl(mat, plane); + + result = ORIENTATION_EDGE; + } + } + else if(G.f & (G_VERTEXPAINT + G_TEXTUREPAINT + G_WEIGHTPAINT + G_SCULPTMODE)) + { + } + else if(G.f & G_PARTICLEEDIT) + { + } + else { + /* we need the one selected object, if its not active */ + ob = OBACT; + if(ob && !(ob->flag & SELECT)) ob = NULL; + + for(base= G.scene->base.first; base; base= base->next) { + if TESTBASELIB(v3d, base) { + if(ob == NULL) { + ob= base->object; + break; + } + } + } + + VECCOPY(normal, ob->obmat[2]); + VECCOPY(plane, ob->obmat[1]); + result = ORIENTATION_NORMAL; + } + + return result; +} diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c new file mode 100644 index 00000000000..a70ace8b9b5 --- /dev/null +++ b/source/blender/editors/transform/transform_snap.c @@ -0,0 +1,1306 @@ +/** + * $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): Martin Poirier + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <math.h> +#include <float.h> +#include <stdio.h> + +#include "PIL_time.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_meshdata_types.h" // Temporary, for snapping to other unselected meshes +#include "DNA_space_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +//#include "BDR_drawobject.h" +// +//#include "editmesh.h" +//#include "BIF_editsima.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" +//#include "BIF_mywindow.h" +//#include "BIF_resources.h" +//#include "BIF_screen.h" +//#include "BIF_editsima.h" +//#include "BIF_drawimage.h" +//#include "BIF_editmesh.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_anim.h" /* for duplis */ +#include "BKE_context.h" + +#include "ED_view3d.h" + +#include "MEM_guardedalloc.h" + +#include "transform.h" +#include "WM_types.h" +//#include "blendef.h" /* for selection modes */ + +static EditVert *EM_get_vert_for_index(int x) {return 0;} // XXX +static EditEdge *EM_get_edge_for_index(int x) {return 0;} // XXX +static EditFace *EM_get_face_for_index(int x) {return 0;} // XXX +static void EM_init_index_arrays(int x, int y, int z) {} // XXX +static void EM_free_index_arrays(void) {} // XXX + +/********************* PROTOTYPES ***********************/ + +void setSnappingCallback(TransInfo *t); + +void ApplySnapTranslation(TransInfo *t, float vec[3]); +void ApplySnapRotation(TransInfo *t, float *vec); +void ApplySnapResize(TransInfo *t, float *vec); + +void CalcSnapGrid(TransInfo *t, float *vec); +void CalcSnapGeometry(TransInfo *t, float *vec); + +void TargetSnapMedian(TransInfo *t); +void TargetSnapCenter(TransInfo *t); +void TargetSnapClosest(TransInfo *t); +void TargetSnapActive(TransInfo *t); + +float RotationBetween(TransInfo *t, float p1[3], float p2[3]); +float TranslationBetween(TransInfo *t, float p1[3], float p2[3]); +float ResizeBetween(TransInfo *t, float p1[3], float p2[3]); + +/* Modes */ +#define NOT_SELECTED 0 +#define NOT_ACTIVE 1 +int snapObjects(TransInfo *t, int *dist, float *loc, float *no, int mode); + + +/****************** IMPLEMENTATIONS *********************/ + +int BIF_snappingSupported(void) +{ + int status = 0; + + if (G.obedit == NULL || G.obedit->type==OB_MESH) /* only support object or mesh */ + { + status = 1; + } + + return status; +} + +void drawSnapping(TransInfo *t) +{ + if ((t->tsnap.status & (SNAP_ON|POINT_INIT|TARGET_INIT)) == (SNAP_ON|POINT_INIT|TARGET_INIT) && + (t->event->ctrl)) + { + + char col[4] = {1, 0, 1}; + //BIF_GetThemeColor3ubv(TH_TRANSFORM, col); + glColor4ub(col[0], col[1], col[2], 128); + + if (t->spacetype == SPACE_VIEW3D) { + View3D *v3d = t->view; + float unitmat[4][4]; + float size; + + glDisable(GL_DEPTH_TEST); + + size = get_drawsize(v3d, t->sa, t->tsnap.snapPoint); + + //size *= 0.5f * BIF_GetThemeValuef(TH_VERTEX_SIZE); + + glPushMatrix(); + + glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]); + + /* draw normal if needed */ + if (usingSnappingNormal(t) && validSnappingNormal(t)) + { + glBegin(GL_LINES); + glVertex3f(0, 0, 0); + glVertex3f(t->tsnap.snapNormal[0], t->tsnap.snapNormal[1], t->tsnap.snapNormal[2]); + glEnd(); + } + + /* sets view screen aligned */ + glRotatef( -360.0f*saacos(v3d->viewquat[0])/(float)M_PI, v3d->viewquat[1], v3d->viewquat[2], v3d->viewquat[3]); + + Mat4One(unitmat); + // TRANSFORM_FIX_ME + //drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat); + + glPopMatrix(); + + if(v3d->zbuf) + glEnable(GL_DEPTH_TEST); + } + else if (t->spacetype==SPACE_IMAGE) + { + /*This will not draw, and Im nor sure why - campbell */ + + /* + float xuser_asp, yuser_asp; + int wi, hi; + float w, h; + + calc_image_view(G.sima, 'f'); // float + myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax); + glLoadIdentity(); + + aspect_sima(G.sima, &xuser_asp, &yuser_asp); + + transform_width_height_tface_uv(&wi, &hi); + w = (((float)wi)/256.0f)*G.sima->zoom * xuser_asp; + h = (((float)hi)/256.0f)*G.sima->zoom * yuser_asp; + + cpack(0xFFFFFF); + glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], 0.0f); + + //glRectf(0,0,1,1); + + setlinestyle(0); + cpack(0x0); + fdrawline(-0.020/w, 0, -0.1/w, 0); + fdrawline(0.1/w, 0, .020/w, 0); + fdrawline(0, -0.020/h, 0, -0.1/h); + fdrawline(0, 0.1/h, 0, 0.020/h); + + glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f); + setlinestyle(0); + */ + + } + } +} + +int handleSnapping(TransInfo *t, wmEvent *event) +{ + int status = 0; + + if (BIF_snappingSupported() && event->type == TABKEY && event->shift) + { + /* toggle snap and reinit */ + G.scene->snap_flag ^= SCE_SNAP; + initSnapping(t); + status = 1; + } + + return status; +} + +void applySnapping(TransInfo *t, float *vec) +{ + if ((t->tsnap.status & SNAP_ON) && + (t->event->ctrl)) + { + double current = PIL_check_seconds_timer(); + + // Time base quirky code to go around findnearest slowness + /* !TODO! add exception for object mode, no need to slow it down then */ + if (current - t->tsnap.last >= 0.1) + { + t->tsnap.calcSnap(t, vec); + t->tsnap.targetSnap(t); + + t->tsnap.last = current; + } + if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT)) + { + t->tsnap.applySnap(t, vec); + } + } +} + +void resetSnapping(TransInfo *t) +{ + t->tsnap.status = 0; + t->tsnap.modePoint = 0; + t->tsnap.modeTarget = 0; + t->tsnap.last = 0; + t->tsnap.applySnap = NULL; + + t->tsnap.snapNormal[0] = 0; + t->tsnap.snapNormal[1] = 0; + t->tsnap.snapNormal[2] = 0; +} + +int usingSnappingNormal(TransInfo *t) +{ + if (G.scene->snap_flag & SCE_SNAP_ROTATE) + { + return 1; + } + else + { + return 0; + } +} + +int validSnappingNormal(TransInfo *t) +{ + if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT)) + { + if (Inpf(t->tsnap.snapNormal, t->tsnap.snapNormal) > 0) + { + return 1; + } + } + + return 0; +} + +void initSnapping(TransInfo *t) +{ + Scene *scene = t->scene; + Object *obedit = NULL; + resetSnapping(t); + + if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) && // Only 3D view or UV + (t->flag & T_CAMERA) == 0) { // Not with camera selected + setSnappingCallback(t); + + /* Edit mode */ + if (t->tsnap.applySnap != NULL && // A snapping function actually exist + (scene->snap_flag & SCE_SNAP) && // Only if the snap flag is on + (obedit != NULL && obedit->type==OB_MESH) && // Temporary limited to edit mode meshes + ((t->flag & T_PROP_EDIT) == 0) ) // No PET, obviously + { + t->tsnap.status |= SNAP_ON; + t->tsnap.modePoint = SNAP_GEO; + } + /* Object mode */ + else if (t->tsnap.applySnap != NULL && // A snapping function actually exist + (scene->snap_flag & SCE_SNAP) && // Only if the snap flag is on + (obedit == NULL) ) // Object Mode + { + t->tsnap.status |= SNAP_ON; + t->tsnap.modePoint = SNAP_GEO; + } + else + { + /* Grid if snap is not possible */ + t->tsnap.modePoint = SNAP_GRID; + } + } + else + { + /* Always grid outside of 3D view */ + t->tsnap.modePoint = SNAP_GRID; + } +} + +void setSnappingCallback(TransInfo *t) +{ + Scene *scene = t->scene; + t->tsnap.calcSnap = CalcSnapGeometry; + + switch(scene->snap_target) + { + case SCE_SNAP_TARGET_CLOSEST: + t->tsnap.modeTarget = SNAP_CLOSEST; + t->tsnap.targetSnap = TargetSnapClosest; + break; + case SCE_SNAP_TARGET_CENTER: + t->tsnap.modeTarget = SNAP_CENTER; + t->tsnap.targetSnap = TargetSnapCenter; + break; + case SCE_SNAP_TARGET_MEDIAN: + t->tsnap.modeTarget = SNAP_MEDIAN; + t->tsnap.targetSnap = TargetSnapMedian; + break; + case SCE_SNAP_TARGET_ACTIVE: + t->tsnap.modeTarget = SNAP_ACTIVE; + t->tsnap.targetSnap = TargetSnapActive; + break; + + } + + switch (t->mode) + { + case TFM_TRANSLATION: + t->tsnap.applySnap = ApplySnapTranslation; + t->tsnap.distance = TranslationBetween; + break; + case TFM_ROTATION: + t->tsnap.applySnap = ApplySnapRotation; + t->tsnap.distance = RotationBetween; + + // Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead + if (scene->snap_target == SCE_SNAP_TARGET_CENTER) { + t->tsnap.modeTarget = SNAP_MEDIAN; + t->tsnap.targetSnap = TargetSnapMedian; + } + break; + case TFM_RESIZE: + t->tsnap.applySnap = ApplySnapResize; + t->tsnap.distance = ResizeBetween; + + // Can't do TARGET_CENTER with resize, use TARGET_MEDIAN instead + if (scene->snap_target == SCE_SNAP_TARGET_CENTER) { + t->tsnap.modeTarget = SNAP_MEDIAN; + t->tsnap.targetSnap = TargetSnapMedian; + } + break; + default: + t->tsnap.applySnap = NULL; + break; + } +} + +/********************** APPLY **************************/ + +void ApplySnapTranslation(TransInfo *t, float vec[3]) +{ + VecSubf(vec, t->tsnap.snapPoint, t->tsnap.snapTarget); +} + +void ApplySnapRotation(TransInfo *t, float *vec) +{ + if (t->tsnap.modeTarget == SNAP_CLOSEST) { + *vec = t->tsnap.dist; + } + else { + *vec = RotationBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint); + } +} + +void ApplySnapResize(TransInfo *t, float vec[3]) +{ + if (t->tsnap.modeTarget == SNAP_CLOSEST) { + vec[0] = vec[1] = vec[2] = t->tsnap.dist; + } + else { + vec[0] = vec[1] = vec[2] = ResizeBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint); + } +} + +/********************** DISTANCE **************************/ + +float TranslationBetween(TransInfo *t, float p1[3], float p2[3]) +{ + return VecLenf(p1, p2); +} + +float RotationBetween(TransInfo *t, float p1[3], float p2[3]) +{ + float angle, start[3], end[3], center[3]; + + VECCOPY(center, t->center); + if(t->flag & (T_EDIT|T_POSE)) { + // TRANSFORM_FIX_ME +// Object *ob= G.obedit?G.obedit:t->poseobj; +// Mat4MulVecfl(ob->obmat, center); + } + + VecSubf(start, p1, center); + VecSubf(end, p2, center); + + // Angle around a constraint axis (error prone, will need debug) + if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { + float axis[3], tmp[3]; + + t->con.applyRot(t, NULL, axis, NULL); + + Projf(tmp, end, axis); + VecSubf(end, end, tmp); + + Projf(tmp, start, axis); + VecSubf(start, start, tmp); + + Normalize(end); + Normalize(start); + + Crossf(tmp, start, end); + + if (Inpf(tmp, axis) < 0.0) + angle = -acos(Inpf(start, end)); + else + angle = acos(Inpf(start, end)); + } + else { + float mtx[3][3]; + + Mat3CpyMat4(mtx, t->viewmat); + + Mat3MulVecfl(mtx, end); + Mat3MulVecfl(mtx, start); + + angle = atan2(start[1],start[0]) - atan2(end[1],end[0]); + } + + if (angle > M_PI) { + angle = angle - 2 * M_PI; + } + else if (angle < -(M_PI)) { + angle = 2 * M_PI + angle; + } + + return angle; +} + +float ResizeBetween(TransInfo *t, float p1[3], float p2[3]) +{ + float d1[3], d2[3], center[3]; + + VECCOPY(center, t->center); + if(t->flag & (T_EDIT|T_POSE)) { + // TRANSFORM_FIX_ME +// Object *ob= G.obedit?G.obedit:t->poseobj; +// Mat4MulVecfl(ob->obmat, center); + } + + VecSubf(d1, p1, center); + VecSubf(d2, p2, center); + + if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { + Mat3MulVecfl(t->con.pmtx, d1); + Mat3MulVecfl(t->con.pmtx, d2); + } + + return VecLength(d2) / VecLength(d1); +} + +/********************** CALC **************************/ + +void CalcSnapGrid(TransInfo *t, float *vec) +{ + snapGridAction(t, t->tsnap.snapPoint, BIG_GEARS); +} + +void CalcSnapGeometry(TransInfo *t, float *vec) +{ + Object *obedit = NULL; + + /* Object mode */ + if (obedit == NULL) + { + if (t->spacetype == SPACE_VIEW3D) + { + float vec[3]; + float no[3]; + int found = 0; + int dist = 40; // Use a user defined value here + + found = snapObjects(t, &dist, vec, no, NOT_SELECTED); + if (found == 1) + { + float tangent[3]; + + VecSubf(tangent, vec, t->tsnap.snapPoint); + tangent[2] = 0; + + if (Inpf(tangent, tangent) > 0) + { + VECCOPY(t->tsnap.snapTangent, tangent); + } + + VECCOPY(t->tsnap.snapPoint, vec); + VECCOPY(t->tsnap.snapNormal, no); + + t->tsnap.status |= POINT_INIT; + } + else + { + t->tsnap.status &= ~POINT_INIT; + } + } + } + /* Mesh edit mode */ + else if (obedit != NULL && obedit->type==OB_MESH) + { + if (t->spacetype == SPACE_VIEW3D) + { + float vec[3]; + float no[3]; + int found = 0; + int dist = 40; // Use a user defined value here + + found = snapObjects(t, &dist, vec, no, NOT_ACTIVE); + if (found == 1) + { + VECCOPY(t->tsnap.snapPoint, vec); + VECCOPY(t->tsnap.snapNormal, no); + + t->tsnap.status |= POINT_INIT; + } + else + { + t->tsnap.status &= ~POINT_INIT; + } + } + else if (t->spacetype == SPACE_IMAGE) + { /* same as above but for UV's */ + MTFace *nearesttf=NULL; + float aspx, aspy; + int face_corner; + + // TRANSFORM_FIX_ME + //find_nearest_uv(&nearesttf, NULL, NULL, &face_corner); + + if (nearesttf != NULL) + { + VECCOPY2D(t->tsnap.snapPoint, nearesttf->uv[face_corner]); + + // TRANSFORM_FIX_ME + //transform_aspect_ratio_tface_uv(&aspx, &aspy); + t->tsnap.snapPoint[0] *= aspx; + t->tsnap.snapPoint[1] *= aspy; + + //Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint); + + t->tsnap.status |= POINT_INIT; + } + else + { + t->tsnap.status &= ~POINT_INIT; + } + } + } +} + +/********************** TARGET **************************/ + +void TargetSnapCenter(TransInfo *t) +{ + // Only need to calculate once + if ((t->tsnap.status & TARGET_INIT) == 0) + { + VECCOPY(t->tsnap.snapTarget, t->center); + if(t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget); + } + + t->tsnap.status |= TARGET_INIT; + } +} + +void TargetSnapActive(TransInfo *t) +{ + // Only need to calculate once + if ((t->tsnap.status & TARGET_INIT) == 0) + { + TransData *td = NULL; + TransData *active_td = NULL; + int i; + + for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++) + { + if (td->flag & TD_ACTIVE) + { + active_td = td; + break; + } + } + + if (active_td) + { + VECCOPY(t->tsnap.snapTarget, active_td->center); + + if(t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget); + } + + t->tsnap.status |= TARGET_INIT; + } + /* No active, default to median */ + else + { + t->tsnap.modeTarget = SNAP_MEDIAN; + t->tsnap.targetSnap = TargetSnapMedian; + TargetSnapMedian(t); + } + } +} + +void TargetSnapMedian(TransInfo *t) +{ + // Only need to calculate once + if ((t->tsnap.status & TARGET_INIT) == 0) + { + TransData *td = NULL; + int i; + + t->tsnap.snapTarget[0] = 0; + t->tsnap.snapTarget[1] = 0; + t->tsnap.snapTarget[2] = 0; + + for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++) + { + VecAddf(t->tsnap.snapTarget, t->tsnap.snapTarget, td->center); + } + + VecMulf(t->tsnap.snapTarget, 1.0 / t->total); + + if(t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget); + } + + t->tsnap.status |= TARGET_INIT; + } +} + +void TargetSnapClosest(TransInfo *t) +{ + // Only valid if a snap point has been selected + if (t->tsnap.status & POINT_INIT) + { + TransData *closest = NULL, *td = NULL; + + /* Object mode */ + if (t->flag & T_OBJECT) + { + int i; + for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++) + { + struct BoundBox *bb = object_get_boundbox(td->ob); + + /* use boundbox if possible */ + if (bb) + { + int j; + + for (j = 0; j < 8; j++) { + float loc[3]; + float dist; + + VECCOPY(loc, bb->vec[j]); + Mat4MulVecfl(td->ext->obmat, loc); + + dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); + + if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist)) + { + VECCOPY(t->tsnap.snapTarget, loc); + closest = td; + t->tsnap.dist = dist; + } + } + } + /* use element center otherwise */ + else + { + float loc[3]; + float dist; + + VECCOPY(loc, td->center); + + dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); + + if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist)) + { + VECCOPY(t->tsnap.snapTarget, loc); + closest = td; + t->tsnap.dist = dist; + } + } + } + } + else + { + int i; + for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++) + { + float loc[3]; + float dist; + + VECCOPY(loc, td->center); + + if(t->flag & (T_EDIT|T_POSE)) { + Object *ob= G.obedit?G.obedit:t->poseobj; + Mat4MulVecfl(ob->obmat, loc); + } + + dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); + + if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist)) + { + VECCOPY(t->tsnap.snapTarget, loc); + closest = td; + t->tsnap.dist = dist; + } + } + } + + t->tsnap.status |= TARGET_INIT; + } +} +/*================================================================*/ + +int snapDerivedMesh(TransInfo *t, Object *ob, DerivedMesh *dm, float obmat[][4], float ray_start[3], float ray_normal[3], short mval[2], float *loc, float *no, int *dist, float *depth, short EditMesh) +{ + int retval = 0; + int totvert = dm->getNumVerts(dm); + int totface = dm->getNumFaces(dm); + + if (totvert > 0) { + float imat[4][4]; + float timat[3][3]; /* transpose inverse matrix for normals */ + float ray_start_local[3], ray_normal_local[3]; + int test = 1; + + Mat4Invert(imat, obmat); + + Mat3CpyMat4(timat, imat); + Mat3Transp(timat); + + VECCOPY(ray_start_local, ray_start); + VECCOPY(ray_normal_local, ray_normal); + + Mat4MulVecfl(imat, ray_start_local); + Mat4Mul3Vecfl(imat, ray_normal_local); + + + /* If number of vert is more than an arbitrary limit, + * test against boundbox first + * */ + if (totface > 16) { + struct BoundBox *bb = object_get_boundbox(ob); + test = ray_hit_boundbox(bb, ray_start_local, ray_normal_local); + } + + if (test == 1) { + + switch (G.scene->snap_mode) + { + case SCE_SNAP_MODE_FACE: + { + MVert *verts = dm->getVertArray(dm); + MFace *faces = dm->getFaceArray(dm); + int *index_array = NULL; + int index = 0; + int i; + + if (EditMesh) + { + index_array = dm->getFaceDataArray(dm, CD_ORIGINDEX); + EM_init_index_arrays(0, 0, 1); + } + + for( i = 0; i < totface; i++) { + EditFace *efa = NULL; + MFace *f = faces + i; + float lambda; + int result; + + test = 1; /* reset for every face */ + + if (EditMesh) + { + if (index_array) + { + index = index_array[i]; + } + else + { + index = i; + } + + if (index == ORIGINDEX_NONE) + { + test = 0; + } + else + { + efa = EM_get_face_for_index(index); + + if (efa && (efa->h || (efa->v1->f & SELECT) || (efa->v2->f & SELECT) || (efa->v3->f & SELECT) || (efa->v4 && efa->v4->f & SELECT))) + { + test = 0; + } + } + } + + + if (test) + { + result = RayIntersectsTriangle(ray_start_local, ray_normal_local, verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, &lambda, NULL); + + if (result) { + float location[3], normal[3]; + float intersect[3]; + float new_depth; + int screen_loc[2]; + int new_dist; + + VECCOPY(intersect, ray_normal_local); + VecMulf(intersect, lambda); + VecAddf(intersect, intersect, ray_start_local); + + VECCOPY(location, intersect); + + if (f->v4) + CalcNormFloat4(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, verts[f->v4].co, normal); + else + CalcNormFloat(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, normal); + + Mat4MulVecfl(obmat, location); + + new_depth = VecLenf(location, ray_start); + + project_int(t->ar, t->view, location, screen_loc); + new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]); + + if (new_dist <= *dist && new_depth < *depth) + { + *depth = new_depth; + retval = 1; + + VECCOPY(loc, location); + VECCOPY(no, normal); + + Mat3MulVecfl(timat, no); + Normalize(no); + + *dist = new_dist; + } + } + + if (f->v4 && result == 0) + { + result = RayIntersectsTriangle(ray_start_local, ray_normal_local, verts[f->v3].co, verts[f->v4].co, verts[f->v1].co, &lambda, NULL); + + if (result) { + float location[3], normal[3]; + float intersect[3]; + float new_depth; + int screen_loc[2]; + int new_dist; + + VECCOPY(intersect, ray_normal_local); + VecMulf(intersect, lambda); + VecAddf(intersect, intersect, ray_start_local); + + VECCOPY(location, intersect); + + if (f->v4) + CalcNormFloat4(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, verts[f->v4].co, normal); + else + CalcNormFloat(verts[f->v1].co, verts[f->v2].co, verts[f->v3].co, normal); + + Mat4MulVecfl(obmat, location); + + new_depth = VecLenf(location, ray_start); + + project_int(t->ar, t->view, location, screen_loc); + new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]); + + if (new_dist <= *dist && new_depth < *depth) + { + *depth = new_depth; + retval = 1; + + VECCOPY(loc, location); + VECCOPY(no, normal); + + Mat3MulVecfl(timat, no); + Normalize(no); + + *dist = new_dist; + } + } + } + } + } + + if (EditMesh) + { + EM_free_index_arrays(); + } + break; + } + case SCE_SNAP_MODE_VERTEX: + { + MVert *verts = dm->getVertArray(dm); + int *index_array = NULL; + int index = 0; + int i; + + if (EditMesh) + { + index_array = dm->getVertDataArray(dm, CD_ORIGINDEX); + EM_init_index_arrays(1, 0, 0); + } + + for( i = 0; i < totvert; i++) { + EditVert *eve = NULL; + MVert *v = verts + i; + + test = 1; /* reset for every vert */ + + if (EditMesh) + { + if (index_array) + { + index = index_array[i]; + } + else + { + index = i; + } + + if (index == ORIGINDEX_NONE) + { + test = 0; + } + else + { + eve = EM_get_vert_for_index(index); + + if (eve && (eve->h || (eve->f & SELECT))) + { + test = 0; + } + } + } + + + if (test) + { + float dvec[3]; + + VecSubf(dvec, v->co, ray_start_local); + + if (Inpf(ray_normal_local, dvec) > 0) + { + float location[3]; + float new_depth; + int screen_loc[2]; + int new_dist; + + VECCOPY(location, v->co); + + Mat4MulVecfl(obmat, location); + + new_depth = VecLenf(location, ray_start); + + project_int(t->ar, t->view, location, screen_loc); + new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]); + + if (new_dist <= *dist && new_depth < *depth) + { + *depth = new_depth; + retval = 1; + + VECCOPY(loc, location); + + NormalShortToFloat(no, v->no); + Mat3MulVecfl(timat, no); + Normalize(no); + + *dist = new_dist; + } + } + } + } + + if (EditMesh) + { + EM_free_index_arrays(); + } + break; + } + case SCE_SNAP_MODE_EDGE: + { + MVert *verts = dm->getVertArray(dm); + MEdge *edges = dm->getEdgeArray(dm); + int totedge = dm->getNumEdges(dm); + int *index_array = NULL; + int index = 0; + int i; + + if (EditMesh) + { + index_array = dm->getEdgeDataArray(dm, CD_ORIGINDEX); + EM_init_index_arrays(0, 1, 0); + } + + for( i = 0; i < totedge; i++) { + EditEdge *eed = NULL; + MEdge *e = edges + i; + + test = 1; /* reset for every vert */ + + if (EditMesh) + { + if (index_array) + { + index = index_array[i]; + } + else + { + index = i; + } + + if (index == ORIGINDEX_NONE) + { + test = 0; + } + else + { + eed = EM_get_edge_for_index(index); + + if (eed && (eed->h || (eed->v1->f & SELECT) || (eed->v2->f & SELECT))) + { + test = 0; + } + } + } + + + if (test) + { + float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; + int result; + + VECCOPY(ray_end, ray_normal_local); + VecMulf(ray_end, 2000); + VecAddf(ray_end, ray_start_local, ray_end); + + result = LineIntersectLine(verts[e->v1].co, verts[e->v2].co, ray_start_local, ray_end, intersect, dvec); /* dvec used but we don't care about result */ + + if (result) + { + float edge_loc[3], vec[3]; + float mul; + + /* check for behind ray_start */ + VecSubf(dvec, intersect, ray_start_local); + + VecSubf(edge_loc, verts[e->v1].co, verts[e->v2].co); + VecSubf(vec, intersect, verts[e->v2].co); + + mul = Inpf(vec, edge_loc) / Inpf(edge_loc, edge_loc); + + if (mul > 1) { + mul = 1; + VECCOPY(intersect, verts[e->v1].co); + } + else if (mul < 0) { + mul = 0; + VECCOPY(intersect, verts[e->v2].co); + } + + if (Inpf(ray_normal_local, dvec) > 0) + { + float location[3]; + float new_depth; + int screen_loc[2]; + int new_dist; + + VECCOPY(location, intersect); + + Mat4MulVecfl(obmat, location); + + new_depth = VecLenf(location, ray_start); + + project_int(t->ar, t->view, location, screen_loc); + new_dist = abs(screen_loc[0] - mval[0]) + abs(screen_loc[1] - mval[1]); + + if (new_dist <= *dist && new_depth < *depth) + { + float n1[3], n2[3]; + + *depth = new_depth; + retval = 1; + + VecSubf(edge_loc, verts[e->v1].co, verts[e->v2].co); + VecSubf(vec, intersect, verts[e->v2].co); + + mul = Inpf(vec, edge_loc) / Inpf(edge_loc, edge_loc); + + NormalShortToFloat(n1, verts[e->v1].no); + NormalShortToFloat(n2, verts[e->v2].no); + VecLerpf(no, n2, n1, mul); + Normalize(no); + + VECCOPY(loc, location); + + Mat3MulVecfl(timat, no); + Normalize(no); + + *dist = new_dist; + } + } + } + } + } + + if (EditMesh) + { + EM_free_index_arrays(); + } + break; + } + } + } + } + + return retval; +} + +int snapObjects(TransInfo *t, int *dist, float *loc, float *no, int mode) { + Scene *scene = t->scene; + View3D *v3d = t->view; + Base *base; + float depth = FLT_MAX; + int retval = 0; + float ray_start[3], ray_normal[3]; + + viewray(t->ar, v3d, t->mval, ray_start, ray_normal); + + if (mode == NOT_ACTIVE) + { + // TRANSFORM_FIX_ME +// DerivedMesh *dm; +// Object *ob = G.obedit; +// +// dm = editmesh_get_derived_cage(CD_MASK_BAREMESH); +// +// retval = snapDerivedMesh(t, ob, dm, ob->obmat, ray_start, ray_normal, mval, loc, no, dist, &depth, 1); +// +// dm->release(dm); + } + + for ( base = scene->base.first; base != NULL; base = base->next ) { + if ( BASE_SELECTABLE(v3d, base) && (base->flag & (BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA)) == 0 && ((mode == NOT_SELECTED && (base->flag & (SELECT|BA_WAS_SEL)) == 0) || (mode == NOT_ACTIVE && base != BASACT)) ) { + Object *ob = base->object; + + if (ob->transflag & OB_DUPLI) + { + DupliObject *dupli_ob; + ListBase *lb = object_duplilist(G.scene, ob); + + for(dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) + { + Object *ob = dupli_ob->ob; + + if (ob->type == OB_MESH) { + DerivedMesh *dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH); + int val; + + val = snapDerivedMesh(t, ob, dm, dupli_ob->mat, ray_start, ray_normal, t->mval, loc, no, dist, &depth, 0); + + retval = retval || val; + + dm->release(dm); + } + } + + free_object_duplilist(lb); + } + + if (ob->type == OB_MESH) { + DerivedMesh *dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH); + int val; + + val = snapDerivedMesh(t, ob, dm, ob->obmat, ray_start, ray_normal, t->mval, loc, no, dist, &depth, 0); + + retval = retval || val; + + dm->release(dm); + } + } + } + + return retval; +} + +/*================================================================*/ + +static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action); + + +void snapGridAction(TransInfo *t, float *val, GearsType action) { + float fac[3]; + + fac[NO_GEARS] = t->snap[0]; + fac[BIG_GEARS] = t->snap[1]; + fac[SMALL_GEARS] = t->snap[2]; + + applyGrid(t, val, t->idx_max, fac, action); +} + + +void snapGrid(TransInfo *t, float *val) { + int invert; + GearsType action; + + // Only do something if using Snap to Grid + if (t->tsnap.modePoint != SNAP_GRID) + return; + + if(t->mode==TFM_ROTATION || t->mode==TFM_WARP || t->mode==TFM_TILT || t->mode==TFM_TRACKBALL || t->mode==TFM_BONE_ROLL) + invert = U.flag & USER_AUTOROTGRID; + else if(t->mode==TFM_RESIZE || t->mode==TFM_SHEAR || t->mode==TFM_BONESIZE || t->mode==TFM_SHRINKFATTEN || t->mode==TFM_CURVE_SHRINKFATTEN) + invert = U.flag & USER_AUTOSIZEGRID; + else + invert = U.flag & USER_AUTOGRABGRID; + + if(invert) { + action = (t->event->ctrl) ? NO_GEARS: BIG_GEARS; + } + else { + action = (t->event->ctrl) ? BIG_GEARS : NO_GEARS; + } + + if (action == BIG_GEARS && (t->event->shift)) { + action = SMALL_GEARS; + } + + snapGridAction(t, val, action); +} + + +static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action) +{ + int i; + float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3) + + // Early bailing out if no need to snap + if (fac[action] == 0.0) + return; + + /* evil hack - snapping needs to be adapted for image aspect ratio */ + if((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) { + // TRANSFORM_FIX_ME + //transform_aspect_ratio_tface_uv(asp, asp+1); + } + + for (i=0; i<=max_index; i++) { + val[i]= fac[action]*asp[i]*(float)floor(val[i]/(fac[action]*asp[i]) +.5); + } +} |