Merged 'backend' changes from AnimSys2. Many of these changes are necessary for the Dopesheet and other changes I'm currently still stabilising. Those will come in due course.
2931 lines
74 KiB
C
2931 lines
74 KiB
C
/* ipo.c
|
|
*
|
|
* $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): 2008, Joshua Leung (IPO System cleanup)
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_curve_types.h"
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_ipo_types.h"
|
|
#include "DNA_key_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_object_force.h"
|
|
#include "DNA_particle_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_sound_types.h"
|
|
#include "DNA_texture_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_world_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "BKE_action.h"
|
|
#include "BKE_blender.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_constraint.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_object.h"
|
|
|
|
#ifndef DISABLE_PYTHON
|
|
#include "BPY_extern.h" /* for BPY_pydriver_eval() */
|
|
#endif
|
|
|
|
#define SMALL -1.0e-10
|
|
|
|
/* ***************************** Adrcode Blocktype Defines ********************************* */
|
|
|
|
/* This array concept was meant to make sure that defines such as OB_LOC_X
|
|
don't have to be enumerated, also for backward compatibility, future changes,
|
|
and to enable it all can be accessed with a for-next loop.
|
|
|
|
This should whole adrcode system should eventually be replaced by a proper Data API
|
|
*/
|
|
|
|
|
|
int co_ar[CO_TOTIPO]= {
|
|
CO_ENFORCE, CO_HEADTAIL
|
|
};
|
|
|
|
int ob_ar[OB_TOTIPO]= {
|
|
OB_LOC_X, OB_LOC_Y, OB_LOC_Z, OB_DLOC_X, OB_DLOC_Y, OB_DLOC_Z,
|
|
OB_ROT_X, OB_ROT_Y, OB_ROT_Z, OB_DROT_X, OB_DROT_Y, OB_DROT_Z,
|
|
OB_SIZE_X, OB_SIZE_Y, OB_SIZE_Z, OB_DSIZE_X, OB_DSIZE_Y, OB_DSIZE_Z,
|
|
OB_LAY, OB_TIME, OB_COL_R, OB_COL_G, OB_COL_B, OB_COL_A,
|
|
OB_PD_FSTR, OB_PD_FFALL, OB_PD_SDAMP, OB_PD_RDAMP, OB_PD_PERM, OB_PD_FMAXD
|
|
};
|
|
|
|
int ac_ar[AC_TOTIPO]= {
|
|
AC_LOC_X, AC_LOC_Y, AC_LOC_Z,
|
|
AC_EUL_X, AC_EUL_Y, AC_EUL_Z,
|
|
AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z,
|
|
AC_SIZE_X, AC_SIZE_Y, AC_SIZE_Z
|
|
};
|
|
|
|
int ma_ar[MA_TOTIPO]= {
|
|
MA_COL_R, MA_COL_G, MA_COL_B,
|
|
MA_SPEC_R, MA_SPEC_G, MA_SPEC_B,
|
|
MA_MIR_R, MA_MIR_G, MA_MIR_B,
|
|
MA_REF, MA_ALPHA, MA_EMIT, MA_AMB,
|
|
MA_SPEC, MA_HARD, MA_SPTR, MA_IOR,
|
|
MA_MODE, MA_HASIZE, MA_TRANSLU, MA_RAYM,
|
|
MA_FRESMIR, MA_FRESMIRI, MA_FRESTRA, MA_FRESTRAI, MA_ADD,
|
|
|
|
MA_MAP1+MAP_OFS_X, MA_MAP1+MAP_OFS_Y, MA_MAP1+MAP_OFS_Z,
|
|
MA_MAP1+MAP_SIZE_X, MA_MAP1+MAP_SIZE_Y, MA_MAP1+MAP_SIZE_Z,
|
|
MA_MAP1+MAP_R, MA_MAP1+MAP_G, MA_MAP1+MAP_B,
|
|
MA_MAP1+MAP_DVAR, MA_MAP1+MAP_COLF, MA_MAP1+MAP_NORF, MA_MAP1+MAP_VARF, MA_MAP1+MAP_DISP
|
|
};
|
|
|
|
int te_ar[TE_TOTIPO] ={
|
|
|
|
TE_NSIZE, TE_NDEPTH, TE_NTYPE, TE_TURB,
|
|
|
|
TE_VNW1, TE_VNW2, TE_VNW3, TE_VNW4,
|
|
TE_VNMEXP, TE_VN_COLT, TE_VN_DISTM,
|
|
|
|
TE_ISCA, TE_DISTA,
|
|
|
|
TE_MG_TYP, TE_MGH, TE_MG_LAC, TE_MG_OCT, TE_MG_OFF, TE_MG_GAIN,
|
|
|
|
TE_N_BAS1, TE_N_BAS2,
|
|
|
|
TE_COL_R, TE_COL_G, TE_COL_B, TE_BRIGHT, TE_CONTRA
|
|
};
|
|
|
|
int seq_ar[SEQ_TOTIPO]= {
|
|
SEQ_FAC1
|
|
};
|
|
|
|
int cu_ar[CU_TOTIPO]= {
|
|
CU_SPEED
|
|
};
|
|
|
|
int wo_ar[WO_TOTIPO]= {
|
|
WO_HOR_R, WO_HOR_G, WO_HOR_B, WO_ZEN_R, WO_ZEN_G, WO_ZEN_B,
|
|
WO_EXPOS, WO_MISI, WO_MISTDI, WO_MISTSTA, WO_MISTHI,
|
|
WO_STAR_R, WO_STAR_G, WO_STAR_B, WO_STARDIST, WO_STARSIZE,
|
|
|
|
MA_MAP1+MAP_OFS_X, MA_MAP1+MAP_OFS_Y, MA_MAP1+MAP_OFS_Z,
|
|
MA_MAP1+MAP_SIZE_X, MA_MAP1+MAP_SIZE_Y, MA_MAP1+MAP_SIZE_Z,
|
|
MA_MAP1+MAP_R, MA_MAP1+MAP_G, MA_MAP1+MAP_B,
|
|
MA_MAP1+MAP_DVAR, MA_MAP1+MAP_COLF, MA_MAP1+MAP_NORF, MA_MAP1+MAP_VARF
|
|
};
|
|
|
|
int la_ar[LA_TOTIPO]= {
|
|
LA_ENERGY, LA_COL_R, LA_COL_G, LA_COL_B,
|
|
LA_DIST, LA_SPOTSI, LA_SPOTBL,
|
|
LA_QUAD1, LA_QUAD2, LA_HALOINT,
|
|
|
|
MA_MAP1+MAP_OFS_X, MA_MAP1+MAP_OFS_Y, MA_MAP1+MAP_OFS_Z,
|
|
MA_MAP1+MAP_SIZE_X, MA_MAP1+MAP_SIZE_Y, MA_MAP1+MAP_SIZE_Z,
|
|
MA_MAP1+MAP_R, MA_MAP1+MAP_G, MA_MAP1+MAP_B,
|
|
MA_MAP1+MAP_DVAR, MA_MAP1+MAP_COLF
|
|
};
|
|
|
|
/* yafray: aperture & focal distance curves added */
|
|
/* qdn: FDIST now available to Blender as well for defocus node */
|
|
int cam_ar[CAM_TOTIPO]= {
|
|
CAM_LENS, CAM_STA, CAM_END, CAM_YF_APERT, CAM_YF_FDIST, CAM_SHIFT_X, CAM_SHIFT_Y
|
|
};
|
|
|
|
int snd_ar[SND_TOTIPO]= {
|
|
SND_VOLUME, SND_PITCH, SND_PANNING, SND_ATTEN
|
|
};
|
|
|
|
int fluidsim_ar[FLUIDSIM_TOTIPO]= {
|
|
FLUIDSIM_VISC, FLUIDSIM_TIME,
|
|
FLUIDSIM_GRAV_X , FLUIDSIM_GRAV_Y , FLUIDSIM_GRAV_Z ,
|
|
FLUIDSIM_VEL_X , FLUIDSIM_VEL_Y , FLUIDSIM_VEL_Z ,
|
|
FLUIDSIM_ACTIVE,
|
|
FLUIDSIM_ATTR_FORCE_STR, FLUIDSIM_ATTR_FORCE_RADIUS,
|
|
FLUIDSIM_VEL_FORCE_STR, FLUIDSIM_VEL_FORCE_RADIUS,
|
|
};
|
|
|
|
int part_ar[PART_TOTIPO]= {
|
|
PART_EMIT_FREQ, PART_EMIT_LIFE, PART_EMIT_VEL, PART_EMIT_AVE, PART_EMIT_SIZE,
|
|
PART_AVE, PART_SIZE, PART_DRAG, PART_BROWN, PART_DAMP, PART_LENGTH, PART_CLUMP,
|
|
PART_GRAV_X, PART_GRAV_Y, PART_GRAV_Z, PART_KINK_AMP, PART_KINK_FREQ, PART_KINK_SHAPE,
|
|
PART_BB_TILT, PART_PD_FSTR, PART_PD_FFALL, PART_PD_FMAXD, PART_PD2_FSTR, PART_PD2_FFALL, PART_PD2_FMAXD
|
|
};
|
|
|
|
/* ************************** Data-Level Functions ************************* */
|
|
|
|
/* ---------------------- Freeing --------------------------- */
|
|
|
|
/* frees the ipo curve itself too */
|
|
void free_ipo_curve (IpoCurve *icu)
|
|
{
|
|
if (icu == NULL)
|
|
return;
|
|
|
|
if (icu->bezt)
|
|
MEM_freeN(icu->bezt);
|
|
if (icu->driver)
|
|
MEM_freeN(icu->driver);
|
|
|
|
MEM_freeN(icu);
|
|
}
|
|
|
|
/* do not free ipo itself */
|
|
void free_ipo (Ipo *ipo)
|
|
{
|
|
IpoCurve *icu, *icn;
|
|
|
|
if (ipo == NULL)
|
|
return;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icn) {
|
|
icn= icu->next;
|
|
|
|
/* must remove the link before freeing, as the curve is freed too */
|
|
BLI_remlink(&ipo->curve, icu);
|
|
free_ipo_curve(icu);
|
|
}
|
|
}
|
|
|
|
/* ---------------------- Init --------------------------- */
|
|
|
|
/* on adding new ipos, or for empty views */
|
|
void ipo_default_v2d_cur (int blocktype, rctf *cur)
|
|
{
|
|
switch (blocktype) {
|
|
case ID_CA:
|
|
cur->xmin= (float)G.scene->r.sfra;
|
|
cur->xmax= (float)G.scene->r.efra;
|
|
cur->ymin= 0.0f;
|
|
cur->ymax= 100.0f;
|
|
break;
|
|
|
|
case ID_MA: case ID_WO: case ID_LA:
|
|
case ID_CU: case ID_CO:
|
|
cur->xmin= (float)(G.scene->r.sfra - 0.1f);
|
|
cur->xmax= (float)G.scene->r.efra;
|
|
cur->ymin= (float)-0.1f;
|
|
cur->ymax= (float)+1.1f;
|
|
break;
|
|
|
|
case ID_TE:
|
|
cur->xmin= (float)(G.scene->r.sfra - 0.1f);
|
|
cur->xmax= (float)G.scene->r.efra;
|
|
cur->ymin= (float)-0.1f;
|
|
cur->ymax= (float)+1.1f;
|
|
break;
|
|
|
|
case ID_SEQ:
|
|
cur->xmin= -5.0f;
|
|
cur->xmax= 105.0f;
|
|
cur->ymin= (float)-0.1f;
|
|
cur->ymax= (float)+1.1f;
|
|
break;
|
|
|
|
case ID_KE:
|
|
cur->xmin= (float)(G.scene->r.sfra - 0.1f);
|
|
cur->xmax= (float)G.scene->r.efra;
|
|
cur->ymin= (float)-0.1f;
|
|
cur->ymax= (float)+2.1f;
|
|
break;
|
|
|
|
default: /* ID_OB and everything else */
|
|
cur->xmin= (float)G.scene->r.sfra;
|
|
cur->xmax= (float)G.scene->r.efra;
|
|
cur->ymin= -5.0f;
|
|
cur->ymax= +5.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* create a new IPO block (allocates the block) */
|
|
Ipo *add_ipo (char name[], int blocktype)
|
|
{
|
|
Ipo *ipo;
|
|
|
|
ipo= alloc_libblock(&G.main->ipo, ID_IP, name);
|
|
ipo->blocktype= blocktype;
|
|
ipo_default_v2d_cur(blocktype, &ipo->cur);
|
|
|
|
return ipo;
|
|
}
|
|
|
|
/* ---------------------- Copy --------------------------- */
|
|
|
|
/* duplicate an IPO block and all its data */
|
|
Ipo *copy_ipo (Ipo *src)
|
|
{
|
|
Ipo *dst;
|
|
IpoCurve *icu;
|
|
|
|
if (src == NULL)
|
|
return NULL;
|
|
|
|
dst= copy_libblock(src);
|
|
BLI_duplicatelist(&dst->curve, &src->curve);
|
|
|
|
for (icu= src->curve.first; icu; icu= icu->next) {
|
|
icu->bezt= MEM_dupallocN(icu->bezt);
|
|
|
|
if (icu->driver)
|
|
icu->driver= MEM_dupallocN(icu->driver);
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* ---------------------- Relink --------------------------- */
|
|
|
|
/* uses id->newid to match pointers with other copied data
|
|
* - called after single-user or other such
|
|
*/
|
|
void ipo_idnew (Ipo *ipo)
|
|
{
|
|
if (ipo) {
|
|
IpoCurve *icu;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
if (icu->driver)
|
|
ID_NEW(icu->driver->ob);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* --------------------- Find + Check ----------------------- */
|
|
|
|
/* find the IPO-curve within a given IPO-block with the adrcode of interest */
|
|
IpoCurve *find_ipocurve (Ipo *ipo, int adrcode)
|
|
{
|
|
if (ipo) {
|
|
IpoCurve *icu;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
if (icu->adrcode == adrcode)
|
|
return icu;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* return whether the given IPO block has a IPO-curve with the given adrcode */
|
|
short has_ipo_code(Ipo *ipo, int adrcode)
|
|
{
|
|
/* return success of faliure from trying to find such an IPO-curve */
|
|
return (find_ipocurve(ipo, adrcode) != NULL);
|
|
}
|
|
|
|
/* ---------------------- Make Local --------------------------- */
|
|
|
|
|
|
/* make the given IPO local (for Objects)
|
|
* - only lib users: do nothing
|
|
* - only local users: set flag
|
|
* - mixed: make copy
|
|
*/
|
|
void make_local_obipo (Ipo *src)
|
|
{
|
|
Object *ob;
|
|
Ipo *dst;
|
|
int local=0, lib=0;
|
|
|
|
/* check if only local and/or lib */
|
|
for (ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
if (ob->ipo == src) {
|
|
if (ob->id.lib) lib= 1;
|
|
else local= 1;
|
|
}
|
|
}
|
|
|
|
/* only local - set flag */
|
|
if (local && lib==0) {
|
|
src->id.lib= 0;
|
|
src->id.flag= LIB_LOCAL;
|
|
new_id(0, (ID *)src, 0);
|
|
}
|
|
/* mixed: make copy */
|
|
else if (local && lib) {
|
|
dst= copy_ipo(src);
|
|
dst->id.us= 0;
|
|
|
|
for (ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
if (ob->ipo == src) {
|
|
if (ob->id.lib == NULL) {
|
|
ob->ipo= dst;
|
|
dst->id.us++;
|
|
src->id.us--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* make the given IPO local (for Materials)
|
|
* - only lib users: do nothing
|
|
* - only local users: set flag
|
|
* - mixed: make copy
|
|
*/
|
|
void make_local_matipo (Ipo *src)
|
|
{
|
|
Material *ma;
|
|
Ipo *dst;
|
|
int local=0, lib=0;
|
|
|
|
/* check if only local and/or lib */
|
|
for (ma= G.main->mat.first; ma; ma= ma->id.next) {
|
|
if (ma->ipo == src) {
|
|
if (ma->id.lib) lib= 1;
|
|
else local= 1;
|
|
}
|
|
}
|
|
|
|
/* only local - set flag */
|
|
if (local && lib==0) {
|
|
src->id.lib= 0;
|
|
src->id.flag= LIB_LOCAL;
|
|
new_id(0, (ID *)src, 0);
|
|
}
|
|
/* mixed: make copy */
|
|
else if (local && lib) {
|
|
dst= copy_ipo(src);
|
|
dst->id.us= 0;
|
|
|
|
for (ma= G.main->mat.first; ma; ma= ma->id.next) {
|
|
if (ma->ipo == src) {
|
|
if (ma->id.lib == NULL) {
|
|
ma->ipo= dst;
|
|
dst->id.us++;
|
|
src->id.us--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* make the given IPO local (for ShapeKeys)
|
|
* - only lib users: do nothing
|
|
* - only local users: set flag
|
|
* - mixed: make copy
|
|
*/
|
|
void make_local_keyipo (Ipo *src)
|
|
{
|
|
Key *key;
|
|
Ipo *dst;
|
|
int local=0, lib=0;
|
|
|
|
/* check if only local and/or lib */
|
|
for (key= G.main->key.first; key; key= key->id.next) {
|
|
if (key->ipo == src) {
|
|
if (key->id.lib) lib= 1;
|
|
else local= 1;
|
|
}
|
|
}
|
|
|
|
/* only local - set flag */
|
|
if (local && lib==0) {
|
|
src->id.lib= 0;
|
|
src->id.flag= LIB_LOCAL;
|
|
new_id(0, (ID *)src, 0);
|
|
}
|
|
/* mixed: make copy */
|
|
else if (local && lib) {
|
|
dst= copy_ipo(src);
|
|
dst->id.us= 0;
|
|
|
|
for (key= G.main->key.first; key; key= key->id.next) {
|
|
if (key->ipo == src) {
|
|
if (key->id.lib == NULL) {
|
|
key->ipo= dst;
|
|
dst->id.us++;
|
|
src->id.us--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* generic call to make IPO's local */
|
|
void make_local_ipo (Ipo *ipo)
|
|
{
|
|
/* can't touch lib-linked data */
|
|
if (ipo->id.lib == NULL)
|
|
return;
|
|
|
|
/* with only one user, just set local flag */
|
|
if (ipo->id.us == 1) {
|
|
ipo->id.lib= 0;
|
|
ipo->id.flag= LIB_LOCAL;
|
|
new_id(0, (ID *)ipo, 0);
|
|
return;
|
|
}
|
|
|
|
/* when more than 1 user, can only make local for certain blocktypes */
|
|
switch (ipo->blocktype) {
|
|
case ID_OB:
|
|
make_local_obipo(ipo);
|
|
break;
|
|
case ID_MA:
|
|
make_local_matipo(ipo);
|
|
break;
|
|
case ID_KE:
|
|
make_local_keyipo(ipo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ***************************** Keyframe Column Tools ********************************* */
|
|
|
|
/* add a BezTriple to a column */
|
|
void add_to_cfra_elem(ListBase *lb, BezTriple *bezt)
|
|
{
|
|
CfraElem *ce, *cen;
|
|
|
|
for (ce= lb->first; ce; ce= ce->next) {
|
|
/* double key? */
|
|
if (ce->cfra == bezt->vec[1][0]) {
|
|
if (bezt->f2 & SELECT) ce->sel= bezt->f2;
|
|
return;
|
|
}
|
|
/* should key be inserted before this column? */
|
|
else if (ce->cfra > bezt->vec[1][0]) break;
|
|
}
|
|
|
|
/* create a new column */
|
|
cen= MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem");
|
|
if (ce) BLI_insertlinkbefore(lb, ce, cen);
|
|
else BLI_addtail(lb, cen);
|
|
|
|
cen->cfra= bezt->vec[1][0];
|
|
cen->sel= bezt->f2;
|
|
}
|
|
|
|
/* make a list of keyframe 'columns' in an IPO block */
|
|
void make_cfra_list (Ipo *ipo, ListBase *elems)
|
|
{
|
|
IpoCurve *icu;
|
|
BezTriple *bezt;
|
|
int a;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
if (icu->flag & IPO_VISIBLE) {
|
|
/* ... removed old checks for adrcode types from here ...
|
|
* - (was this used for IpoKeys in the past?)
|
|
*/
|
|
|
|
bezt= icu->bezt;
|
|
if (bezt) {
|
|
for (a=0; a < icu->totvert; a++, bezt++) {
|
|
add_to_cfra_elem(elems, bezt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ***************************** Timing Stuff ********************************* */
|
|
|
|
/* This (evil) function is needed to cope with two legacy Blender rendering features
|
|
* mblur (motion blur that renders 'subframes' and blurs them together), and fields
|
|
* rendering. Thus, the use of ugly globals from object.c
|
|
*/
|
|
// BAD... EVIL... JUJU...!!!!
|
|
float frame_to_float (int cfra) /* see also bsystem_time in object.c */
|
|
{
|
|
extern float bluroffs; /* bad stuff borrowed from object.c */
|
|
extern float fieldoffs;
|
|
float ctime;
|
|
|
|
ctime= (float)cfra;
|
|
ctime+= bluroffs+fieldoffs;
|
|
ctime*= G.scene->r.framelen;
|
|
|
|
return ctime;
|
|
}
|
|
|
|
/* Calculate the extents of IPO block's keyframes */
|
|
void calc_ipo_range (Ipo *ipo, float *start, float *end)
|
|
{
|
|
IpoCurve *icu;
|
|
float min=999999999.0f, max=-999999999.0f;
|
|
short foundvert=0;
|
|
|
|
if (ipo) {
|
|
for (icu=ipo->curve.first; icu; icu=icu->next) {
|
|
if (icu->totvert) {
|
|
min= MIN2(min, icu->bezt[0].vec[1][0]);
|
|
max= MAX2(max, icu->bezt[icu->totvert-1].vec[1][0]);
|
|
foundvert=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* minimum length is 1 frame */
|
|
if (foundvert) {
|
|
if (min == max) max += 1.0f;
|
|
*start= min;
|
|
*end= max;
|
|
}
|
|
else {
|
|
*start= 0.0f;
|
|
*end= 1.0f;
|
|
}
|
|
}
|
|
|
|
/* ***************************** IPO Curve Sanity ********************************* */
|
|
/* The functions here are used in various parts of Blender, usually after some editing
|
|
* of keyframe data has occurred. They ensure that keyframe data is properly ordered and
|
|
* that the handles are correctly
|
|
*/
|
|
|
|
/* This function recalculates the handles of an IPO-Curve
|
|
* If the BezTriples have been rearranged, sort them first before using this.
|
|
*/
|
|
void calchandles_ipocurve (IpoCurve *icu)
|
|
{
|
|
BezTriple *bezt, *prev, *next;
|
|
int a= icu->totvert;
|
|
|
|
/* Error checking:
|
|
* - need at least two points
|
|
* - need bezier keys
|
|
* - only bezier-interpolation has handles (for now)
|
|
*/
|
|
if (ELEM(NULL, icu, icu->bezt) || (a < 2) || ELEM(icu->ipo, IPO_CONST, IPO_LIN))
|
|
return;
|
|
|
|
/* get initial pointers */
|
|
bezt= icu->bezt;
|
|
prev= NULL;
|
|
next= (bezt + 1);
|
|
|
|
/* loop over all beztriples, adjusting handles */
|
|
while (a--) {
|
|
/* clamp timing of handles to be on either side of beztriple */
|
|
if (bezt->vec[0][0] > bezt->vec[1][0]) bezt->vec[0][0]= bezt->vec[1][0];
|
|
if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0]= bezt->vec[1][0];
|
|
|
|
/* calculate autohandles */
|
|
if (icu->flag & IPO_AUTO_HORIZ)
|
|
calchandleNurb(bezt, prev, next, 2); /* 2==special autohandle && keep extrema horizontal */
|
|
else
|
|
calchandleNurb(bezt, prev, next, 1); /* 1==special autohandle */
|
|
|
|
/* for automatic ease in and out */
|
|
if ((bezt->h1==HD_AUTO) && (bezt->h2==HD_AUTO)) {
|
|
/* only do this on first or last beztriple */
|
|
if ((a==0) || (a==icu->totvert-1)) {
|
|
/* set both handles to have same horizontal value as keyframe */
|
|
if (icu->extrap==IPO_HORIZ) {
|
|
bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* advance pointers for next iteration */
|
|
prev= bezt;
|
|
if (a == 1) next= NULL;
|
|
else next++;
|
|
bezt++;
|
|
}
|
|
}
|
|
|
|
/* Use when IPO-Curve with handles has changed
|
|
* It treats all BezTriples with the following rules:
|
|
* - PHASE 1: do types have to be altered?
|
|
* -> Auto handles: become aligned when selection status is NOT(000 || 111)
|
|
* -> Vector handles: become 'nothing' when (one half selected AND other not)
|
|
* - PHASE 2: recalculate handles
|
|
*/
|
|
void testhandles_ipocurve (IpoCurve *icu)
|
|
{
|
|
BezTriple *bezt;
|
|
int a;
|
|
|
|
/* only beztriples have handles (bpoints don't though) */
|
|
if (ELEM(NULL, icu, icu->bezt))
|
|
return;
|
|
|
|
/* loop over beztriples */
|
|
for (a=0, bezt=icu->bezt; a < icu->totvert; a++, bezt++) {
|
|
short flag= 0;
|
|
|
|
/* flag is initialised as selection status
|
|
* of beztriple control-points (labelled 0,1,2)
|
|
*/
|
|
if (bezt->f1 & SELECT) flag |= (1<<0); // == 1
|
|
if (bezt->f2 & SELECT) flag |= (1<<1); // == 2
|
|
if (bezt->f3 & SELECT) flag |= (1<<2); // == 4
|
|
|
|
/* one or two handles selected only */
|
|
if (ELEM(flag, 0, 7)==0) {
|
|
/* auto handles become aligned */
|
|
if (bezt->h1==HD_AUTO)
|
|
bezt->h1= HD_ALIGN;
|
|
if(bezt->h2==HD_AUTO)
|
|
bezt->h2= HD_ALIGN;
|
|
|
|
/* vector handles become 'free' when only one half selected */
|
|
if(bezt->h1==HD_VECT) {
|
|
/* only left half (1 or 2 or 1+2) */
|
|
if (flag < 4)
|
|
bezt->h1= 0;
|
|
}
|
|
if(bezt->h2==HD_VECT) {
|
|
/* only right half (4 or 2+4) */
|
|
if (flag > 3)
|
|
bezt->h2= 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* recalculate handles */
|
|
calchandles_ipocurve(icu);
|
|
}
|
|
|
|
/* This function sorts BezTriples so that they are arranged in chronological order,
|
|
* as tools working on IPO-Curves expect that the BezTriples are in order.
|
|
*/
|
|
void sort_time_ipocurve(IpoCurve *icu)
|
|
{
|
|
short ok= 1;
|
|
|
|
/* keep adjusting order of beztriples until nothing moves (bubble-sort) */
|
|
while (ok) {
|
|
ok= 0;
|
|
|
|
/* currently, will only be needed when there are beztriples */
|
|
if (icu->bezt) {
|
|
BezTriple *bezt;
|
|
int a;
|
|
|
|
/* loop over ALL points to adjust position in array and recalculate handles */
|
|
for (a=0, bezt=icu->bezt; a < icu->totvert; a++, bezt++) {
|
|
/* check if thee's a next beztriple which we could try to swap with current */
|
|
if (a < (icu->totvert-1)) {
|
|
/* swap if one is after the other (and indicate that order has changed) */
|
|
if (bezt->vec[1][0] > (bezt+1)->vec[1][0]) {
|
|
SWAP(BezTriple, *bezt, *(bezt+1));
|
|
ok= 1;
|
|
}
|
|
|
|
/* if either one of both of the points exceeds crosses over the keyframe time... */
|
|
if ( (bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0]) ) {
|
|
/* swap handles if they have switched sides for some reason */
|
|
SWAP(float, bezt->vec[0][0], bezt->vec[2][0]);
|
|
SWAP(float, bezt->vec[0][1], bezt->vec[2][1]);
|
|
}
|
|
else {
|
|
/* clamp handles */
|
|
if (bezt->vec[0][0] > bezt->vec[1][0])
|
|
bezt->vec[0][0]= bezt->vec[1][0];
|
|
if (bezt->vec[2][0] < bezt->vec[1][0])
|
|
bezt->vec[2][0]= bezt->vec[1][0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function tests if any BezTriples are out of order, thus requiring a sort */
|
|
int test_time_ipocurve (IpoCurve *icu)
|
|
{
|
|
int a;
|
|
|
|
/* currently, only need to test beztriples */
|
|
if (icu->bezt) {
|
|
BezTriple *bezt;
|
|
|
|
/* loop through all beztriples, stopping when one exceeds the one after it */
|
|
for (a=0, bezt= icu->bezt; a < (icu->totvert - 1); a++, bezt++) {
|
|
if (bezt->vec[1][0] > (bezt+1)->vec[1][0])
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* none need any swapping */
|
|
return 0;
|
|
}
|
|
|
|
/* --------- */
|
|
|
|
/* The total length of the handles is not allowed to be more
|
|
* than the horizontal distance between (v1-v4).
|
|
* This is to prevent curve loops.
|
|
*/
|
|
void correct_bezpart (float *v1, float *v2, float *v3, float *v4)
|
|
{
|
|
float h1[2], h2[2], len1, len2, len, fac;
|
|
|
|
/* calculate handle deltas */
|
|
h1[0]= v1[0]-v2[0];
|
|
h1[1]= v1[1]-v2[1];
|
|
h2[0]= v4[0]-v3[0];
|
|
h2[1]= v4[1]-v3[1];
|
|
|
|
/* calculate distances:
|
|
* - len = span of time between keyframes
|
|
* - len1 = length of handle of start key
|
|
* - len2 = length of handle of end key
|
|
*/
|
|
len= v4[0]- v1[0];
|
|
len1= (float)fabs(h1[0]);
|
|
len2= (float)fabs(h2[0]);
|
|
|
|
/* if the handles have no length, no need to do any corrections */
|
|
if ((len1+len2) == 0.0)
|
|
return;
|
|
|
|
/* the two handles cross over each other, so force them
|
|
* apart using the proportion they overlap
|
|
*/
|
|
if ((len1+len2) > len) {
|
|
fac= len/(len1+len2);
|
|
|
|
v2[0]= (v1[0]-fac*h1[0]);
|
|
v2[1]= (v1[1]-fac*h1[1]);
|
|
|
|
v3[0]= (v4[0]-fac*h2[0]);
|
|
v3[1]= (v4[1]-fac*h2[1]);
|
|
}
|
|
}
|
|
|
|
/* This function sets the interpolation mode for an entire Ipo-Curve.
|
|
* It is primarily used for patching old files, but is also used in the interface
|
|
* to make sure that all segments of the curve use the same interpolation.
|
|
*/
|
|
void set_interpolation_ipocurve (IpoCurve *icu, short ipo)
|
|
{
|
|
BezTriple *bezt;
|
|
int a;
|
|
|
|
/* validate arguments */
|
|
if (icu == NULL) return;
|
|
if (ELEM3(ipo, IPO_CONST, IPO_LIN, IPO_BEZ)==0) return;
|
|
|
|
/* set interpolation mode for whole curve */
|
|
icu->ipo= ipo;
|
|
|
|
/* set interpolation mode of all beztriples */
|
|
for (a=0, bezt=icu->bezt; a<icu->totvert; a++, bezt++)
|
|
bezt->ipo= ipo;
|
|
}
|
|
|
|
/* ***************************** Curve Calculations ********************************* */
|
|
|
|
/* find root/zero */
|
|
int findzero (float x, float q0, float q1, float q2, float q3, float *o)
|
|
{
|
|
double c0, c1, c2, c3, a, b, c, p, q, d, t, phi;
|
|
int nr= 0;
|
|
|
|
c0= q0 - x;
|
|
c1= 3 * (q1 - q0);
|
|
c2= 3 * (q0 - 2*q1 + q2);
|
|
c3= q3 - q0 + 3 * (q1 - q2);
|
|
|
|
if (c3 != 0.0) {
|
|
a= c2/c3;
|
|
b= c1/c3;
|
|
c= c0/c3;
|
|
a= a/3;
|
|
|
|
p= b/3 - a*a;
|
|
q= (2*a*a*a - a*b + c) / 2;
|
|
d= q*q + p*p*p;
|
|
|
|
if (d > 0.0) {
|
|
t= sqrt(d);
|
|
o[0]= (float)(Sqrt3d(-q+t) + Sqrt3d(-q-t) - a);
|
|
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1;
|
|
else return 0;
|
|
}
|
|
else if (d == 0.0) {
|
|
t= Sqrt3d(-q);
|
|
o[0]= (float)(2*t - a);
|
|
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++;
|
|
o[nr]= (float)(-t-a);
|
|
|
|
if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1;
|
|
else return nr;
|
|
}
|
|
else {
|
|
phi= acos(-q / sqrt(-(p*p*p)));
|
|
t= sqrt(-p);
|
|
p= cos(phi/3);
|
|
q= sqrt(3 - 3*p*p);
|
|
o[0]= (float)(2*t*p - a);
|
|
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++;
|
|
o[nr]= (float)(-t * (p + q) - a);
|
|
|
|
if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) nr++;
|
|
o[nr]= (float)(-t * (p - q) - a);
|
|
|
|
if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1;
|
|
else return nr;
|
|
}
|
|
}
|
|
else {
|
|
a=c2;
|
|
b=c1;
|
|
c=c0;
|
|
|
|
if (a != 0.0) {
|
|
// discriminant
|
|
p= b*b - 4*a*c;
|
|
|
|
if (p > 0) {
|
|
p= sqrt(p);
|
|
o[0]= (float)((-b-p) / (2 * a));
|
|
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) nr++;
|
|
o[nr]= (float)((-b+p)/(2*a));
|
|
|
|
if ((o[nr] >= SMALL) && (o[nr] <= 1.000001)) return nr+1;
|
|
else return nr;
|
|
}
|
|
else if (p == 0) {
|
|
o[0]= (float)(-b / (2 * a));
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1;
|
|
else return 0;
|
|
}
|
|
}
|
|
else if (b != 0.0) {
|
|
o[0]= (float)(-c/b);
|
|
|
|
if ((o[0] >= SMALL) && (o[0] <= 1.000001)) return 1;
|
|
else return 0;
|
|
}
|
|
else if (c == 0.0) {
|
|
o[0]= 0.0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void berekeny (float f1, float f2, float f3, float f4, float *o, int b)
|
|
{
|
|
float t, c0, c1, c2, c3;
|
|
int a;
|
|
|
|
c0= f1;
|
|
c1= 3.0f * (f2 - f1);
|
|
c2= 3.0f * (f1 - 2.0f*f2 + f3);
|
|
c3= f4 - f1 + 3.0f * (f2 - f3);
|
|
|
|
for (a=0; a < b; a++) {
|
|
t= o[a];
|
|
o[a]= c0 + t*c1 + t*t*c2 + t*t*t*c3;
|
|
}
|
|
}
|
|
|
|
void berekenx (float *f, float *o, int b)
|
|
{
|
|
float t, c0, c1, c2, c3;
|
|
int a;
|
|
|
|
c0= f[0];
|
|
c1= 3 * (f[3] - f[0]);
|
|
c2= 3 * (f[0] - 2*f[3] + f[6]);
|
|
c3= f[9] - f[0] + 3 * (f[3] - f[6]);
|
|
|
|
for (a=0; a < b; a++) {
|
|
t= o[a];
|
|
o[a]= c0 + t*c1 + t*t*c2 + t*t*t*c3;
|
|
}
|
|
}
|
|
|
|
/* ***************************** IPO - Calculations ********************************* */
|
|
|
|
/* ---------------------- Curve Evaluation --------------------------- */
|
|
|
|
/* helper function for evaluating drivers:
|
|
* - we need the local transform = current transform - (parent transform + bone transform)
|
|
* - (local transform is on action channel level)
|
|
*/
|
|
static void posechannel_get_local_transform (bPoseChannel *pchan, float loc[], float eul[], float size[])
|
|
{
|
|
float parmat[4][4], offs_bone[4][4], imat[4][4];
|
|
float diff_mat[4][4];
|
|
|
|
/* get first the parent + bone transform in parmat */
|
|
if (pchan->parent) {
|
|
/* bone transform itself */
|
|
Mat4CpyMat3(offs_bone, pchan->bone->bone_mat);
|
|
|
|
/* The bone's root offset (is in the parent's coordinate system) */
|
|
VECCOPY(offs_bone[3], pchan->bone->head);
|
|
|
|
/* Get the length translation of parent (length along y axis) */
|
|
offs_bone[3][1]+= pchan->parent->bone->length;
|
|
|
|
Mat4MulSerie(parmat, pchan->parent->pose_mat, offs_bone, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
/* invert it */
|
|
Mat4Invert(imat, parmat);
|
|
}
|
|
else {
|
|
Mat4CpyMat3(offs_bone, pchan->bone->bone_mat);
|
|
VECCOPY(offs_bone[3], pchan->bone->head);
|
|
|
|
/* invert it */
|
|
Mat4Invert(imat, offs_bone);
|
|
}
|
|
|
|
/* difference: current transform - (parent transform + bone transform) */
|
|
Mat4MulMat4(diff_mat, pchan->pose_mat, imat);
|
|
|
|
/* extract relevant components */
|
|
if (loc)
|
|
VECCOPY(loc, diff_mat[3]);
|
|
if (eul)
|
|
Mat4ToEul(diff_mat, eul);
|
|
if (size)
|
|
Mat4ToSize(diff_mat, size);
|
|
}
|
|
|
|
/* evaluate an IPO-driver to get a 'time' value to use instead of "ipotime"
|
|
* - "ipotime" is the frame at which IPO-curve is being evaluated
|
|
* - has to return a float value
|
|
*/
|
|
static float eval_driver (IpoDriver *driver, float ipotime)
|
|
{
|
|
#ifndef DISABLE_PYTHON
|
|
/* currently, drivers are either PyDrivers (evaluating a PyExpression, or Object/Pose-Channel transforms) */
|
|
if (driver->type == IPO_DRIVER_TYPE_PYTHON) {
|
|
/* check for empty or invalid expression */
|
|
if ( (driver->name[0] == '\0') ||
|
|
(driver->flag & IPO_DRIVER_FLAG_INVALID) )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
/* this evaluates the expression using Python,and returns its result:
|
|
* - on errors it reports, then returns 0.0f
|
|
*/
|
|
return BPY_pydriver_eval(driver);
|
|
}
|
|
else
|
|
#endif /* DISABLE_PYTHON */
|
|
{
|
|
Object *ob= driver->ob;
|
|
|
|
/* must have an object to evaluate */
|
|
if (ob == NULL)
|
|
return 0.0f;
|
|
|
|
/* if a proxy, use the proxy source*/
|
|
if (ob->proxy_from)
|
|
ob= ob->proxy_from;
|
|
|
|
/* use given object as driver */
|
|
if (driver->blocktype == ID_OB) {
|
|
/* depsgraph failure: ob ipos are calculated in where_is_object, this might get called too late */
|
|
if ((ob->ipo) && (ob->ctime != ipotime)) {
|
|
/* calculate the value of relevant channel on the Object, but do not write the value
|
|
* calculated on to the Object but onto "ipotime" instead
|
|
*/
|
|
calc_ipo_spec(ob->ipo, driver->adrcode, &ipotime);
|
|
return ipotime;
|
|
}
|
|
|
|
/* return the value of the relevant channel */
|
|
switch (driver->adrcode) {
|
|
case OB_LOC_X:
|
|
return ob->loc[0];
|
|
case OB_LOC_Y:
|
|
return ob->loc[1];
|
|
case OB_LOC_Z:
|
|
return ob->loc[2];
|
|
case OB_ROT_X: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( ob->rot[0]/(M_PI_2/9.0) );
|
|
case OB_ROT_Y: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( ob->rot[1]/(M_PI_2/9.0) );
|
|
case OB_ROT_Z: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( ob->rot[2]/(M_PI_2/9.0) );
|
|
case OB_SIZE_X:
|
|
return ob->size[0];
|
|
case OB_SIZE_Y:
|
|
return ob->size[1];
|
|
case OB_SIZE_Z:
|
|
return ob->size[2];
|
|
}
|
|
}
|
|
|
|
/* use given pose-channel as driver */
|
|
else { /* ID_AR */
|
|
bPoseChannel *pchan= get_pose_channel(ob->pose, driver->name);
|
|
|
|
/* must have at least 1 bone to use */
|
|
if (pchan && pchan->bone) {
|
|
/* rotation difference is not a simple driver (i.e. value drives value), but the angle between 2 bones is driving stuff...
|
|
* - the name of the second pchan is also stored in driver->name, but packed after the other one by DRIVER_NAME_OFFS chars
|
|
*/
|
|
if (driver->adrcode == OB_ROT_DIFF) {
|
|
bPoseChannel *pchan2= get_pose_channel(ob->pose, driver->name+DRIVER_NAME_OFFS);
|
|
|
|
if (pchan2 && pchan2->bone) {
|
|
float q1[4], q2[4], quat[4], angle;
|
|
|
|
Mat4ToQuat(pchan->pose_mat, q1);
|
|
Mat4ToQuat(pchan2->pose_mat, q2);
|
|
|
|
QuatInv(q1);
|
|
QuatMul(quat, q1, q2);
|
|
angle = 2.0f * (saacos(quat[0]));
|
|
angle= ABS(angle);
|
|
|
|
return (angle > M_PI) ? (float)((2.0f * M_PI) - angle) : (float)(angle);
|
|
}
|
|
}
|
|
|
|
/* standard driver */
|
|
else {
|
|
float loc[3], eul[3], size[3];
|
|
|
|
/* retrieve local transforms to return
|
|
* - we use eulers here NOT quats, so that Objects can be driven by bones easily
|
|
* also, this way is more understandable for users
|
|
*/
|
|
posechannel_get_local_transform(pchan, loc, eul, size);
|
|
|
|
switch (driver->adrcode) {
|
|
case OB_LOC_X:
|
|
return loc[0];
|
|
case OB_LOC_Y:
|
|
return loc[1];
|
|
case OB_LOC_Z:
|
|
return loc[2];
|
|
case OB_ROT_X: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( eul[0]/(M_PI_2/9.0) );
|
|
case OB_ROT_Y: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( eul[1]/(M_PI_2/9.0) );
|
|
case OB_ROT_Z: /* hack: euler rotations are divided by 10 deg to fit on same axes as other channels */
|
|
return (float)( eul[2]/(M_PI_2/9.0) );
|
|
case OB_SIZE_X:
|
|
return size[0];
|
|
case OB_SIZE_Y:
|
|
return size[1];
|
|
case OB_SIZE_Z:
|
|
return size[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* return 0.0f, as couldn't find relevant data to use */
|
|
return 0.0f;
|
|
}
|
|
|
|
/* evaluate and return the value of the given IPO-curve at the specified frame ("evaltime") */
|
|
float eval_icu (IpoCurve *icu, float evaltime)
|
|
{
|
|
float cvalue = 0.0f;
|
|
|
|
/* if there is a driver, evaluate it to find value to use as "evaltime"
|
|
* - this value will also be returned as the value of the 'curve', if there are no keyframes
|
|
*/
|
|
if (icu->driver) {
|
|
/* ipotime now serves as input for the curve */
|
|
evaltime= cvalue= eval_driver(icu->driver, evaltime);
|
|
}
|
|
|
|
/* there are keyframes (in the form of BezTriples) which can be interpolated between */
|
|
if (icu->bezt) {
|
|
/* get pointers */
|
|
BezTriple *bezt, *prevbezt, *lastbezt;
|
|
float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac;
|
|
float cycdx, cycdy, ofs, cycyofs= 0.0f;
|
|
int a, b;
|
|
|
|
/* get pointers */
|
|
a= icu->totvert-1;
|
|
prevbezt= icu->bezt;
|
|
bezt= prevbezt+1;
|
|
lastbezt= prevbezt + a;
|
|
|
|
/* extrapolation mode is 'cyclic' - find relative place within a cycle */
|
|
if (icu->extrap & IPO_CYCL) {
|
|
/* ofs is start frame of cycle */
|
|
ofs= prevbezt->vec[1][0];
|
|
|
|
/* calculate period and amplitude (total height) of a cycle */
|
|
cycdx= lastbezt->vec[1][0] - prevbezt->vec[1][0];
|
|
cycdy= lastbezt->vec[1][1] - prevbezt->vec[1][1];
|
|
|
|
/* cycle occurs over some period of time (cycdx should be positive all the time) */
|
|
if (cycdx) {
|
|
/* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle
|
|
* - IPO_CYCLX = (IPO_CYCL + IPO_DIR)
|
|
*/
|
|
if (icu->extrap & IPO_DIR) {
|
|
cycyofs = (float)floor((evaltime - ofs) / cycdx);
|
|
cycyofs *= cycdy;
|
|
}
|
|
|
|
/* calculate where in the cycle we are (overwrite evaltime to reflect this) */
|
|
evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs);
|
|
if (evaltime < ofs) evaltime += cycdx;
|
|
}
|
|
}
|
|
|
|
/* evaluation time at or past endpoints? */
|
|
if (prevbezt->vec[1][0] >= evaltime) {
|
|
/* before or on first keyframe */
|
|
if ((icu->extrap & IPO_DIR) && (prevbezt->ipo != IPO_CONST)) {
|
|
/* linear or bezier interpolation */
|
|
if (prevbezt->ipo==IPO_LIN) {
|
|
/* Use the next center point instead of our own handle for
|
|
* linear interpolated extrapolate
|
|
*/
|
|
if (icu->totvert == 1)
|
|
cvalue= prevbezt->vec[1][1];
|
|
else {
|
|
bezt = prevbezt+1;
|
|
dx= prevbezt->vec[1][0] - evaltime;
|
|
fac= bezt->vec[1][0] - prevbezt->vec[1][0];
|
|
|
|
/* prevent division by zero */
|
|
if (fac) {
|
|
fac= (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac;
|
|
cvalue= prevbezt->vec[1][1] - (fac * dx);
|
|
}
|
|
else
|
|
cvalue= prevbezt->vec[1][1];
|
|
}
|
|
}
|
|
else {
|
|
/* Use the first handle (earlier) of first BezTriple to calculate the
|
|
* gradient and thus the value of the curve at evaltime
|
|
*/
|
|
dx= prevbezt->vec[1][0] - evaltime;
|
|
fac= prevbezt->vec[1][0] - prevbezt->vec[0][0];
|
|
|
|
/* prevent division by zero */
|
|
if (fac) {
|
|
fac= (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac;
|
|
cvalue= prevbezt->vec[1][1] - (fac * dx);
|
|
}
|
|
else
|
|
cvalue= prevbezt->vec[1][1];
|
|
}
|
|
}
|
|
else {
|
|
/* constant (IPO_HORIZ) extrapolation or constant interpolation,
|
|
* so just extend first keyframe's value
|
|
*/
|
|
cvalue= prevbezt->vec[1][1];
|
|
}
|
|
}
|
|
else if (lastbezt->vec[1][0] <= evaltime) {
|
|
/* after or on last keyframe */
|
|
if( (icu->extrap & IPO_DIR) && (lastbezt->ipo != IPO_CONST)) {
|
|
/* linear or bezier interpolation */
|
|
if (lastbezt->ipo==IPO_LIN) {
|
|
/* Use the next center point instead of our own handle for
|
|
* linear interpolated extrapolate
|
|
*/
|
|
if (icu->totvert == 1)
|
|
cvalue= lastbezt->vec[1][1];
|
|
else {
|
|
prevbezt = lastbezt - 1;
|
|
dx= evaltime - lastbezt->vec[1][0];
|
|
fac= lastbezt->vec[1][0] - prevbezt->vec[1][0];
|
|
|
|
/* prevent division by zero */
|
|
if (fac) {
|
|
fac= (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac;
|
|
cvalue= lastbezt->vec[1][1] + (fac * dx);
|
|
}
|
|
else
|
|
cvalue= lastbezt->vec[1][1];
|
|
}
|
|
}
|
|
else {
|
|
/* Use the gradient of the second handle (later) of last BezTriple to calculate the
|
|
* gradient and thus the value of the curve at evaltime
|
|
*/
|
|
dx= evaltime - lastbezt->vec[1][0];
|
|
fac= lastbezt->vec[2][0] - lastbezt->vec[1][0];
|
|
|
|
/* prevent division by zero */
|
|
if (fac) {
|
|
fac= (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac;
|
|
cvalue= lastbezt->vec[1][1] + (fac * dx);
|
|
}
|
|
else
|
|
cvalue= lastbezt->vec[1][1];
|
|
}
|
|
}
|
|
else {
|
|
/* constant (IPO_HORIZ) extrapolation or constant interpolation,
|
|
* so just extend last keyframe's value
|
|
*/
|
|
cvalue= lastbezt->vec[1][1];
|
|
}
|
|
}
|
|
else {
|
|
/* evaltime occurs somewhere in the middle of the curve */
|
|
for (a=0; prevbezt && bezt && (a < icu->totvert-1); a++, prevbezt=bezt, bezt++) {
|
|
/* evaltime occurs within the interval defined by these two keyframes */
|
|
if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) {
|
|
/* value depends on interpolation mode */
|
|
if (prevbezt->ipo == IPO_CONST) {
|
|
/* constant (evaltime not relevant, so no interpolation needed) */
|
|
cvalue= prevbezt->vec[1][1];
|
|
}
|
|
else if (prevbezt->ipo == IPO_LIN) {
|
|
/* linear - interpolate between values of the two keyframes */
|
|
fac= bezt->vec[1][0] - prevbezt->vec[1][0];
|
|
|
|
/* prevent division by zero */
|
|
if (fac) {
|
|
fac= (evaltime - prevbezt->vec[1][0]) / fac;
|
|
cvalue= prevbezt->vec[1][1] + (fac * (bezt->vec[1][1] - prevbezt->vec[1][1]));
|
|
}
|
|
else
|
|
cvalue= prevbezt->vec[1][1];
|
|
}
|
|
else {
|
|
/* bezier interpolation */
|
|
/* v1,v2 are the first keyframe and its 2nd handle */
|
|
v1[0]= prevbezt->vec[1][0];
|
|
v1[1]= prevbezt->vec[1][1];
|
|
v2[0]= prevbezt->vec[2][0];
|
|
v2[1]= prevbezt->vec[2][1];
|
|
/* v3,v4 are the last keyframe's 1st handle + the last keyframe */
|
|
v3[0]= bezt->vec[0][0];
|
|
v3[1]= bezt->vec[0][1];
|
|
v4[0]= bezt->vec[1][0];
|
|
v4[1]= bezt->vec[1][1];
|
|
|
|
/* adjust handles so that they don't overlap (forming a loop) */
|
|
correct_bezpart(v1, v2, v3, v4);
|
|
|
|
/* try to get a value for this position - if failure, try another set of points */
|
|
b= findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl);
|
|
if (b) {
|
|
berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1);
|
|
cvalue= opl[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* apply y-offset (for 'cyclic extrapolation') to calculated value */
|
|
cvalue += cycyofs;
|
|
}
|
|
|
|
/* clamp evaluated value to lie within allowable value range for this channel */
|
|
if (icu->ymin < icu->ymax) {
|
|
CLAMP(cvalue, icu->ymin, icu->ymax);
|
|
}
|
|
|
|
/* return evaluated value */
|
|
return cvalue;
|
|
}
|
|
|
|
/* ------------------- IPO-Block/Curve Calculation - General API ----------------------- */
|
|
|
|
/* calculate the value of the given IPO-curve at the current frame, and set its curval */
|
|
void calc_icu (IpoCurve *icu, float ctime)
|
|
{
|
|
/* calculate and set curval (evaluates driver too) */
|
|
icu->curval= eval_icu(icu, ctime);
|
|
}
|
|
|
|
/* calculate for the current frame, all IPO-curves in IPO-block that can be evaluated
|
|
* - icu->curval is set for all IPO-curves which are evaluated!
|
|
*/
|
|
void calc_ipo (Ipo *ipo, float ctime)
|
|
{
|
|
IpoCurve *icu;
|
|
|
|
/* if there is no IPO block to evaluate, or whole block is "muted" */
|
|
if (ipo == NULL) return;
|
|
if (ipo->muteipo) return;
|
|
|
|
/* loop over all curves */
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
/* only evaluated curve if allowed to:
|
|
* - Muted channels should not be evaluated as they shouldn't have any effect
|
|
* --> user explictly turned them off!
|
|
* - Drivers should be evaluated at all updates
|
|
* --> TODO Note: drivers should be separated from standard channels
|
|
* - IPO_LOCK is not set, as it is set by some internal mechanisms to prevent
|
|
* IPO-curve from overwriting data (currently only used for IPO-Record).
|
|
*/
|
|
if ((icu->driver) || (icu->flag & IPO_LOCK)==0) {
|
|
if ((icu->flag & IPO_MUTE)==0)
|
|
calc_icu(icu, ctime);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ------------------- IPO-Block/Curve Calculation - Special Hacks ----------------------- */
|
|
|
|
/* Calculate and return the value of the 'Time' Ipo-Curve from an Object,
|
|
* OR return the current time if not found
|
|
* - used in object.c -> bsystem_time()
|
|
*/
|
|
float calc_ipo_time (Ipo *ipo, float ctime)
|
|
{
|
|
/* only Time IPO from Object IPO-blocks are relevant */
|
|
if ((ipo) && (ipo->blocktype == ID_OB)) {
|
|
IpoCurve *icu= find_ipocurve(ipo, OB_TIME);
|
|
|
|
/* only calculate (and set icu->curval) for time curve */
|
|
if (icu) {
|
|
calc_icu(icu, ctime);
|
|
return (10.0f * icu->curval);
|
|
}
|
|
}
|
|
|
|
/* no appropriate time-curve found */
|
|
return ctime;
|
|
}
|
|
|
|
/* Evaluate the specified channel in the given IPO block on the specified frame (ctime),
|
|
* writing the value into that channel's icu->curval, but ALSO dumping it in ctime.
|
|
* - Returns success and modifies ctime!
|
|
*/
|
|
short calc_ipo_spec (Ipo *ipo, int adrcode, float *ctime)
|
|
{
|
|
IpoCurve *icu= find_ipocurve(ipo, adrcode);
|
|
|
|
/* only evaluate if found */
|
|
if (icu) {
|
|
/* only calculate if allowed to (not locked and not muted)
|
|
* - drivers not taken into account, because this may be called when calculating a driver
|
|
*/
|
|
if ((icu->flag & (IPO_LOCK|IPO_MUTE))==0)
|
|
calc_icu(icu, *ctime);
|
|
|
|
/* value resulting from calculations is written into ctime! */
|
|
*ctime= icu->curval;
|
|
return 1;
|
|
}
|
|
|
|
/* couldn't evaluate */
|
|
return 0;
|
|
}
|
|
|
|
/* ***************************** IPO - DataAPI ********************************* */
|
|
|
|
/* --------------------- Flush/Execute IPO Values ----------------------------- */
|
|
|
|
/* Flush IpoCurve->curvals to the data they affect (defined by ID)
|
|
* - not for Actions or Constraints! (those have their own special handling)
|
|
*/
|
|
void execute_ipo (ID *id, Ipo *ipo)
|
|
{
|
|
IpoCurve *icu;
|
|
void *poin;
|
|
int type;
|
|
|
|
/* don't do anything without an IPO block */
|
|
if (ipo == NULL)
|
|
return;
|
|
|
|
/* loop over IPO Curves, getting pointer to var to affect, and write into that pointer */
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
poin= get_ipo_poin(id, icu, &type);
|
|
if (poin) write_ipo_poin(poin, type, icu->curval);
|
|
}
|
|
}
|
|
|
|
/* Flush Action-Channel IPO data to Pose Channel */
|
|
void execute_action_ipo (bActionChannel *achan, bPoseChannel *pchan)
|
|
{
|
|
/* only do this if there's an Action Channel and Pose Channel to use */
|
|
if (achan && achan->ipo && pchan) {
|
|
IpoCurve *icu;
|
|
|
|
/* loop over IPO-curves, getting a pointer to pchan var to write to */
|
|
for (icu= achan->ipo->curve.first; icu; icu= icu->next) {
|
|
void *poin= get_pchan_ipo_poin(pchan, icu->adrcode);
|
|
|
|
if (poin) {
|
|
/* only euler-rotations are of type float-degree, all others are 'float' only */
|
|
if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z))
|
|
write_ipo_poin(poin, IPO_FLOAT_DEGR, icu->curval);
|
|
else
|
|
write_ipo_poin(poin, IPO_FLOAT, icu->curval);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* --------------------- Force Calculation + Flush IPO Values ----------------------------- */
|
|
|
|
/* Calculate values for given IPO block, then flush to all of block's users
|
|
* - for general usage
|
|
*/
|
|
void do_ipo (Ipo *ipo)
|
|
{
|
|
if (ipo) {
|
|
float ctime= frame_to_float(G.scene->r.cfra);
|
|
|
|
/* calculate values, then flush to all users of this IPO block */
|
|
calc_ipo(ipo, ctime);
|
|
do_ipo_nocalc(ipo);
|
|
}
|
|
}
|
|
|
|
/* Calculate values for given Material's IPO block, then flush to given Material only */
|
|
void do_mat_ipo (Material *ma)
|
|
{
|
|
float ctime;
|
|
|
|
if (ELEM(NULL, ma, ma->ipo))
|
|
return;
|
|
|
|
ctime= frame_to_float(G.scene->r.cfra);
|
|
/* if(ob->ipoflag & OB_OFFS_OB) ctime-= ob->sf; */
|
|
|
|
/* calculate values for current time, then flush values to given material only */
|
|
calc_ipo(ma->ipo, ctime);
|
|
execute_ipo((ID *)ma, ma->ipo);
|
|
}
|
|
|
|
/* Calculate values for given Object's IPO block, then flush to given Object only
|
|
* - there's also some funky stuff that looks like it's for scene layers
|
|
*/
|
|
void do_ob_ipo (Object *ob)
|
|
{
|
|
float ctime;
|
|
unsigned int lay;
|
|
|
|
if (ob->ipo == NULL)
|
|
return;
|
|
|
|
/* do not set ob->ctime here: for example when parent in invisible layer */
|
|
ctime= bsystem_time(ob, (float) G.scene->r.cfra, 0.0);
|
|
|
|
/* calculate values of */
|
|
calc_ipo(ob->ipo, ctime);
|
|
|
|
/* Patch: remember localview */
|
|
lay= ob->lay & 0xFF000000;
|
|
|
|
/* flush IPO values to this object only */
|
|
execute_ipo((ID *)ob, ob->ipo);
|
|
|
|
/* hack: for layer animation??? - is this what this is? (Aligorith, 28Sep2008) */
|
|
ob->lay |= lay;
|
|
if ((ob->id.name[2]=='S') && (ob->id.name[3]=='C') && (ob->id.name[4]=='E')) {
|
|
if (strcmp(G.scene->id.name+2, ob->id.name+6)==0) {
|
|
G.scene->lay= ob->lay;
|
|
//XXX copy_view3d_lock(0);
|
|
/* no redraw here! creates too many calls */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Only execute those IPO-Curves with drivers, on the current frame, for the given Object
|
|
* - TODO: Drivers should really be made separate from standard anim channels
|
|
*/
|
|
void do_ob_ipodrivers (Object *ob, Ipo *ipo, float ctime)
|
|
{
|
|
IpoCurve *icu;
|
|
void *poin;
|
|
int type;
|
|
|
|
for (icu= ipo->curve.first; icu; icu= icu->next) {
|
|
if (icu->driver) {
|
|
icu->curval= eval_icu(icu, ctime);
|
|
|
|
poin= get_ipo_poin((ID *)ob, icu, &type);
|
|
if (poin) write_ipo_poin(poin, type, icu->curval);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Special variation to calculate IPO values for Sequence + perform other stuff */
|
|
void do_seq_ipo (Sequence *seq, int cfra)
|
|
{
|
|
float ctime, div;
|
|
|
|
/* seq_ipo has an exception: calc both fields immediately */
|
|
if (seq->ipo) {
|
|
if ((seq->flag & SEQ_IPO_FRAME_LOCKED) != 0) {
|
|
ctime = frame_to_float(cfra);
|
|
div = 1.0;
|
|
}
|
|
else {
|
|
ctime= frame_to_float(cfra - seq->startdisp);
|
|
div= (seq->enddisp - seq->startdisp) / 100.0f;
|
|
if (div == 0.0) return;
|
|
}
|
|
|
|
/* 2nd field */
|
|
calc_ipo(seq->ipo, (ctime+0.5f)/div);
|
|
execute_ipo((ID *)seq, seq->ipo);
|
|
seq->facf1= seq->facf0;
|
|
|
|
/* 1st field */
|
|
calc_ipo(seq->ipo, ctime/div);
|
|
execute_ipo((ID *)seq, seq->ipo);
|
|
}
|
|
else
|
|
seq->facf1= seq->facf0= 1.0f;
|
|
}
|
|
|
|
/* --------- */
|
|
|
|
|
|
/* exception: it does calc for objects...
|
|
* now find out why this routine was used anyway!
|
|
*/
|
|
void do_ipo_nocalc (Ipo *ipo)
|
|
{
|
|
Object *ob;
|
|
Material *ma;
|
|
Tex *tex;
|
|
World *wo;
|
|
Lamp *la;
|
|
Camera *ca;
|
|
bSound *snd;
|
|
|
|
if (ipo == NULL)
|
|
return;
|
|
|
|
/* only flush IPO values (without calculating first/again) on
|
|
* to the datablocks that use the given IPO block
|
|
*/
|
|
switch (ipo->blocktype) {
|
|
case ID_OB:
|
|
for (ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
if (ob->ipo == ipo) do_ob_ipo(ob);
|
|
}
|
|
break;
|
|
case ID_MA:
|
|
for (ma= G.main->mat.first; ma; ma= ma->id.next) {
|
|
if (ma->ipo == ipo) execute_ipo((ID *)ma, ipo);
|
|
}
|
|
break;
|
|
case ID_TE:
|
|
for (tex= G.main->tex.first; tex; tex= tex->id.next) {
|
|
if (tex->ipo == ipo) execute_ipo((ID *)tex, ipo);
|
|
}
|
|
break;
|
|
case ID_WO:
|
|
for (wo= G.main->world.first; wo; wo= wo->id.next) {
|
|
if (wo->ipo == ipo) execute_ipo((ID *)wo, ipo);
|
|
}
|
|
break;
|
|
case ID_LA:
|
|
for (la= G.main->lamp.first; la; la= la->id.next) {
|
|
if (la->ipo == ipo) execute_ipo((ID *)la, ipo);
|
|
}
|
|
break;
|
|
case ID_CA:
|
|
for (ca= G.main->camera.first; ca; ca= ca->id.next) {
|
|
if (ca->ipo == ipo) execute_ipo((ID *)ca, ipo);
|
|
}
|
|
break;
|
|
case ID_SO:
|
|
for (snd= G.main->sound.first; snd; snd= snd->id.next) {
|
|
if (snd->ipo == ipo) execute_ipo((ID *)snd, ipo);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Executes IPO's for whole database on frame change, in a specified order,
|
|
* with datablocks being calculated in alphabetical order
|
|
* - called on scene_update_for_newframe() only
|
|
*/
|
|
void do_all_data_ipos ()
|
|
{
|
|
Material *ma;
|
|
Tex *tex;
|
|
World *wo;
|
|
Ipo *ipo;
|
|
Lamp *la;
|
|
Key *key;
|
|
Camera *ca;
|
|
bSound *snd;
|
|
Sequence *seq;
|
|
Editing *ed;
|
|
Base *base;
|
|
float ctime;
|
|
|
|
ctime= frame_to_float(G.scene->r.cfra);
|
|
|
|
/* this exception cannot be depgraphed yet... what todo with objects in other layers?... */
|
|
for (base= G.scene->base.first; base; base= base->next) {
|
|
Object *ob= base->object;
|
|
|
|
/* only update layer when an ipo */
|
|
if (has_ipo_code(ob->ipo, OB_LAY)) {
|
|
do_ob_ipo(ob);
|
|
base->lay= ob->lay;
|
|
}
|
|
}
|
|
|
|
/* layers for the set...*/
|
|
if (G.scene->set) {
|
|
for (base= G.scene->set->base.first; base; base= base->next) {
|
|
Object *ob= base->object;
|
|
|
|
if (has_ipo_code(ob->ipo, OB_LAY)) {
|
|
do_ob_ipo(ob);
|
|
base->lay= ob->lay;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Calculate all IPO blocks in use, execept those for Objects */
|
|
for (ipo= G.main->ipo.first; ipo; ipo= ipo->id.next) {
|
|
if ((ipo->id.us) && (ipo->blocktype != ID_OB)) {
|
|
calc_ipo(ipo, ctime);
|
|
}
|
|
}
|
|
|
|
/* Texture Blocks */
|
|
for (tex= G.main->tex.first; tex; tex= tex->id.next) {
|
|
if (tex->ipo) execute_ipo((ID *)tex, tex->ipo);
|
|
}
|
|
|
|
/* Material Blocks */
|
|
for (ma= G.main->mat.first; ma; ma= ma->id.next) {
|
|
if (ma->ipo) execute_ipo((ID *)ma, ma->ipo);
|
|
}
|
|
|
|
/* World Blocks */
|
|
for (wo= G.main->world.first; wo; wo= wo->id.next) {
|
|
if (wo->ipo) execute_ipo((ID *)wo, wo->ipo);
|
|
}
|
|
|
|
/* ShapeKey Blocks */
|
|
for (key= G.main->key.first; key; key= key->id.next) {
|
|
if (key->ipo) execute_ipo((ID *)key, key->ipo);
|
|
}
|
|
|
|
/* Lamp Blocks */
|
|
for (la= G.main->lamp.first; la; la= la->id.next) {
|
|
if (la->ipo) execute_ipo((ID *)la, la->ipo);
|
|
}
|
|
|
|
/* Camera Blocks */
|
|
for (ca= G.main->camera.first; ca; ca= ca->id.next) {
|
|
if (ca->ipo) execute_ipo((ID *)ca, ca->ipo);
|
|
}
|
|
|
|
/* Sound Blocks (Old + Unused) */
|
|
for (snd= G.main->sound.first; snd; snd= snd->id.next) {
|
|
if (snd->ipo) execute_ipo((ID *)snd, snd->ipo);
|
|
}
|
|
|
|
/* Sequencer: process FAC Ipos used as volume envelopes */
|
|
ed= G.scene->ed;
|
|
if (ed) {
|
|
for (seq= ed->seqbasep->first; seq; seq= seq->next) {
|
|
if ( ((seq->type == SEQ_RAM_SOUND) || (seq->type == SEQ_HD_SOUND)) &&
|
|
(seq->startdisp <= G.scene->r.cfra+2) &&
|
|
(seq->enddisp>G.scene->r.cfra) &&
|
|
(seq->ipo) )
|
|
{
|
|
do_seq_ipo(seq, G.scene->r.cfra);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* --------------------- Assorted ----------------------------- */
|
|
|
|
/* clear delta-transforms on all Objects which use the given IPO block */
|
|
void clear_delta_obipo(Ipo *ipo)
|
|
{
|
|
Object *ob;
|
|
|
|
/* only search if there's an IPO */
|
|
if (ipo == NULL)
|
|
return;
|
|
|
|
/* search through all objects in database */
|
|
for (ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
/* can only update if not a library */
|
|
if (ob->id.lib == NULL) {
|
|
if (ob->ipo == ipo) {
|
|
memset(&ob->dloc, 0, 12);
|
|
memset(&ob->drot, 0, 12);
|
|
memset(&ob->dsize, 0, 12);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ***************************** IPO - DataAPI ********************************* */
|
|
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!! FIXME - BAD CRUFT WARNING !!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
/* These functions here should be replaced eventually by the Data API, as this is
|
|
* inflexible duplication...
|
|
*/
|
|
|
|
/* --------------------- Get Pointer API ----------------------------- */
|
|
|
|
/* get pointer to pose-channel's channel, but set appropriate flags first */
|
|
// TODO: most channels (except euler rots, which are float-degr) are floats, so do we need type arg?
|
|
void *get_pchan_ipo_poin (bPoseChannel *pchan, int adrcode)
|
|
{
|
|
void *poin= NULL;
|
|
|
|
switch (adrcode) {
|
|
case AC_QUAT_W:
|
|
poin= &(pchan->quat[0]);
|
|
pchan->flag |= POSE_ROT;
|
|
break;
|
|
case AC_QUAT_X:
|
|
poin= &(pchan->quat[1]);
|
|
pchan->flag |= POSE_ROT;
|
|
break;
|
|
case AC_QUAT_Y:
|
|
poin= &(pchan->quat[2]);
|
|
pchan->flag |= POSE_ROT;
|
|
break;
|
|
case AC_QUAT_Z:
|
|
poin= &(pchan->quat[3]);
|
|
pchan->flag |= POSE_ROT;
|
|
break;
|
|
|
|
case AC_EUL_X:
|
|
poin= &(pchan->eul[0]);
|
|
pchan->flag |= POSE_ROT;
|
|
//type= IPO_FLOAT_DEGR;
|
|
break;
|
|
case AC_EUL_Y:
|
|
poin= &(pchan->eul[1]);
|
|
pchan->flag |= POSE_ROT;
|
|
//type= IPO_FLOAT_DEGR;
|
|
break;
|
|
case AC_EUL_Z:
|
|
poin= &(pchan->eul[2]);
|
|
pchan->flag |= POSE_ROT;
|
|
//type= IPO_FLOAT_DEGR;
|
|
break;
|
|
|
|
case AC_LOC_X:
|
|
poin= &(pchan->loc[0]);
|
|
pchan->flag |= POSE_LOC;
|
|
break;
|
|
case AC_LOC_Y:
|
|
poin= &(pchan->loc[1]);
|
|
pchan->flag |= POSE_LOC;
|
|
break;
|
|
case AC_LOC_Z:
|
|
poin= &(pchan->loc[2]);
|
|
pchan->flag |= POSE_LOC;
|
|
break;
|
|
|
|
case AC_SIZE_X:
|
|
poin= &(pchan->size[0]);
|
|
pchan->flag |= POSE_SIZE;
|
|
break;
|
|
case AC_SIZE_Y:
|
|
poin= &(pchan->size[1]);
|
|
pchan->flag |= POSE_SIZE;
|
|
break;
|
|
case AC_SIZE_Z:
|
|
poin= &(pchan->size[2]);
|
|
pchan->flag |= POSE_SIZE;
|
|
break;
|
|
}
|
|
|
|
/* return pointer */
|
|
return poin;
|
|
}
|
|
|
|
/* get texture channel */
|
|
static void *give_tex_poin (Tex *tex, int adrcode, int *type )
|
|
{
|
|
void *poin= NULL;
|
|
|
|
switch (adrcode) {
|
|
case TE_NSIZE:
|
|
poin= &(tex->noisesize); break;
|
|
case TE_TURB:
|
|
poin= &(tex->turbul); break;
|
|
case TE_NDEPTH:
|
|
poin= &(tex->noisedepth); *type= IPO_SHORT; break;
|
|
case TE_NTYPE:
|
|
poin= &(tex->noisetype); *type= IPO_SHORT; break;
|
|
case TE_VNW1:
|
|
poin= &(tex->vn_w1); break;
|
|
case TE_VNW2:
|
|
poin= &(tex->vn_w2); break;
|
|
case TE_VNW3:
|
|
poin= &(tex->vn_w3); break;
|
|
case TE_VNW4:
|
|
poin= &(tex->vn_w4); break;
|
|
case TE_VNMEXP:
|
|
poin= &(tex->vn_mexp); break;
|
|
case TE_ISCA:
|
|
poin= &(tex->ns_outscale); break;
|
|
case TE_DISTA:
|
|
poin= &(tex->dist_amount); break;
|
|
case TE_VN_COLT:
|
|
poin= &(tex->vn_coltype); *type= IPO_SHORT; break;
|
|
case TE_VN_DISTM:
|
|
poin= &(tex->vn_distm); *type= IPO_SHORT; break;
|
|
case TE_MG_TYP:
|
|
poin= &(tex->stype); *type= IPO_SHORT; break;
|
|
case TE_MGH:
|
|
poin= &(tex->mg_H); break;
|
|
case TE_MG_LAC:
|
|
poin= &(tex->mg_lacunarity); break;
|
|
case TE_MG_OCT:
|
|
poin= &(tex->mg_octaves); break;
|
|
case TE_MG_OFF:
|
|
poin= &(tex->mg_offset); break;
|
|
case TE_MG_GAIN:
|
|
poin= &(tex->mg_gain); break;
|
|
case TE_N_BAS1:
|
|
poin= &(tex->noisebasis); *type= IPO_SHORT; break;
|
|
case TE_N_BAS2:
|
|
poin= &(tex->noisebasis2); *type= IPO_SHORT; break;
|
|
case TE_COL_R:
|
|
poin= &(tex->rfac); break;
|
|
case TE_COL_G:
|
|
poin= &(tex->gfac); break;
|
|
case TE_COL_B:
|
|
poin= &(tex->bfac); break;
|
|
case TE_BRIGHT:
|
|
poin= &(tex->bright); break;
|
|
case TE_CONTRA:
|
|
poin= &(tex->contrast); break;
|
|
}
|
|
|
|
/* return pointer */
|
|
return poin;
|
|
}
|
|
|
|
/* get texture-slot/mapping channel */
|
|
void *give_mtex_poin (MTex *mtex, int adrcode)
|
|
{
|
|
void *poin= NULL;
|
|
|
|
switch (adrcode) {
|
|
case MAP_OFS_X:
|
|
poin= &(mtex->ofs[0]); break;
|
|
case MAP_OFS_Y:
|
|
poin= &(mtex->ofs[1]); break;
|
|
case MAP_OFS_Z:
|
|
poin= &(mtex->ofs[2]); break;
|
|
case MAP_SIZE_X:
|
|
poin= &(mtex->size[0]); break;
|
|
case MAP_SIZE_Y:
|
|
poin= &(mtex->size[1]); break;
|
|
case MAP_SIZE_Z:
|
|
poin= &(mtex->size[2]); break;
|
|
case MAP_R:
|
|
poin= &(mtex->r); break;
|
|
case MAP_G:
|
|
poin= &(mtex->g); break;
|
|
case MAP_B:
|
|
poin= &(mtex->b); break;
|
|
case MAP_DVAR:
|
|
poin= &(mtex->def_var); break;
|
|
case MAP_COLF:
|
|
poin= &(mtex->colfac); break;
|
|
case MAP_NORF:
|
|
poin= &(mtex->norfac); break;
|
|
case MAP_VARF:
|
|
poin= &(mtex->varfac); break;
|
|
case MAP_DISP:
|
|
poin= &(mtex->dispfac); break;
|
|
}
|
|
|
|
/* return pointer */
|
|
return poin;
|
|
}
|
|
|
|
/* GS reads the memory pointed at in a specific ordering. There are,
|
|
* however two definitions for it. I have jotted them down here, both,
|
|
* but I think the first one is actually used. The thing is that
|
|
* big-endian systems might read this the wrong way round. OTOH, we
|
|
* constructed the IDs that are read out with this macro explicitly as
|
|
* well. I expect we'll sort it out soon... */
|
|
|
|
/* from blendef: */
|
|
#define GS(a) (*((short *)(a)))
|
|
|
|
/* from misc_util: flip the bytes from x */
|
|
/* #define GS(x) (((unsigned char *)(x))[0] << 8 | ((unsigned char *)(x))[1]) */
|
|
|
|
|
|
/* general function to get pointer to source/destination data */
|
|
void *get_ipo_poin (ID *id, IpoCurve *icu, int *type)
|
|
{
|
|
void *poin= NULL;
|
|
MTex *mtex= NULL;
|
|
|
|
/* most channels will have float data, but those with other types will override this */
|
|
*type= IPO_FLOAT;
|
|
|
|
/* data is divided into 'blocktypes' based on ID-codes */
|
|
switch (GS(id->name)) {
|
|
case ID_OB: /* object channels ----------------------------- */
|
|
{
|
|
Object *ob= (Object *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case OB_LOC_X:
|
|
poin= &(ob->loc[0]); break;
|
|
case OB_LOC_Y:
|
|
poin= &(ob->loc[1]); break;
|
|
case OB_LOC_Z:
|
|
poin= &(ob->loc[2]); break;
|
|
case OB_DLOC_X:
|
|
poin= &(ob->dloc[0]); break;
|
|
case OB_DLOC_Y:
|
|
poin= &(ob->dloc[1]); break;
|
|
case OB_DLOC_Z:
|
|
poin= &(ob->dloc[2]); break;
|
|
|
|
case OB_ROT_X:
|
|
poin= &(ob->rot[0]); *type= IPO_FLOAT_DEGR; break;
|
|
case OB_ROT_Y:
|
|
poin= &(ob->rot[1]); *type= IPO_FLOAT_DEGR; break;
|
|
case OB_ROT_Z:
|
|
poin= &(ob->rot[2]); *type= IPO_FLOAT_DEGR; break;
|
|
case OB_DROT_X:
|
|
poin= &(ob->drot[0]); *type= IPO_FLOAT_DEGR; break;
|
|
case OB_DROT_Y:
|
|
poin= &(ob->drot[1]); *type= IPO_FLOAT_DEGR; break;
|
|
case OB_DROT_Z:
|
|
poin= &(ob->drot[2]); *type= IPO_FLOAT_DEGR; break;
|
|
|
|
case OB_SIZE_X:
|
|
poin= &(ob->size[0]); break;
|
|
case OB_SIZE_Y:
|
|
poin= &(ob->size[1]); break;
|
|
case OB_SIZE_Z:
|
|
poin= &(ob->size[2]); break;
|
|
case OB_DSIZE_X:
|
|
poin= &(ob->dsize[0]); break;
|
|
case OB_DSIZE_Y:
|
|
poin= &(ob->dsize[1]); break;
|
|
case OB_DSIZE_Z:
|
|
poin= &(ob->dsize[2]); break;
|
|
|
|
case OB_LAY:
|
|
poin= &(ob->lay); *type= IPO_INT_BIT; break;
|
|
|
|
case OB_COL_R:
|
|
poin= &(ob->col[0]); break;
|
|
case OB_COL_G:
|
|
poin= &(ob->col[1]); break;
|
|
case OB_COL_B:
|
|
poin= &(ob->col[2]); break;
|
|
case OB_COL_A:
|
|
poin= &(ob->col[3]); break;
|
|
|
|
case OB_PD_FSTR:
|
|
if (ob->pd) poin= &(ob->pd->f_strength);
|
|
break;
|
|
case OB_PD_FFALL:
|
|
if (ob->pd) poin= &(ob->pd->f_power);
|
|
break;
|
|
case OB_PD_SDAMP:
|
|
if (ob->pd) poin= &(ob->pd->pdef_damp);
|
|
break;
|
|
case OB_PD_RDAMP:
|
|
if (ob->pd) poin= &(ob->pd->pdef_rdamp);
|
|
break;
|
|
case OB_PD_PERM:
|
|
if (ob->pd) poin= &(ob->pd->pdef_perm);
|
|
break;
|
|
case OB_PD_FMAXD:
|
|
if (ob->pd) poin= &(ob->pd->maxdist);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_MA: /* material channels ----------------------------- */
|
|
{
|
|
Material *ma= (Material *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case MA_COL_R:
|
|
poin= &(ma->r); break;
|
|
case MA_COL_G:
|
|
poin= &(ma->g); break;
|
|
case MA_COL_B:
|
|
poin= &(ma->b); break;
|
|
case MA_SPEC_R:
|
|
poin= &(ma->specr); break;
|
|
case MA_SPEC_G:
|
|
poin= &(ma->specg); break;
|
|
case MA_SPEC_B:
|
|
poin= &(ma->specb); break;
|
|
case MA_MIR_R:
|
|
poin= &(ma->mirr); break;
|
|
case MA_MIR_G:
|
|
poin= &(ma->mirg); break;
|
|
case MA_MIR_B:
|
|
poin= &(ma->mirb); break;
|
|
case MA_REF:
|
|
poin= &(ma->ref); break;
|
|
case MA_ALPHA:
|
|
poin= &(ma->alpha); break;
|
|
case MA_EMIT:
|
|
poin= &(ma->emit); break;
|
|
case MA_AMB:
|
|
poin= &(ma->amb); break;
|
|
case MA_SPEC:
|
|
poin= &(ma->spec); break;
|
|
case MA_HARD:
|
|
poin= &(ma->har); *type= IPO_SHORT; break;
|
|
case MA_SPTR:
|
|
poin= &(ma->spectra); break;
|
|
case MA_IOR:
|
|
poin= &(ma->ang); break;
|
|
case MA_MODE:
|
|
poin= &(ma->mode); *type= IPO_INT_BIT; break; // evil... dumping bitflags directly to user!
|
|
case MA_HASIZE:
|
|
poin= &(ma->hasize); break;
|
|
case MA_TRANSLU:
|
|
poin= &(ma->translucency); break;
|
|
case MA_RAYM:
|
|
poin= &(ma->ray_mirror); break;
|
|
case MA_FRESMIR:
|
|
poin= &(ma->fresnel_mir); break;
|
|
case MA_FRESMIRI:
|
|
poin= &(ma->fresnel_mir_i); break;
|
|
case MA_FRESTRA:
|
|
poin= &(ma->fresnel_tra); break;
|
|
case MA_FRESTRAI:
|
|
poin= &(ma->fresnel_tra_i); break;
|
|
case MA_ADD:
|
|
poin= &(ma->add); break;
|
|
}
|
|
|
|
if (poin == NULL) {
|
|
if (icu->adrcode & MA_MAP1) mtex= ma->mtex[0];
|
|
else if (icu->adrcode & MA_MAP2) mtex= ma->mtex[1];
|
|
else if (icu->adrcode & MA_MAP3) mtex= ma->mtex[2];
|
|
else if (icu->adrcode & MA_MAP4) mtex= ma->mtex[3];
|
|
else if (icu->adrcode & MA_MAP5) mtex= ma->mtex[4];
|
|
else if (icu->adrcode & MA_MAP6) mtex= ma->mtex[5];
|
|
else if (icu->adrcode & MA_MAP7) mtex= ma->mtex[6];
|
|
else if (icu->adrcode & MA_MAP8) mtex= ma->mtex[7];
|
|
else if (icu->adrcode & MA_MAP9) mtex= ma->mtex[8];
|
|
else if (icu->adrcode & MA_MAP10) mtex= ma->mtex[9];
|
|
else if (icu->adrcode & MA_MAP12) mtex= ma->mtex[11];
|
|
else if (icu->adrcode & MA_MAP11) mtex= ma->mtex[10];
|
|
else if (icu->adrcode & MA_MAP13) mtex= ma->mtex[12];
|
|
else if (icu->adrcode & MA_MAP14) mtex= ma->mtex[13];
|
|
else if (icu->adrcode & MA_MAP15) mtex= ma->mtex[14];
|
|
else if (icu->adrcode & MA_MAP16) mtex= ma->mtex[15];
|
|
else if (icu->adrcode & MA_MAP17) mtex= ma->mtex[16];
|
|
else if (icu->adrcode & MA_MAP18) mtex= ma->mtex[17];
|
|
|
|
if (mtex)
|
|
poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1)));
|
|
}
|
|
}
|
|
break;
|
|
case ID_TE: /* texture channels ----------------------------- */
|
|
{
|
|
Tex *tex= (Tex *)id;
|
|
|
|
if (tex)
|
|
poin= give_tex_poin(tex, icu->adrcode, type);
|
|
}
|
|
break;
|
|
case ID_SEQ: /* sequence channels ----------------------------- */
|
|
{
|
|
Sequence *seq= (Sequence *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case SEQ_FAC1:
|
|
poin= &(seq->facf0); break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_CU: /* curve channels ----------------------------- */
|
|
{
|
|
poin= &(icu->curval);
|
|
}
|
|
break;
|
|
case ID_KE: /* shapekey channels ----------------------------- */
|
|
{
|
|
Key *key= (Key *)id;
|
|
KeyBlock *kb;
|
|
|
|
for(kb= key->block.first; kb; kb= kb->next) {
|
|
if (kb->adrcode == icu->adrcode)
|
|
break;
|
|
}
|
|
|
|
if (kb)
|
|
poin= &(kb->curval);
|
|
}
|
|
break;
|
|
case ID_WO: /* world channels ----------------------------- */
|
|
{
|
|
World *wo= (World *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case WO_HOR_R:
|
|
poin= &(wo->horr); break;
|
|
case WO_HOR_G:
|
|
poin= &(wo->horg); break;
|
|
case WO_HOR_B:
|
|
poin= &(wo->horb); break;
|
|
case WO_ZEN_R:
|
|
poin= &(wo->zenr); break;
|
|
case WO_ZEN_G:
|
|
poin= &(wo->zeng); break;
|
|
case WO_ZEN_B:
|
|
poin= &(wo->zenb); break;
|
|
|
|
case WO_EXPOS:
|
|
poin= &(wo->exposure); break;
|
|
|
|
case WO_MISI:
|
|
poin= &(wo->misi); break;
|
|
case WO_MISTDI:
|
|
poin= &(wo->mistdist); break;
|
|
case WO_MISTSTA:
|
|
poin= &(wo->miststa); break;
|
|
case WO_MISTHI:
|
|
poin= &(wo->misthi); break;
|
|
|
|
case WO_STAR_R:
|
|
poin= &(wo->starr); break;
|
|
case WO_STAR_G:
|
|
poin= &(wo->starg); break;
|
|
case WO_STAR_B:
|
|
poin= &(wo->starb); break;
|
|
|
|
case WO_STARDIST:
|
|
poin= &(wo->stardist); break;
|
|
case WO_STARSIZE:
|
|
poin= &(wo->starsize); break;
|
|
}
|
|
|
|
if (poin == NULL) {
|
|
if (icu->adrcode & MA_MAP1) mtex= wo->mtex[0];
|
|
else if (icu->adrcode & MA_MAP2) mtex= wo->mtex[1];
|
|
else if (icu->adrcode & MA_MAP3) mtex= wo->mtex[2];
|
|
else if (icu->adrcode & MA_MAP4) mtex= wo->mtex[3];
|
|
else if (icu->adrcode & MA_MAP5) mtex= wo->mtex[4];
|
|
else if (icu->adrcode & MA_MAP6) mtex= wo->mtex[5];
|
|
else if (icu->adrcode & MA_MAP7) mtex= wo->mtex[6];
|
|
else if (icu->adrcode & MA_MAP8) mtex= wo->mtex[7];
|
|
else if (icu->adrcode & MA_MAP9) mtex= wo->mtex[8];
|
|
else if (icu->adrcode & MA_MAP10) mtex= wo->mtex[9];
|
|
else if (icu->adrcode & MA_MAP11) mtex= wo->mtex[10];
|
|
else if (icu->adrcode & MA_MAP12) mtex= wo->mtex[11];
|
|
else if (icu->adrcode & MA_MAP13) mtex= wo->mtex[12];
|
|
else if (icu->adrcode & MA_MAP14) mtex= wo->mtex[13];
|
|
else if (icu->adrcode & MA_MAP15) mtex= wo->mtex[14];
|
|
else if (icu->adrcode & MA_MAP16) mtex= wo->mtex[15];
|
|
else if (icu->adrcode & MA_MAP17) mtex= wo->mtex[16];
|
|
else if (icu->adrcode & MA_MAP18) mtex= wo->mtex[17];
|
|
|
|
if (mtex)
|
|
poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1)));
|
|
}
|
|
}
|
|
break;
|
|
case ID_LA: /* lamp channels ----------------------------- */
|
|
{
|
|
Lamp *la= (Lamp *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case LA_ENERGY:
|
|
poin= &(la->energy); break;
|
|
case LA_COL_R:
|
|
poin= &(la->r); break;
|
|
case LA_COL_G:
|
|
poin= &(la->g); break;
|
|
case LA_COL_B:
|
|
poin= &(la->b); break;
|
|
case LA_DIST:
|
|
poin= &(la->dist); break;
|
|
case LA_SPOTSI:
|
|
poin= &(la->spotsize); break;
|
|
case LA_SPOTBL:
|
|
poin= &(la->spotblend); break;
|
|
case LA_QUAD1:
|
|
poin= &(la->att1); break;
|
|
case LA_QUAD2:
|
|
poin= &(la->att2); break;
|
|
case LA_HALOINT:
|
|
poin= &(la->haint); break;
|
|
}
|
|
|
|
if (poin == NULL) {
|
|
if (icu->adrcode & MA_MAP1) mtex= la->mtex[0];
|
|
else if (icu->adrcode & MA_MAP2) mtex= la->mtex[1];
|
|
else if (icu->adrcode & MA_MAP3) mtex= la->mtex[2];
|
|
else if (icu->adrcode & MA_MAP4) mtex= la->mtex[3];
|
|
else if (icu->adrcode & MA_MAP5) mtex= la->mtex[4];
|
|
else if (icu->adrcode & MA_MAP6) mtex= la->mtex[5];
|
|
else if (icu->adrcode & MA_MAP7) mtex= la->mtex[6];
|
|
else if (icu->adrcode & MA_MAP8) mtex= la->mtex[7];
|
|
else if (icu->adrcode & MA_MAP9) mtex= la->mtex[8];
|
|
else if (icu->adrcode & MA_MAP10) mtex= la->mtex[9];
|
|
else if (icu->adrcode & MA_MAP11) mtex= la->mtex[10];
|
|
else if (icu->adrcode & MA_MAP12) mtex= la->mtex[11];
|
|
else if (icu->adrcode & MA_MAP13) mtex= la->mtex[12];
|
|
else if (icu->adrcode & MA_MAP14) mtex= la->mtex[13];
|
|
else if (icu->adrcode & MA_MAP15) mtex= la->mtex[14];
|
|
else if (icu->adrcode & MA_MAP16) mtex= la->mtex[15];
|
|
else if (icu->adrcode & MA_MAP17) mtex= la->mtex[16];
|
|
else if (icu->adrcode & MA_MAP18) mtex= la->mtex[17];
|
|
|
|
if (mtex)
|
|
poin= give_mtex_poin(mtex, (icu->adrcode & (MA_MAP1-1)));
|
|
}
|
|
}
|
|
break;
|
|
case ID_CA: /* camera channels ----------------------------- */
|
|
{
|
|
Camera *ca= (Camera *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case CAM_LENS:
|
|
if (ca->type == CAM_ORTHO)
|
|
poin= &(ca->ortho_scale);
|
|
else
|
|
poin= &(ca->lens);
|
|
break;
|
|
case CAM_STA:
|
|
poin= &(ca->clipsta); break;
|
|
case CAM_END:
|
|
poin= &(ca->clipend); break;
|
|
|
|
case CAM_YF_APERT:
|
|
poin= &(ca->YF_aperture); break;
|
|
case CAM_YF_FDIST:
|
|
poin= &(ca->YF_dofdist); break;
|
|
|
|
case CAM_SHIFT_X:
|
|
poin= &(ca->shiftx); break;
|
|
case CAM_SHIFT_Y:
|
|
poin= &(ca->shifty); break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_SO: /* sound channels ----------------------------- */
|
|
{
|
|
bSound *snd= (bSound *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case SND_VOLUME:
|
|
poin= &(snd->volume); break;
|
|
case SND_PITCH:
|
|
poin= &(snd->pitch); break;
|
|
case SND_PANNING:
|
|
poin= &(snd->panning); break;
|
|
case SND_ATTEN:
|
|
poin= &(snd->attenuation); break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_PA: /* particle channels ----------------------------- */
|
|
{
|
|
ParticleSettings *part= (ParticleSettings *)id;
|
|
|
|
switch (icu->adrcode) {
|
|
case PART_EMIT_FREQ:
|
|
case PART_EMIT_LIFE:
|
|
case PART_EMIT_VEL:
|
|
case PART_EMIT_AVE:
|
|
case PART_EMIT_SIZE:
|
|
poin= NULL;
|
|
break;
|
|
|
|
case PART_CLUMP:
|
|
poin= &(part->clumpfac); break;
|
|
case PART_AVE:
|
|
poin= &(part->avefac); break;
|
|
case PART_SIZE:
|
|
poin= &(part->size); break;
|
|
case PART_DRAG:
|
|
poin= &(part->dragfac); break;
|
|
case PART_BROWN:
|
|
poin= &(part->brownfac); break;
|
|
case PART_DAMP:
|
|
poin= &(part->dampfac); break;
|
|
case PART_LENGTH:
|
|
poin= &(part->length); break;
|
|
case PART_GRAV_X:
|
|
poin= &(part->acc[0]); break;
|
|
case PART_GRAV_Y:
|
|
poin= &(part->acc[1]); break;
|
|
case PART_GRAV_Z:
|
|
poin= &(part->acc[2]); break;
|
|
case PART_KINK_AMP:
|
|
poin= &(part->kink_amp); break;
|
|
case PART_KINK_FREQ:
|
|
poin= &(part->kink_freq); break;
|
|
case PART_KINK_SHAPE:
|
|
poin= &(part->kink_shape); break;
|
|
case PART_BB_TILT:
|
|
poin= &(part->bb_tilt); break;
|
|
|
|
case PART_PD_FSTR:
|
|
if (part->pd) poin= &(part->pd->f_strength);
|
|
break;
|
|
case PART_PD_FFALL:
|
|
if (part->pd) poin= &(part->pd->f_power);
|
|
break;
|
|
case PART_PD_FMAXD:
|
|
if (part->pd) poin= &(part->pd->maxdist);
|
|
break;
|
|
case PART_PD2_FSTR:
|
|
if (part->pd2) poin= &(part->pd2->f_strength);
|
|
break;
|
|
case PART_PD2_FFALL:
|
|
if (part->pd2) poin= &(part->pd2->f_power);
|
|
break;
|
|
case PART_PD2_FMAXD:
|
|
if (part->pd2) poin= &(part->pd2->maxdist);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* return pointer */
|
|
return poin;
|
|
}
|
|
|
|
/* --------------------- IPO-Curve Limits ----------------------------- */
|
|
|
|
/* set limits for IPO-curve
|
|
* Note: must be synced with UI and PyAPI
|
|
*/
|
|
void set_icu_vars (IpoCurve *icu)
|
|
{
|
|
/* defaults. 0.0 for y-extents makes these ignored */
|
|
icu->ymin= icu->ymax= 0.0;
|
|
icu->ipo= IPO_BEZ;
|
|
|
|
switch (icu->blocktype) {
|
|
case ID_OB: /* object channels ----------------------------- */
|
|
{
|
|
if (icu->adrcode == OB_LAY) {
|
|
icu->ipo= IPO_CONST;
|
|
icu->vartype= IPO_BITS;
|
|
}
|
|
}
|
|
break;
|
|
case ID_MA: /* material channels ----------------------------- */
|
|
{
|
|
if (icu->adrcode < MA_MAP1) {
|
|
switch (icu->adrcode) {
|
|
case MA_HASIZE:
|
|
icu->ymax= 10000.0; break;
|
|
case MA_HARD:
|
|
icu->ymax= 511.0; break;
|
|
case MA_SPEC:
|
|
icu->ymax= 2.0; break;
|
|
case MA_MODE:
|
|
icu->ipo= IPO_CONST;
|
|
icu->vartype= IPO_BITS; break;
|
|
case MA_RAYM:
|
|
icu->ymax= 1.0; break;
|
|
case MA_TRANSLU:
|
|
icu->ymax= 1.0; break;
|
|
case MA_IOR:
|
|
icu->ymin= 1.0;
|
|
icu->ymax= 3.0; break;
|
|
case MA_FRESMIR:
|
|
icu->ymax= 5.0; break;
|
|
case MA_FRESMIRI:
|
|
icu->ymin= 1.0;
|
|
icu->ymax= 5.0; break;
|
|
case MA_FRESTRA:
|
|
icu->ymax= 5.0; break;
|
|
case MA_FRESTRAI:
|
|
icu->ymin= 1.0;
|
|
icu->ymax= 5.0; break;
|
|
case MA_ADD:
|
|
icu->ymax= 1.0; break;
|
|
case MA_EMIT:
|
|
icu->ymax= 2.0; break;
|
|
default:
|
|
icu->ymax= 1.0; break;
|
|
}
|
|
}
|
|
else {
|
|
switch (icu->adrcode & (MA_MAP1-1)) {
|
|
case MAP_OFS_X:
|
|
case MAP_OFS_Y:
|
|
case MAP_OFS_Z:
|
|
case MAP_SIZE_X:
|
|
case MAP_SIZE_Y:
|
|
case MAP_SIZE_Z:
|
|
icu->ymax= 1000.0;
|
|
icu->ymin= -1000.0;
|
|
break;
|
|
case MAP_R:
|
|
case MAP_G:
|
|
case MAP_B:
|
|
case MAP_DVAR:
|
|
case MAP_COLF:
|
|
case MAP_VARF:
|
|
case MAP_DISP:
|
|
icu->ymax= 1.0;
|
|
break;
|
|
case MAP_NORF:
|
|
icu->ymax= 25.0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ID_TE: /* texture channels ----------------------------- */
|
|
{
|
|
switch (icu->adrcode & (MA_MAP1-1)) {
|
|
case TE_NSIZE:
|
|
icu->ymin= 0.0001f;
|
|
icu->ymax= 2.0f;
|
|
break;
|
|
case TE_NDEPTH:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 6.0f;
|
|
break;
|
|
case TE_NTYPE:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
case TE_TURB:
|
|
icu->ymax= 200.0f;
|
|
break;
|
|
case TE_VNW1:
|
|
case TE_VNW2:
|
|
case TE_VNW3:
|
|
case TE_VNW4:
|
|
icu->ymax= 2.0f;
|
|
icu->ymin= -2.0f;
|
|
break;
|
|
case TE_VNMEXP:
|
|
icu->ymax= 10.0f;
|
|
icu->ymin= 0.01f;
|
|
break;
|
|
case TE_VN_DISTM:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 6.0f;
|
|
break;
|
|
case TE_VN_COLT:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 3.0f;
|
|
break;
|
|
case TE_ISCA:
|
|
icu->ymax= 10.0f;
|
|
icu->ymin= 0.01f;
|
|
break;
|
|
case TE_DISTA:
|
|
icu->ymax= 10.0f;
|
|
break;
|
|
case TE_MG_TYP:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 6.0f;
|
|
break;
|
|
case TE_MGH:
|
|
icu->ymin= 0.0001f;
|
|
icu->ymax= 2.0f;
|
|
break;
|
|
case TE_MG_LAC:
|
|
case TE_MG_OFF:
|
|
case TE_MG_GAIN:
|
|
icu->ymax= 6.0f; break;
|
|
case TE_MG_OCT:
|
|
icu->ymax= 8.0f; break;
|
|
case TE_N_BAS1:
|
|
case TE_N_BAS2:
|
|
icu->vartype= IPO_SHORT;
|
|
icu->ipo= IPO_CONST;
|
|
icu->ymax= 8.0f;
|
|
break;
|
|
case TE_COL_R:
|
|
icu->ymax= 0.0f; break;
|
|
case TE_COL_G:
|
|
icu->ymax= 2.0f; break;
|
|
case TE_COL_B:
|
|
icu->ymax= 2.0f; break;
|
|
case TE_BRIGHT:
|
|
icu->ymax= 2.0f; break;
|
|
case TE_CONTRA:
|
|
icu->ymax= 5.0f; break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_SEQ: /* sequence channels ----------------------------- */
|
|
{
|
|
icu->ymax= 1.0f;
|
|
}
|
|
break;
|
|
case ID_CU: /* curve channels ----------------------------- */
|
|
{
|
|
icu->ymax= 1.0f;
|
|
}
|
|
break;
|
|
case ID_WO: /* world channels ----------------------------- */
|
|
{
|
|
if (icu->adrcode < MA_MAP1) {
|
|
switch (icu->adrcode) {
|
|
case WO_EXPOS:
|
|
icu->ymax= 5.0f; break;
|
|
|
|
case WO_MISTDI:
|
|
case WO_MISTSTA:
|
|
case WO_MISTHI:
|
|
case WO_STARDIST:
|
|
case WO_STARSIZE:
|
|
break;
|
|
|
|
default:
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (icu->adrcode & (MA_MAP1-1)) {
|
|
case MAP_OFS_X:
|
|
case MAP_OFS_Y:
|
|
case MAP_OFS_Z:
|
|
case MAP_SIZE_X:
|
|
case MAP_SIZE_Y:
|
|
case MAP_SIZE_Z:
|
|
icu->ymax= 100.0f;
|
|
icu->ymin= -100.0f;
|
|
break;
|
|
case MAP_R:
|
|
case MAP_G:
|
|
case MAP_B:
|
|
case MAP_DVAR:
|
|
case MAP_COLF:
|
|
case MAP_NORF:
|
|
case MAP_VARF:
|
|
case MAP_DISP:
|
|
icu->ymax= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ID_LA: /* lamp channels ----------------------------- */
|
|
{
|
|
if (icu->adrcode < MA_MAP1) {
|
|
switch (icu->adrcode) {
|
|
case LA_ENERGY:
|
|
case LA_DIST:
|
|
break;
|
|
|
|
case LA_COL_R:
|
|
case LA_COL_G:
|
|
case LA_COL_B:
|
|
case LA_SPOTBL:
|
|
case LA_QUAD1:
|
|
case LA_QUAD2:
|
|
icu->ymax= 1.0f; break;
|
|
|
|
case LA_SPOTSI:
|
|
icu->ymax= 180.0f; break;
|
|
|
|
case LA_HALOINT:
|
|
icu->ymax= 5.0f; break;
|
|
}
|
|
}
|
|
else {
|
|
switch (icu->adrcode & (MA_MAP1-1)) {
|
|
case MAP_OFS_X:
|
|
case MAP_OFS_Y:
|
|
case MAP_OFS_Z:
|
|
case MAP_SIZE_X:
|
|
case MAP_SIZE_Y:
|
|
case MAP_SIZE_Z:
|
|
icu->ymax= 100.0f;
|
|
icu->ymin= -100.0f;
|
|
break;
|
|
case MAP_R:
|
|
case MAP_G:
|
|
case MAP_B:
|
|
case MAP_DVAR:
|
|
case MAP_COLF:
|
|
case MAP_NORF:
|
|
case MAP_VARF:
|
|
case MAP_DISP:
|
|
icu->ymax= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ID_CA: /* camera channels ----------------------------- */
|
|
{
|
|
switch (icu->adrcode) {
|
|
case CAM_LENS:
|
|
icu->ymin= 1.0f;
|
|
icu->ymax= 1000.0f;
|
|
break;
|
|
case CAM_STA:
|
|
icu->ymin= 0.001f;
|
|
break;
|
|
case CAM_END:
|
|
icu->ymin= 0.1f;
|
|
break;
|
|
|
|
case CAM_YF_APERT:
|
|
icu->ymin = 0.0f;
|
|
icu->ymax = 2.0f;
|
|
break;
|
|
case CAM_YF_FDIST:
|
|
icu->ymin = 0.0f;
|
|
icu->ymax = 5000.0f;
|
|
break;
|
|
|
|
case CAM_SHIFT_X:
|
|
case CAM_SHIFT_Y:
|
|
icu->ymin= -2.0f;
|
|
icu->ymax= 2.0f;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_SO: /* sound channels ----------------------------- */
|
|
{
|
|
switch (icu->adrcode) {
|
|
case SND_VOLUME:
|
|
icu->ymin= 0.0f;
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
case SND_PITCH:
|
|
icu->ymin= -12.0f;
|
|
icu->ymin= 12.0f;
|
|
break;
|
|
case SND_PANNING:
|
|
icu->ymin= 0.0f;
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
case SND_ATTEN:
|
|
icu->ymin= 0.0f;
|
|
icu->ymin= 1.0f;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_PA: /* particle channels ----------------------------- */
|
|
{
|
|
switch (icu->adrcode) {
|
|
case PART_EMIT_LIFE:
|
|
case PART_SIZE:
|
|
case PART_KINK_FREQ:
|
|
case PART_EMIT_VEL:
|
|
case PART_EMIT_AVE:
|
|
case PART_EMIT_SIZE:
|
|
icu->ymin= 0.0f;
|
|
break;
|
|
case PART_CLUMP:
|
|
icu->ymin= -1.0f;
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
case PART_DRAG:
|
|
case PART_DAMP:
|
|
case PART_LENGTH:
|
|
icu->ymin= 0.0f;
|
|
icu->ymax= 1.0f;
|
|
break;
|
|
case PART_KINK_SHAPE:
|
|
icu->ymin= -0.999f;
|
|
icu->ymax= 0.999f;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ID_CO: /* constraint channels ----------------------------- */
|
|
{
|
|
icu->ymin= 0.0f;
|
|
icu->ymax= 1.0f;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* by default, slider limits will be icu->ymin and icu->ymax */
|
|
icu->slide_min= icu->ymin;
|
|
icu->slide_max= icu->ymax;
|
|
}
|
|
|
|
/* --------------------- Pointer I/O API ----------------------------- */
|
|
|
|
/* write the given value directly into the given pointer */
|
|
void write_ipo_poin (void *poin, int type, float val)
|
|
{
|
|
/* Note: we only support a limited number of types, with the value
|
|
* to set needing to be cast to the appropriate type first
|
|
* -> (float to integer conversions could be slow)
|
|
*/
|
|
switch(type) {
|
|
case IPO_FLOAT:
|
|
*((float *)poin)= val;
|
|
break;
|
|
|
|
case IPO_FLOAT_DEGR: /* special hack for rotation so that it fits on same axis as other transforms */
|
|
*((float *)poin)= (float)(val * M_PI_2 / 9.0);
|
|
break;
|
|
|
|
case IPO_INT:
|
|
case IPO_INT_BIT: // fixme... directly revealing bitflag combinations is evil!
|
|
case IPO_LONG:
|
|
*((int *)poin)= (int)val;
|
|
break;
|
|
|
|
case IPO_SHORT:
|
|
case IPO_SHORT_BIT: // fixme... directly revealing bitflag combinations is evil!
|
|
*((short *)poin)= (short)val;
|
|
break;
|
|
|
|
case IPO_CHAR:
|
|
case IPO_CHAR_BIT: // fixme... directly revealing bitflag combinations is evil!
|
|
*((char *)poin)= (char)val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* read the value from the pointer that was obtained */
|
|
float read_ipo_poin (void *poin, int type)
|
|
{
|
|
float val = 0.0;
|
|
|
|
/* Note: we only support a limited number of types, with the value
|
|
* to set needing to be cast to the appropriate type first
|
|
* -> (int to float conversions may loose accuracy in rare cases)
|
|
*/
|
|
switch (type) {
|
|
case IPO_FLOAT:
|
|
val= *((float *)poin);
|
|
break;
|
|
|
|
case IPO_FLOAT_DEGR: /* special hack for rotation so that it fits on same axis as other transforms */
|
|
val= *( (float *)poin);
|
|
val = (float)(val / (M_PI_2/9.0));
|
|
break;
|
|
|
|
case IPO_INT:
|
|
case IPO_INT_BIT: // fixme... directly revealing bitflag combinations is evil!
|
|
case IPO_LONG:
|
|
val= (float)( *((int *)poin) );
|
|
break;
|
|
|
|
case IPO_SHORT:
|
|
case IPO_SHORT_BIT: // fixme... directly revealing bitflag combinations is evil!
|
|
val= *((short *)poin);
|
|
break;
|
|
|
|
case IPO_CHAR:
|
|
case IPO_CHAR_BIT: // fixme... directly revealing bitflag combinations is evil
|
|
val= *((char *)poin);
|
|
break;
|
|
}
|
|
|
|
/* return value */
|
|
return val;
|
|
}
|
|
|
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!! FIXME - BAD CRUFT WARNING !!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
|
|
/* ***************************** IPO <--> GameEngine Interface ********************************* */
|
|
|
|
/* channels is max 32 items, allocated by calling function */
|
|
short IPO_GetChannels (Ipo *ipo, IPO_Channel *channels)
|
|
{
|
|
IpoCurve *icu;
|
|
int total = 0;
|
|
|
|
/* don't do anything with no IPO-block */
|
|
if (ipo == NULL)
|
|
return 0;
|
|
|
|
/* store the IPO-curve's adrcode in the relevant channel slot */
|
|
for (icu=ipo->curve.first; (icu) && (total < 31); icu=icu->next, total++)
|
|
channels[total]= icu->adrcode;
|
|
|
|
/* return the number of channels stored */
|
|
return total;
|
|
}
|
|
|
|
/* Get the float value for channel 'channel' at time 'ctime' */
|
|
float IPO_GetFloatValue (Ipo *ipo, IPO_Channel channel, float ctime)
|
|
{
|
|
/* don't evaluate if no IPO to use */
|
|
if (ipo == NULL)
|
|
return 0;
|
|
|
|
/* only calculate the specified channel */
|
|
calc_ipo_spec(ipo, channel, &ctime);
|
|
|
|
/* unapply rotation hack, as gameengine doesn't use it */
|
|
if ((OB_ROT_X <= channel) && (channel <= OB_DROT_Z))
|
|
ctime *= (float)(M_PI_2/9.0);
|
|
|
|
/* return the value of this channel */
|
|
return ctime;
|
|
}
|