This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/src/transform_conversions.c
Ton Roosendaal 59672145a9 Cleanup & goodies for rigging geeks! :)
- PoseMode: Wkey menu, "Flip Left/Right Names". On selected bones, it flips
  the L/R tags in names, and calls the proper code to rename everything
  that's related (constraint targets, bone-childs, etc).
- PoseMode: Shift+S snapmenu: snap cursor to selected now works
- Outliner: select bones now correctly sets 'active' flag for bones,
  updating the UI as well.
  Also made sure you cannot select hidden bones in outliner.
- 3DWindow: in PoseMode/EditMode draws name of active Bone too (with user
  option "Draw active object name" set.
- Added the new Armature/PoseMode options in View3D pulldowns.

Cleanup:

- moved Pose code from editaction.c to poseobject.c
- removed BSE_editaction.h and BSE_editaction_types.h, moved contents of
  it to BIF_editaction.h. One include per C file should be fine. :)
  I know the src/ structure would require more elaborated includes, but
  we don't have that now...
2005-07-27 10:37:20 +00:00

1647 lines
38 KiB
C
Executable File

/**
* $Id$
*
* ***** BEGIN GPL/BL DUAL 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. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* 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/BL DUAL LICENSE BLOCK *****
*/
#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_object_force.h"
#include "DNA_scene_types.h"
#include "DNA_screen_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 "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_blender.h"
#include "BKE_curve.h"
#include "BKE_constraint.h"
#include "BKE_depsgraph.h"
#include "BKE_displist.h"
#include "BKE_effect.h"
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_ipo.h"
#include "BKE_lattice.h"
#include "BKE_mball.h"
#include "BKE_object.h"
#include "BKE_softbody.h"
#include "BKE_utildefines.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_gl.h"
#include "BIF_poseobject.h"
#include "BIF_mywindow.h"
#include "BIF_resources.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BSE_view.h"
#include "BSE_edit.h"
#include "BSE_editipo.h"
#include "BSE_editipo_types.h"
#include "BDR_editobject.h" // reset_slowparents()
#include "BLI_arithb.h"
#include "BLI_editVert.h"
#include "blendef.h"
#include "mydevice.h"
extern ListBase editNurb;
extern ListBase editelems;
#include "transform.h"
/* ************************** 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 = Normalise(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(TransInfo *t)
{
TransData *td;
Object *ob;
ID *id;
ob= OBACT;
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);
Mat3Inv(td->smtx, td->mtx);
if( GS(id->name)==ID_ME) {
Mesh *me= ob->data;
me->texflag &= ~AUTOSPACE;
td->loc= me->loc;
td->ext->rot= me->rot;
td->ext->size= me->size;
}
else if( GS(id->name)==ID_CU) {
Curve *cu= ob->data;
cu->texflag &= ~CU_AUTOSPACE;
td->loc= cu->loc;
td->ext->rot= cu->rot;
td->ext->size= cu->size;
}
else if( GS(id->name)==ID_MB) {
MetaBall *mb= ob->data;
mb->texflag &= ~MB_AUTOSPACE;
td->loc= mb->loc;
td->ext->rot= mb->rot;
td->ext->size= mb->size;
}
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(TransInfo *t) {
TransData *td = NULL;
EditMesh *em = G.editMesh;
EditEdge *eed;
Mesh *me = G.obedit->data;
float mtx[3][3], smtx[3][3];
int count=0, countsel=0;
int propmode = t->flag & T_PROP_EDIT;
/* THIS IS A REALLY STUPID HACK, MUST BE A BETTER WAY TO DO IT */
/* this is sufficient to invoke edges added in mesh, but only in editmode */
if(me->medge==NULL) {
me->medge= MEM_callocN(sizeof(MEdge), "fake medge");
me->totedge= 1;
allqueue(REDRAWBUTSEDIT, 0);
}
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;
td->val = &(eed->crease);
td->ival = eed->crease;
td++;
}
}
}
/* ********************* pose mode ************* */
/* recursive, make sure it's identical structured as next one */
/* only counts the parent selection, and tags transform flag */
/* exported for manipulator */
void count_bone_select(TransInfo *t, 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->flag & BONE_SELECTED) {
/* We don't let IK children get "grabbed" */
if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_IK_TOPARENT)==0 ) {
bone->flag |= BONE_TRANSFORM;
t->total++;
do_next= 0; // no transform on children if one parent bone is selected
}
}
}
count_bone_select(t, &bone->childbase, do_next);
}
}
static int add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td)
{
Bone *bone= pchan->bone;
float pmat[3][3], omat[3][3];
float vec[3];
if(bone) {
if (bone->flag & BONE_TRANSFORM) {
/* We don't let IK children get "grabbed" */
if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_IK_TOPARENT)==0 ) {
VECCOPY(vec, pchan->pose_mat[3]);
VECCOPY(td->center, vec);
td->ob = ob;
td->flag= TD_SELECTED|TD_USEQUAT;
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 the parent transform + own transform */
Mat3CpyMat4(omat, ob->obmat);
if(pchan->parent) {
Mat3CpyMat4(pmat, pchan->parent->pose_mat);
Mat3MulSerie(td->mtx, pchan->bone->bone_mat, pmat, omat, 0,0,0,0,0); // dang mulserie swaps args
}
else {
Mat3MulMat3(td->mtx, omat, pchan->bone->bone_mat); // huh, transposed?
}
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);
return 1;
}
}
}
return 0;
}
/* only called with pose mode active object now */
static void createTransPose(TransInfo *t)
{
Object *ob= OBACT;
bArmature *arm;
bPoseChannel *pchan;
TransData *td;
TransDataExtension *tdx;
int i;
/* check validity of state */
arm=get_armature (ob);
if (arm==NULL || ob->pose==NULL) return;
if (arm->flag & ARM_RESTPOS){
notice ("Pose edit not possible while Rest Position is enabled");
return;
}
if (!(ob->lay & G.vd->lay)) return;
/* count total */
count_bone_select(t, &arm->bonebase, 1);
if(t->total==0 && t->mode==TFM_TRANSLATION) {
t->mode= TFM_ROTATION;
count_bone_select(t, &arm->bonebase, 1);
}
if(t->total==0) return;
/* 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( add_pose_transdata(t, pchan, ob, td) ) td++;
}
if(td != (t->data+t->total)) printf("Bone selection count error\n");
}
/* ********************* armature ************** */
static void createTransArmatureVerts(TransInfo *t)
{
EditBone *ebo;
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 (t->mode==TFM_BONESIZE) {
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_mallocN(t->total*sizeof(TransData), "TransEditBone");
for (ebo=G.edbo.first;ebo;ebo=ebo->next){
if (t->mode==TFM_BONESIZE) {
if (ebo->flag & BONE_SELECTED) {
// abusive storage of scale in the loc pointer :)
td->loc= &ebo->xwidth;
VECCOPY (td->iloc, td->loc);
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->val = 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;
Mat3CpyMat3(td->smtx, smtx);
Mat3CpyMat3(td->mtx, mtx);
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;
Mat3CpyMat3(td->smtx, smtx);
Mat3CpyMat3(td->mtx, mtx);
td->ext = NULL;
td->tdi = NULL;
td->val = NULL;
td++;
}
}
}
}
/* ********************* meta elements ********* */
static void createTransMBallVerts(TransInfo *t)
{
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_mallocN(t->total*sizeof(TransData), "TransObData(MBall EditMode)");
tx = t->ext = MEM_mallocN(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++;
}
}
}
/* ********************* 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;
}
}
}
}
}
static void createTransCurveVerts(TransInfo *t)
{
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(bezt->f1 & 1) countsel++;
if(bezt->f2 & 1) countsel++;
if(bezt->f3 & 1) 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 & 1) 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_mallocN(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) {
if(propmode || (bezt->f1 & 1)) {
VECCOPY(td->iloc, bezt->vec[0]);
td->loc= bezt->vec[0];
VECCOPY(td->center, bezt->vec[1]);
if(bezt->f1 & 1) td->flag= TD_SELECTED;
else td->flag= 0;
td->ext = NULL;
td->tdi = NULL;
td->val = NULL;
Mat3CpyMat3(td->smtx, smtx);
Mat3CpyMat3(td->mtx, mtx);
td++;
count++;
tail++;
}
/* THIS IS THE CV, the other two are handles */
if(propmode || (bezt->f2 & 1)) {
VECCOPY(td->iloc, bezt->vec[1]);
td->loc= bezt->vec[1];
VECCOPY(td->center, td->loc);
if(bezt->f2 & 1) td->flag= TD_SELECTED;
else td->flag= 0;
td->ext = NULL;
td->tdi = NULL;
td->val = &(bezt->alfa);
td->ival = bezt->alfa;
Mat3CpyMat3(td->smtx, smtx);
Mat3CpyMat3(td->mtx, mtx);
td++;
count++;
tail++;
}
if(propmode || (bezt->f3 & 1)) {
VECCOPY(td->iloc, bezt->vec[2]);
td->loc= bezt->vec[2];
VECCOPY(td->center, bezt->vec[1]);
if(bezt->f3 & 1) td->flag= TD_SELECTED;
else td->flag= 0;
td->ext = NULL;
td->tdi = NULL;
td->val = NULL;
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);
}
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 & 1)) {
VECCOPY(td->iloc, bp->vec);
td->loc= bp->vec;
VECCOPY(td->center, td->loc);
if(bp->f1 & 1) td->flag= TD_SELECTED;
else td->flag= 0;
td->ext = NULL;
td->tdi = NULL;
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);
}
}
}
/* ********************* lattice *************** */
static void createTransLatticeVerts(TransInfo *t)
{
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->f1 & 1) 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_mallocN(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 & 1)) {
if(bp->hide==0) {
VECCOPY(td->iloc, bp->vec);
td->loc= bp->vec;
VECCOPY(td->center, td->loc);
if(bp->f1 & 1) 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++;
}
}
/* ********************* mesh ****************** */
/* proportional distance based on connectivity */
#define E_VEC(a) (vectors + (3 * (int)(a)->vn))
#define E_NEAR(a) (nears[((int)(a)->vn)])
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' */
/* vn is offset on scratch array */
for(eve= em->verts.first; eve; eve= eve->next) {
if(eve->h==0) {
eve->vn = (EditVert *)(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);
if (lenn - len1 > 0.00001f && len2 - lenn > 0.00001f) {
VECCOPY(vec2, nvec);
E_NEAR(v2) = E_NEAR(v1);
done = 1;
}
else if (len2 - len1 > 0.00001f && len1 - lenn > 0.00001f) {
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);
if (lenn - len2 > 0.00001f && len1 - lenn > 0.00001f) {
VECCOPY(vec1, nvec);
E_NEAR(v1) = E_NEAR(v2);
done = 1;
}
else if (len1 - len2 > 0.00001f && len2 - lenn > 0.00001f) {
VECCOPY(vec1, vec2);
E_NEAR(v1) = E_NEAR(v2);
done = 1;
}
}
}
else {
v2->f2 = 1;
VecSubf(vec2, v2->co, E_NEAR(v1)->co);
if (VecLength(vec1) - VecLength(vec2) > 0.00001f) {
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);
if (VecLength(vec2) - VecLength(vec1) > 0.00001f) {
VECCOPY(vec1, vec2);
}
E_NEAR(v1) = E_NEAR(v2);
done = 1;
}
}
}
}
}
static void VertsToTransData(TransData *td, EditVert *eve)
{
td->flag = 0;
td->loc = eve->co;
VECCOPY(td->center, td->loc);
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;
}
static void createTransEditVerts(TransInfo *t)
{
TransData *tob = NULL;
EditMesh *em = G.editMesh;
EditVert *eve;
EditVert **nears = NULL;
float *vectors = NULL;
float mtx[3][3], smtx[3][3];
int count=0, countsel=0;
int propmode = t->flag & T_PROP_EDIT;
// 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;
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_mallocN(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);
for (eve=em->verts.first; eve; eve=eve->next) {
if(eve->h==0) {
if(propmode || eve->f1) {
VertsToTransData(tob, eve);
if(eve->f1) tob->flag |= TD_SELECTED;
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;
}
}
Mat3CpyMat3(tob->smtx, smtx);
Mat3CpyMat3(tob->mtx, mtx);
tob++;
}
}
}
if (propmode) {
MEM_freeN(vectors);
MEM_freeN(nears);
}
}
/* **************** 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 */
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;
}
/* *************************** Object Transform data ******************* */
static void ObjectToTransData(TransData *td, Object *ob)
{
float obmtx[3][3];
Object *tr;
void *cfirst, *clast;
/* set axismtx BEFORE clearing constraints to have the real orientation */
Mat3CpyMat4(td->axismtx, ob->obmat);
Mat3Ortho(td->axismtx);
/* then why are constraints and track disabled here?
they dont alter loc/rot/size itself (ton) */
cfirst = ob->constraints.first;
clast = ob->constraints.last;
ob->constraints.first=ob->constraints.last=NULL;
tr= ob->track;
ob->track= NULL;
where_is_object(ob);
ob->track= tr;
ob->constraints.first = cfirst;
ob->constraints.last = clast;
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]);
if (ob->parent)
{
float totmat[3][3], obinv[3][3];
/* we calculate smtx without obmat: so a parmat */
object_to_mat3(ob, obmtx);
Mat3CpyMat4(totmat, ob->obmat);
Mat3Inv(obinv, totmat);
Mat3MulMat3(td->smtx, obmtx, obinv);
Mat3Inv(td->mtx, td->smtx);
}
else
{
Mat3One(td->smtx);
Mat3One(td->mtx);
}
}
/* 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(TransInfo *t)
{
/*
if Base selected and has parent selected:
base->flag= BA_WAS_SEL
*/
Base *base;
/* makes sure base flags and object flags are identical */
copy_baseflags();
for (base= FIRSTBASE; base; base= base->next) {
base->flag &= ~BA_WAS_SEL;
if(TESTBASELIB(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) {
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 */
DAG_scene_flush_update(G.scene);
/* and we store them temporal in base (only used for transform code) */
/* this because after doing updates, the object->recalc is cleared */
for (base= FIRSTBASE; 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(void)
{
Base *base;
base= FIRSTBASE;
while(base) {
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);
base = base->next;
}
}
/* inserting keys, refresh ipo-keys, softbody, redraw events... (ton) */
void special_aftertrans_update(short cancelled)
{
Object *ob= OBACT;
Base *base;
int redrawipo=0;
if(G.obedit); // nothing
else if (ob && (ob->flag & OB_POSEMODE)) {
bArmature *arm= ob->data;
bAction *act;
bPose *pose;
bPoseChannel *pchan;
if(cancelled) /* if cancelled we do the update always */
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
else if(G.flags & G_RECORDKEYS) {
act= ob->action;
pose= ob->pose;
if (!act)
act= ob->action= add_empty_action();
set_pose_keys(ob); // sets chan->flag to POSE_KEY if bone selected
for (pchan=pose->chanbase.first; pchan; pchan=pchan->next){
if (pchan->flag & POSE_KEY){
set_action_key(act, pchan, AC_QUAT_X, 1);
set_action_key(act, pchan, AC_QUAT_Y, 1);
set_action_key(act, pchan, AC_QUAT_Z, 1);
set_action_key(act, pchan, AC_QUAT_W, 1);
set_action_key(act, pchan, AC_SIZE_X, 1);
set_action_key(act, pchan, AC_SIZE_Y, 1);
set_action_key(act, pchan, AC_SIZE_Z, 1);
set_action_key(act, pchan, AC_LOC_X, 1);
set_action_key(act, pchan, AC_LOC_Y, 1);
set_action_key(act, pchan, AC_LOC_Z, 1);
}
}
remake_action_ipos (act);
allspace(REMAKEIPO, 0);
allqueue(REDRAWACTION, 0);
allqueue(REDRAWIPO, 0);
allqueue(REDRAWNLA, 0);
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()
}
/* do not call DAG_object_flush_update always, we dont want actions to update, for inserting keys */
}
else {
base= FIRSTBASE;
while(base) {
if(base->flag & BA_DO_IPO) redrawipo= 1;
ob= base->object;
/* reset soft body object */
if(ob->softflag & OB_SB_ENABLE) sbObjectReset(ob, NULL);
/* Set autokey if necessary */
if ((G.flags & G_RECORDKEYS) && (!cancelled) && (base->flag & SELECT)){
insertkey(&base->object->id, OB_ROT_X);
insertkey(&base->object->id, OB_ROT_Y);
insertkey(&base->object->id, OB_ROT_Z);
insertkey(&base->object->id, OB_LOC_X);
insertkey(&base->object->id, OB_LOC_Y);
insertkey(&base->object->id, OB_LOC_Z);
insertkey(&base->object->id, OB_SIZE_X);
insertkey(&base->object->id, OB_SIZE_Y);
insertkey(&base->object->id, OB_SIZE_Z);
remake_object_ipos (ob);
allqueue(REDRAWIPO, 0);
allspace(REMAKEIPO, 0);
allqueue(REDRAWVIEW3D, 0);
allqueue(REDRAWNLA, 0);
}
base= base->next;
}
}
clear_trans_object_base_flags();
if(redrawipo) {
allqueue(REDRAWNLA, 0);
allqueue(REDRAWACTION, 0);
allqueue(REDRAWIPO, 0);
}
reset_slowparents();
/* note; should actually only be done for all objects when a lamp is moved... (ton) */
if(G.vd->drawtype == OB_SHADED) reshadeall_displist();
}
static void createTransObject(TransInfo *t)
{
TransData *td = NULL;
TransDataExtension *tx;
Object *ob;
Base *base;
IpoKey *ik;
ListBase elems;
set_trans_object_base_flags(t);
/* count */
for(base= FIRSTBASE; base; base= base->next) {
if TESTBASELIB(base) {
ob= base->object;
/* store ipo keys? */
if(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++;
}
else {
t->total++;
}
}
}
if(!t->total) {
/* clear here, main transform function escapes too */
clear_trans_object_base_flags();
return;
}
td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransOb");
tx = t->ext = MEM_callocN(t->total*sizeof(TransDataExtension), "TransObExtension");
for(base= FIRSTBASE; base; base= base->next) {
if TESTBASELIB(base) {
ob= base->object;
td->flag= TD_SELECTED;
td->ext = tx;
/* store ipo keys? */
if(ob->ipo && ob->ipo->showkey && (ob->ipoflag & OB_DRAWKEY)) {
popfirst(&elems); // bring back pushed listbase
if(elems.first) {
float 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;
pushdata(ob->loc, 7*3*4); // tsk! tsk!
for(ik= elems.first; ik; ik= ik->next) {
/* weak... this doesn't correct for floating values, giving small errors */
CFRA= (short)(ik->val/G.scene->r.framelen);
do_ob_ipo(ob);
ObjectToTransData(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= (short)cfraont;
ob->ipoflag= ipoflag;
where_is_object(ob); // restore
}
else {
ObjectToTransData(td, ob);
td->tdi = NULL;
td->val = NULL;
td++;
tx++;
}
}
else {
ObjectToTransData(td, ob);
td->tdi = NULL;
td->val = NULL;
td++;
tx++;
}
}
}
}
void createTransData(TransInfo *t)
{
Object *ob= OBACT;
if (t->context == CTX_TEXTURE) {
t->flag |= T_TEXTURE;
createTransTexspace(t);
}
else if (t->context == CTX_EDGE) {
t->ext = NULL;
t->flag |= T_EDIT;
createTransEdge(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 (G.obedit) {
t->ext = NULL;
if (G.obedit->type == OB_MESH) {
createTransEditVerts(t);
}
else if ELEM(G.obedit->type, OB_CURVE, OB_SURF) {
createTransCurveVerts(t);
}
else if (G.obedit->type==OB_LATTICE) {
createTransLatticeVerts(t);
}
else if (G.obedit->type==OB_MBALL) {
createTransMBallVerts(t);
}
else if (G.obedit->type==OB_ARMATURE) {
createTransArmatureVerts(t);
}
else {
printf("not done yet! only have mesh surface curve\n");
}
if(t->data && t->flag & T_PROP_EDIT) {
if (ELEM(G.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;
/* exception... hackish, we want bonescale to use bone orientation matrix (ton) */
if(t->mode==TFM_BONESIZE) {
t->flag &= ~T_EDIT;
t->flag |= T_POSE;
}
}
else if (ob && (ob->flag & OB_POSEMODE)) {
t->flag |= T_POSE;
createTransPose(t);
}
else {
createTransObject(t);
t->flag |= T_OBJECT;
}
if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp>1) {
t->flag |= T_CAMERA;
}
}