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
This commit is contained in:
Martin Poirier
2008-12-29 01:41:28 +00:00
parent 97a82102d4
commit b6b61681ef
18 changed files with 16523 additions and 12 deletions

View File

@@ -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 *);

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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] )

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
*/
}

View File

@@ -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 */
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}