/* * $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. * * This is a new part of Blender. * * Contributor(s): Joseph Gilbert * * ***** END GPL LICENSE BLOCK ***** */ #include "NLA.h" /*This must come first*/ #include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "BKE_action.h" #include "BKE_nla.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_library.h" #include "BLI_blenlib.h" #include "Object.h" #include "Ipo.h" #include "gen_utils.h" #include "gen_library.h" #include "blendef.h" #include "MEM_guardedalloc.h" #define ACTSTRIP_STRIDEAXIS_X 0 #define ACTSTRIP_STRIDEAXIS_Y 1 #define ACTSTRIP_STRIDEAXIS_Z 2 /*****************************************************************************/ /* Python API function prototypes for the NLA module. */ /*****************************************************************************/ static PyObject *M_NLA_NewAction( PyObject * self, PyObject * args ); static PyObject *M_NLA_CopyAction( PyObject * self, PyObject * args ); static PyObject *M_NLA_GetActions( PyObject * self ); /*****************************************************************************/ /* The following string definitions are used for documentation strings. */ /* In Python these will be written to the console when doing a */ /* Blender.Armature.NLA.__doc__ */ /*****************************************************************************/ char M_NLA_doc[] = "The Blender NLA module -This module provides control over Armature keyframing in Blender."; char M_NLA_NewAction_doc[] = "(name) - Create new action for linking to an object."; char M_NLA_CopyAction_doc[] = "(name) - Copy action and return copy."; char M_NLA_GetActions_doc[] = "(name) - Returns a dictionary of actions."; /*****************************************************************************/ /* Python method structure definition for Blender.Armature.NLA module: */ /*****************************************************************************/ struct PyMethodDef M_NLA_methods[] = { {"NewAction", ( PyCFunction ) M_NLA_NewAction, METH_VARARGS, M_NLA_NewAction_doc}, {"CopyAction", ( PyCFunction ) M_NLA_CopyAction, METH_VARARGS, M_NLA_CopyAction_doc}, {"GetActions", ( PyCFunction ) M_NLA_GetActions, METH_NOARGS, M_NLA_GetActions_doc}, {NULL, NULL, 0, NULL} }; /*****************************************************************************/ /* Python BPy_Action methods declarations: */ /*****************************************************************************/ static PyObject *Action_setActive( BPy_Action * self, PyObject * args ); static PyObject *Action_getFrameNumbers(BPy_Action *self); static PyObject *Action_getChannelIpo( BPy_Action * self, PyObject * value ); static PyObject *Action_getChannelNames( BPy_Action * self ); static PyObject *Action_renameChannel( BPy_Action * self, PyObject * args ); static PyObject *Action_verifyChannel( BPy_Action * self, PyObject * value ); static PyObject *Action_removeChannel( BPy_Action * self, PyObject * value ); static PyObject *Action_getAllChannelIpos( BPy_Action * self ); /*****************************************************************************/ /* Python BPy_Action methods table: */ /*****************************************************************************/ static PyMethodDef BPy_Action_methods[] = { /* name, method, flags, doc */ {"getName", ( PyCFunction ) GenericLib_getName, METH_NOARGS, "() - return Action name"}, {"setName", ( PyCFunction ) GenericLib_setName_with_method, METH_VARARGS, "(str) - rename Action"}, {"setActive", ( PyCFunction ) Action_setActive, METH_VARARGS, "(str) -set this action as the active action for an object"}, {"getFrameNumbers", (PyCFunction) Action_getFrameNumbers, METH_NOARGS, "() - get the frame numbers at which keys have been inserted"}, {"getChannelIpo", ( PyCFunction ) Action_getChannelIpo, METH_O, "(str) -get the Ipo from a named action channel in this action"}, {"getChannelNames", ( PyCFunction ) Action_getChannelNames, METH_NOARGS, "() -get the channel names for this action"}, {"renameChannel", ( PyCFunction ) Action_renameChannel, METH_VARARGS, "(from, to) -rename the channel from string to string"}, {"verifyChannel", ( PyCFunction ) Action_verifyChannel, METH_O, "(str) -verify the channel in this action"}, {"removeChannel", ( PyCFunction ) Action_removeChannel, METH_O, "(str) -remove the channel from the action"}, {"getAllChannelIpos", ( PyCFunction ) Action_getAllChannelIpos, METH_NOARGS, "() - Return a dict of (name:ipo)-keys containing each channel in the object's action"}, {NULL, NULL, 0, NULL} }; /*****************************************************************************/ /* Python TypeAction callback function prototypes: */ /*****************************************************************************/ static int Action_compare( BPy_Action * a, BPy_Action * b ); static PyObject *Action_repr( BPy_Action * bone ); /*-------------------------------------------------------------------------*/ static PyObject *M_NLA_NewAction( PyObject * self_unused, PyObject * args ) { char *name_str = "DefaultAction"; BPy_Action *py_action = NULL; /* for Action Data object wrapper in Python */ bAction *bl_action = NULL; /* for actual Action Data we create in Blender */ if( !PyArg_ParseTuple( args, "|s", &name_str ) ) { EXPP_ReturnPyObjError( PyExc_AttributeError, "expected string or nothing" ); return NULL; } /* Create new action globally */ bl_action = alloc_libblock( &G.main->action, ID_AC, name_str ); bl_action->id.flag |= LIB_FAKEUSER; /* no need to assign a user because alloc_libblock alredy assigns one */ /* now create the wrapper obj in Python */ if( bl_action ) py_action = ( BPy_Action * ) PyObject_NEW( BPy_Action, &Action_Type ); else { EXPP_ReturnPyObjError( PyExc_RuntimeError, "couldn't create Action Data in Blender" ); return NULL; } if( py_action == NULL ) { EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create Action Data object" ); return NULL; } py_action->action = bl_action; /* link Python action wrapper with Blender Action */ Py_INCREF( py_action ); return ( PyObject * ) py_action; } static PyObject *M_NLA_CopyAction( PyObject * self_unused, PyObject * args ) { BPy_Action *py_action = NULL; bAction *copyAction = NULL; if( !PyArg_ParseTuple( args, "O!", &Action_Type, &py_action ) ) { EXPP_ReturnPyObjError( PyExc_AttributeError, "expected python action type" ); return NULL; } copyAction = copy_action( py_action->action ); return Action_CreatePyObject( copyAction ); } static PyObject *M_NLA_GetActions( PyObject * self_unused ) { PyObject *dict = PyDict_New( ); bAction *action = NULL; for( action = G.main->action.first; action; action = action->id.next ) { PyObject *py_action = Action_CreatePyObject( action ); if( py_action ) { /* Insert dict entry using the bone name as key */ if( PyDict_SetItemString ( dict, action->id.name + 2, py_action ) != 0 ) { Py_DECREF( py_action ); Py_DECREF( dict ); return EXPP_ReturnPyObjError ( PyExc_RuntimeError, "NLA_GetActions: couldn't set dict item" ); } Py_DECREF( py_action ); } else { Py_DECREF( dict ); return ( EXPP_ReturnPyObjError( PyExc_RuntimeError, "NLA_GetActions: could not create Action object" ) ); } } return dict; } static PyObject *Action_getFrameNumbers(BPy_Action *self) { bActionChannel *achan = NULL; IpoCurve *icu = NULL; BezTriple *bezt = NULL; int verts; PyObject *py_list = NULL; py_list = PyList_New(0); for(achan = self->action->chanbase.first; achan; achan = achan->next){ if (achan->ipo) { for (icu = achan->ipo->curve.first; icu; icu = icu->next){ bezt= icu->bezt; if(bezt) { verts = icu->totvert; while(verts--) { PyObject *value; value = PyInt_FromLong((int)bezt->vec[1][0]); if ( PySequence_Contains(py_list, value) == 0){ PyList_Append(py_list, value); } Py_DECREF(value); bezt++; } } } } } PyList_Sort(py_list); return EXPP_incr_ret(py_list); } static PyObject *Action_setActive( BPy_Action * self, PyObject * args ) { BPy_Object *object; if( !self->action ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "couldn't get attribute from a NULL action" ); if( !PyArg_ParseTuple( args, "O!", &Object_Type, &object ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "expected python object argument" ); if( object->object->type != OB_ARMATURE ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "object not of type armature" ); /* if object is already attached to an action, decrement user count */ if( object->object->action ) --object->object->action->id.us; /* set the active action to object */ object->object->action = self->action; ++object->object->action->id.us; Py_RETURN_NONE; } static PyObject *Action_getChannelIpo( BPy_Action * self, PyObject * value ) { char *chanName = PyString_AsString(value); bActionChannel *chan; if( !chanName ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "string expected" ); chan = get_action_channel( self->action, chanName ); if( !chan ) return EXPP_ReturnPyObjError( PyExc_ValueError, "no channel with that name" ); if( !chan->ipo ) { Py_RETURN_NONE; } return Ipo_CreatePyObject( chan->ipo ); } static PyObject *Action_getChannelNames( BPy_Action * self ) { PyObject *list = PyList_New( BLI_countlist(&(self->action->chanbase)) ); bActionChannel *chan = NULL; int index=0; for( chan = self->action->chanbase.first; chan; chan = chan->next ) { PyList_SetItem( list, index, PyString_FromString(chan->name) ); index++; } return list; } static PyObject *Action_renameChannel( BPy_Action * self, PyObject * args ) { char *chanFrom, *chanTo; bActionChannel *chan; if( !PyArg_ParseTuple( args, "ss", &chanFrom, &chanTo ) ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "2 strings expected" ); chan = get_action_channel( self->action, chanFrom ); if( !chan ) return EXPP_ReturnPyObjError( PyExc_ValueError, "no channel with that name" ); if (strlen(chanTo) > 31) return EXPP_ReturnPyObjError( PyExc_ValueError, "new name greater then 31 characters long" ); if (get_action_channel( self->action, chanTo )) return EXPP_ReturnPyObjError( PyExc_ValueError, "channel target name alredy exists" ); strcpy(chan->name, chanTo); Py_RETURN_NONE; } /*----------------------------------------------------------------------*/ static PyObject *Action_verifyChannel( BPy_Action * self, PyObject * value ) { char *chanName = PyString_AsString(value); bActionChannel *chan; if( !self->action ) ( EXPP_ReturnPyObjError( PyExc_RuntimeError, "couldn't create channel for a NULL action" ) ); if( !chanName ) return ( EXPP_ReturnPyObjError( PyExc_AttributeError, "expected string argument" ) ); chan = verify_action_channel(self->action, chanName); Py_RETURN_NONE; } static PyObject *Action_removeChannel( BPy_Action * self, PyObject * value ) { char *chanName = PyString_AsString(value); bActionChannel *chan; if( !chanName ) return (EXPP_ReturnPyObjError( PyExc_AttributeError, "string expected" )); chan = get_action_channel( self->action, chanName ); if( chan == NULL ) { EXPP_ReturnPyObjError( PyExc_AttributeError, "no channel with that name..." ); return NULL; } /*release ipo*/ if( chan->ipo ) chan->ipo->id.us--; /*remove channel*/ BLI_freelinkN( &self->action->chanbase, chan ); Py_RETURN_NONE; } static PyObject *Action_getAllChannelIpos( BPy_Action * self ) { PyObject *dict = PyDict_New( ); bActionChannel *chan = NULL; for( chan = self->action->chanbase.first; chan; chan = chan->next ) { PyObject *ipo_attr; if( chan->ipo ) ipo_attr = Ipo_CreatePyObject( chan->ipo ); else { ipo_attr = Py_None; Py_INCREF( ipo_attr ); } if( ipo_attr ) { /* Insert dict entry using the bone name as key*/ if( PyDict_SetItemString( dict, chan->name, ipo_attr ) != 0 ) { Py_DECREF( ipo_attr ); Py_DECREF( dict ); return EXPP_ReturnPyObjError ( PyExc_RuntimeError, "Action_getAllChannelIpos: couldn't set dict item" ); } Py_DECREF( ipo_attr ); } else { Py_DECREF( dict ); return ( EXPP_ReturnPyObjError( PyExc_RuntimeError, "Action_getAllChannelIpos: could not create Ipo object" ) ); } } return dict; } /*----------------------------------------------------------------------*/ static int Action_compare( BPy_Action * a, BPy_Action * b ) { return ( a->action == b->action ) ? 0 : -1; } /*----------------------------------------------------------------------*/ static PyObject *Action_repr( BPy_Action * self ) { if( self->action ) return PyString_FromFormat( "[Action \"%s\"]", self->action->id.name + 2 ); else return PyString_FromString( "NULL" ); } /*----------------------------------------------------------------------*/ PyObject *Action_CreatePyObject( struct bAction * act ) { BPy_Action *blen_action; if(!act) Py_RETURN_NONE; blen_action = ( BPy_Action * ) PyObject_NEW( BPy_Action, &Action_Type ); if( !blen_action) { return ( EXPP_ReturnPyObjError ( PyExc_RuntimeError, "failure to create object!" ) ); } blen_action->action = act; return ( ( PyObject * ) blen_action ); } /*----------------------------------------------------------------------*/ struct bAction *Action_FromPyObject( PyObject * py_obj ) { BPy_Action *blen_obj; blen_obj = ( BPy_Action * ) py_obj; return ( blen_obj->action ); } /*****************************************************************************/ /* Python attributes get/set structure: */ /*****************************************************************************/ static PyGetSetDef BPy_Action_getseters[] = { GENERIC_LIB_GETSETATTR, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /*****************************************************************************/ /* Python TypeAction structure definition: */ /*****************************************************************************/ PyTypeObject Action_Type = { PyObject_HEAD_INIT( NULL ) 0, /* ob_size */ "Blender Action", /* tp_name */ sizeof( BPy_Action ), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ NULL, /* tp_dealloc */ NULL, /* tp_print */ NULL, /* tp_getattr */ NULL, /* tp_setattr */ ( cmpfunc ) Action_compare, /* tp_compare */ ( reprfunc ) Action_repr, /* tp_repr */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ ( hashfunc ) GenericLib_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_Action_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_Action_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; /*****************************************************************************/ /* ActionStrip wrapper */ /*****************************************************************************/ /*****************************************************************************/ /* Python BPy_ActionStrip attributes: */ /*****************************************************************************/ /* * return the action for the action strip */ static PyObject *ActionStrip_getAction( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return Action_CreatePyObject( self->strip->act ); } /* * return the start frame of the action strip */ static PyObject *ActionStrip_getStripStart( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->start ); } /* * set the start frame of the action strip */ static int ActionStrip_setStripStart( BPy_ActionStrip * self, PyObject * value ) { int retval; if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); retval = EXPP_setFloatClamped( value, &self->strip->start, -1000.0, self->strip->end-1 ); if( !retval ) { float max = self->strip->end - self->strip->start; if( self->strip->blendin > max ) self->strip->blendin = max; if( self->strip->blendout > max ) self->strip->blendout = max; } return retval; } /* * return the ending frame of the action strip */ static PyObject *ActionStrip_getStripEnd( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->end ); } /* * set the ending frame of the action strip */ static int ActionStrip_setStripEnd( BPy_ActionStrip * self, PyObject * value ) { int retval; if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); retval = EXPP_setFloatClamped( value, &self->strip->end, self->strip->start+1, MAXFRAMEF ); if( !retval ) { float max = self->strip->end - self->strip->start; if( self->strip->blendin > max ) self->strip->blendin = max; if( self->strip->blendout > max ) self->strip->blendout = max; } return retval; } /* * return the start frame of the action */ static PyObject *ActionStrip_getActionStart( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->actstart ); } /* * set the start frame of the action */ static int ActionStrip_setActionStart( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->actstart, -1000.0, self->strip->actend-1 ); } /* * return the ending frame of the action */ static PyObject *ActionStrip_getActionEnd( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->actend ); } /* * set the ending frame of the action */ static int ActionStrip_setActionEnd( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->actend, self->strip->actstart+1, MAXFRAMEF ); } /* * return the repeat value of the action strip */ static PyObject *ActionStrip_getRepeat( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->repeat ); } /* * set the repeat value of the action strip */ static int ActionStrip_setRepeat( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->repeat, 0.001f, 1000.0f ); } /* * return the blend in of the action strip */ static PyObject *ActionStrip_getBlendIn( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->blendin ); } /* * set the blend in value of the action strip */ static int ActionStrip_setBlendIn( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->blendin, 0.0, self->strip->end - self->strip->start ); } /* * return the blend out of the action strip */ static PyObject *ActionStrip_getBlendOut( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( self->strip->blendout ); } /* * set the blend out value of the action strip */ static int ActionStrip_setBlendOut( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->blendout, 0.0, self->strip->end - self->strip->start ); } /* * return the blend mode of the action strip */ static PyObject *ActionStrip_getBlendMode( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyInt_FromLong( (long)self->strip->mode ) ; } /* * set the blend mode value of the action strip */ static int ActionStrip_setBlendMode( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setIValueRange( value, &self->strip->mode, 0, ACTSTRIPMODE_ADD, 'h' ); } /* * return the flag settings of the action strip */ #define ACTIONSTRIP_MASK (ACTSTRIP_SELECT | ACTSTRIP_USESTRIDE \ | ACTSTRIP_HOLDLASTFRAME | ACTSTRIP_ACTIVE | ACTSTRIP_LOCK_ACTION \ | ACTSTRIP_MUTE) static PyObject *ActionStrip_getFlag( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyInt_FromLong( (long)( self->strip->flag & ACTIONSTRIP_MASK ) ) ; } /* * set the flag settings out value of the action strip */ static int ActionStrip_setFlag( BPy_ActionStrip * self, PyObject * arg ) { PyObject *num = PyNumber_Int( arg ); int value; if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); if( !num ) return EXPP_ReturnIntError( PyExc_TypeError, "expected int argument" ); value = PyInt_AS_LONG( num ); Py_DECREF( num ); if( ( value & ACTIONSTRIP_MASK ) != value ) { char errstr[128]; sprintf ( errstr , "expected int bitmask of 0x%04x", ACTIONSTRIP_MASK ); return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } self->strip->flag = (short)value; return 0; } /* * return the stride axis of the action strip */ static PyObject *ActionStrip_getStrideAxis( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyInt_FromLong( (long)self->strip->stride_axis ) ; } /* * set the stride axis of the action strip */ static int ActionStrip_setStrideAxis( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setIValueRange( value, &self->strip->stride_axis, ACTSTRIP_STRIDEAXIS_X, ACTSTRIP_STRIDEAXIS_Z, 'h' ); } /* * return the stride length of the action strip */ static PyObject *ActionStrip_getStrideLength( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyFloat_FromDouble( (double)self->strip->stridelen ) ; } /* * set the stride length of the action strip */ static int ActionStrip_setStrideLength( BPy_ActionStrip * self, PyObject * value ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); return EXPP_setFloatClamped( value, &self->strip->stridelen, 0.0001f, 1000.0 ); } /* * return the stride bone name */ static PyObject *ActionStrip_getStrideBone( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return PyString_FromString( self->strip->stridechannel ); } static PyObject *ActionStrip_getGroupTarget( BPy_ActionStrip * self ) { if( !self->strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); if (self->strip->object) { return Object_CreatePyObject( self->strip->object ); } else { Py_RETURN_NONE; } } /* * set the stride bone name */ static int ActionStrip_setStrideBone( BPy_ActionStrip * self, PyObject * attr ) { char *name = PyString_AsString( attr ); if( !name ) return EXPP_ReturnIntError( PyExc_TypeError, "expected string arg" ); if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); BLI_strncpy( self->strip->stridechannel, name, 32 ); return 0; } static int ActionStrip_setGroupTarget( BPy_ActionStrip * self, PyObject * args ) { if( !self->strip ) return EXPP_ReturnIntError( PyExc_RuntimeError, "This strip has been removed!" ); if( (PyObject *)args == Py_None ) self->strip->object = NULL; else if( BPy_Object_Check( args ) ) self->strip->object = ((BPy_Object *)args)->object; else return EXPP_ReturnIntError( PyExc_TypeError, "expected an object or None" ); return 0; } /*****************************************************************************/ /* Python BPy_Constraint attributes get/set structure: */ /*****************************************************************************/ static PyGetSetDef BPy_ActionStrip_getseters[] = { {"action", (getter)ActionStrip_getAction, (setter)NULL, "Action associated with the strip", NULL}, {"stripStart", (getter)ActionStrip_getStripStart, (setter)ActionStrip_setStripStart, "Starting frame of the strip", NULL}, {"stripEnd", (getter)ActionStrip_getStripEnd, (setter)ActionStrip_setStripEnd, "Ending frame of the strip", NULL}, {"actionStart", (getter)ActionStrip_getActionStart, (setter)ActionStrip_setActionStart, "Starting frame of the action", NULL}, {"actionEnd", (getter)ActionStrip_getActionEnd, (setter)ActionStrip_setActionEnd, "Ending frame of the action", NULL}, {"repeat", (getter)ActionStrip_getRepeat, (setter)ActionStrip_setRepeat, "The number of times to repeat the action range", NULL}, {"blendIn", (getter)ActionStrip_getBlendIn, (setter)ActionStrip_setBlendIn, "Number of frames of motion blending", NULL}, {"blendOut", (getter)ActionStrip_getBlendOut, (setter)ActionStrip_setBlendOut, "Number of frames of ease-out", NULL}, {"mode", (getter)ActionStrip_getBlendMode, (setter)ActionStrip_setBlendMode, "Setting of blending mode", NULL}, {"flag", (getter)ActionStrip_getFlag, (setter)ActionStrip_setFlag, "Setting of blending flags", NULL}, {"strideAxis", (getter)ActionStrip_getStrideAxis, (setter)ActionStrip_setStrideAxis, "Dominant axis for stride bone", NULL}, {"strideLength", (getter)ActionStrip_getStrideLength, (setter)ActionStrip_setStrideLength, "Distance covered by one complete cycle of the action", NULL}, {"strideBone", (getter)ActionStrip_getStrideBone, (setter)ActionStrip_setStrideBone, "Name of Bone used for stride", NULL}, {"groupTarget", (getter)ActionStrip_getGroupTarget, (setter)ActionStrip_setGroupTarget, "Name of target armature within group", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; /*****************************************************************************/ /* Python BPy_ActionStrip methods: */ /*****************************************************************************/ /* * restore the values of ActionStart and ActionEnd to their defaults */ static PyObject *ActionStrip_resetLimits( BPy_ActionStrip *self ) { bActionStrip *strip = self->strip; if( !strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); calc_action_range( strip->act, &strip->actstart, &strip->actend, 1 ); Py_RETURN_NONE; } /* * reset the strip size */ static PyObject *ActionStrip_resetStripSize( BPy_ActionStrip *self ) { float mapping; bActionStrip *strip = self->strip; if( !strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); mapping = (strip->actend - strip->actstart) / (strip->end - strip->start); strip->end = strip->start + mapping*(strip->end - strip->start); Py_RETURN_NONE; } /* * snap to start and end to nearest frames */ static PyObject *ActionStrip_snapToFrame( BPy_ActionStrip *self ) { bActionStrip *strip = self->strip; if( !strip ) return EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); strip->start= (float)floor(strip->start+0.5); strip->end= (float)floor(strip->end+0.5); Py_RETURN_NONE; } /*****************************************************************************/ /* Python BPy_ActionStrip methods table: */ /*****************************************************************************/ static PyMethodDef BPy_ActionStrip_methods[] = { /* name, method, flags, doc */ {"resetActionLimits", ( PyCFunction ) ActionStrip_resetLimits, METH_NOARGS, "Restores the values of ActionStart and ActionEnd to their defaults"}, {"resetStripSize", ( PyCFunction ) ActionStrip_resetStripSize, METH_NOARGS, "Resets the Action Strip size to its creation values"}, {"snapToFrame", ( PyCFunction ) ActionStrip_snapToFrame, METH_NOARGS, "Snaps the ends of the action strip to the nearest whole numbered frame"}, {NULL, NULL, 0, NULL} }; /*****************************************************************************/ /* Python ActionStrip_Type structure definition: */ /*****************************************************************************/ PyTypeObject ActionStrip_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender.ActionStrip", /* char *tp_name; */ sizeof( BPy_ActionStrip ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ ( reprfunc ) NULL, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ NULL, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ NULL, /* getiterfunc tp_iter; */ NULL, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_ActionStrip_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ BPy_ActionStrip_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; static PyObject *M_ActionStrip_FlagsDict( void ) { PyObject *S = PyConstant_New( ); if( S ) { BPy_constant *d = ( BPy_constant * ) S; PyConstant_Insert( d, "SELECT", PyInt_FromLong( ACTSTRIP_SELECT ) ); PyConstant_Insert( d, "STRIDE_PATH", PyInt_FromLong( ACTSTRIP_USESTRIDE ) ); PyConstant_Insert( d, "HOLD", PyInt_FromLong( ACTSTRIP_HOLDLASTFRAME ) ); PyConstant_Insert( d, "ACTIVE", PyInt_FromLong( ACTSTRIP_ACTIVE ) ); PyConstant_Insert( d, "LOCK_ACTION", PyInt_FromLong( ACTSTRIP_LOCK_ACTION ) ); PyConstant_Insert( d, "MUTE", PyInt_FromLong( ACTSTRIP_MUTE ) ); } return S; } static PyObject *M_ActionStrip_AxisDict( void ) { PyObject *S = PyConstant_New( ); if( S ) { BPy_constant *d = ( BPy_constant * ) S; PyConstant_Insert( d, "STRIDEAXIS_X", PyInt_FromLong( ACTSTRIP_STRIDEAXIS_X ) ); PyConstant_Insert( d, "STRIDEAXIS_Y", PyInt_FromLong( ACTSTRIP_STRIDEAXIS_Y ) ); PyConstant_Insert( d, "STRIDEAXIS_Z", PyInt_FromLong( ACTSTRIP_STRIDEAXIS_Z ) ); } return S; } static PyObject *M_ActionStrip_ModeDict( void ) { PyObject *S = PyConstant_New( ); if( S ) { BPy_constant *d = ( BPy_constant * ) S; PyConstant_Insert( d, "MODE_ADD", PyInt_FromLong( ACTSTRIPMODE_ADD ) ); } return S; } PyObject *ActionStrip_CreatePyObject( struct bActionStrip *strip ) { BPy_ActionStrip *pyobj; pyobj = ( BPy_ActionStrip * ) PyObject_NEW( BPy_ActionStrip, &ActionStrip_Type ); if( !pyobj ) return EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create BPy_ActionStrip object" ); pyobj->strip = strip; return ( PyObject * ) pyobj; } /*****************************************************************************/ /* ActionStrip Sequence wrapper */ /*****************************************************************************/ /* * Initialize the iterator */ static PyObject *ActionStrips_getIter( BPy_ActionStrips * self ) { self->iter = (bActionStrip *)self->ob->nlastrips.first; return EXPP_incr_ret ( (PyObject *) self ); } /* * Get the next action strip */ static PyObject *ActionStrips_nextIter( BPy_ActionStrips * self ) { bActionStrip *strip = self->iter; if( strip ) { self->iter = strip->next; return ActionStrip_CreatePyObject( strip ); } return EXPP_ReturnPyObjError( PyExc_StopIteration, "iterator at end" ); } /* return the number of action strips */ static int ActionStrips_length( BPy_ActionStrips * self ) { return BLI_countlist( &self->ob->nlastrips ); } /* return an action strip */ static PyObject *ActionStrips_item( BPy_ActionStrips * self, int i ) { bActionStrip *strip = NULL; /* if index is negative, start counting from the end of the list */ if( i < 0 ) i += ActionStrips_length( self ); /* skip through the list until we get the strip or end of list */ strip = self->ob->nlastrips.first; while( i && strip ) { --i; strip = strip->next; } if( strip ) return ActionStrip_CreatePyObject( strip ); else return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range" ); } /*****************************************************************************/ /* Python BPy_ActionStrips sequence table: */ /*****************************************************************************/ static PySequenceMethods ActionStrips_as_sequence = { ( inquiry ) ActionStrips_length, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) ActionStrips_item, /* sq_item */ ( intintargfunc ) 0, /* sq_slice */ ( intobjargproc ) 0, /* sq_ass_item */ ( intintobjargproc ) 0, /* sq_ass_slice */ ( objobjproc ) 0, /* sq_contains */ ( binaryfunc ) 0, /* sq_inplace_concat */ ( intargfunc ) 0, /* sq_inplace_repeat */ }; /*****************************************************************************/ /* Python BPy_ActionStrip methods: */ /*****************************************************************************/ /* * helper function to check for a valid action strip argument */ static bActionStrip *locate_strip( BPy_ActionStrips *self, PyObject *args, BPy_ActionStrip **stripobj ) { bActionStrip *strip = NULL; BPy_ActionStrip *pyobj; /* check that argument is a constraint */ if( !PyArg_ParseTuple( args, "O!", &ActionStrip_Type, &pyobj ) ) { EXPP_ReturnPyObjError( PyExc_TypeError, "expected an action strip as an argument" ); return NULL; } if( !pyobj->strip ) { EXPP_ReturnPyObjError( PyExc_RuntimeError, "This strip has been removed!" ); return NULL; } /* if caller needs the object, return it */ if( stripobj ) *stripobj = pyobj; /* find the action strip in the NLA */ for( strip = self->ob->nlastrips.first; strip; strip = strip->next ) if( strip == pyobj->strip ) return strip; /* return exception if we can't find the strip */ EXPP_ReturnPyObjError( PyExc_AttributeError, "action strip does not belong to this object" ); return NULL; } /* * remove an action strip from the NLA */ static PyObject *ActionStrips_remove( BPy_ActionStrips *self, PyObject * args ) { BPy_ActionStrip *pyobj; bActionStrip *strip = locate_strip( self, args, &pyobj ); /* return exception if we can't find the strip */ if( !strip ) return (PyObject *)NULL; /* do the actual removal */ free_actionstrip(strip); BLI_remlink(&self->ob->nlastrips, strip); MEM_freeN(strip); pyobj->strip = NULL; Py_RETURN_NONE; } /* * move an action strip up in the strip list */ static PyObject *ActionStrips_moveUp( BPy_ActionStrips *self, PyObject * args ) { bActionStrip *strip = locate_strip( self, args, NULL ); /* return exception if we can't find the strip */ if( !strip ) return (PyObject *)NULL; /* if strip is not already the first, move it up */ if( strip != self->ob->nlastrips.first ) { BLI_remlink(&self->ob->nlastrips, strip); BLI_insertlink(&self->ob->nlastrips, strip->prev->prev, strip); } Py_RETURN_NONE; } /* * move an action strip down in the strip list */ static PyObject *ActionStrips_moveDown( BPy_ActionStrips *self, PyObject * args ) { bActionStrip *strip = locate_strip( self, args, NULL ); /* return exception if we can't find the strip */ if( !strip ) return (PyObject *)NULL; /* if strip is not already the last, move it down */ if( strip != self->ob->nlastrips.last ) { BLI_remlink(&self->ob->nlastrips, strip); BLI_insertlink(&self->ob->nlastrips, strip->next, strip); } Py_RETURN_NONE; } static PyObject *ActionStrips_append( BPy_ActionStrips *self, PyObject * args ) { BPy_Action *pyobj; Object *ob; bActionStrip *strip; bAction *act; /* check that argument is an action */ if( !PyArg_ParseTuple( args, "O!", &Action_Type, &pyobj ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "expected an action as an argument" ); ob = self->ob; act = pyobj->action; /* Initialize the new action block */ strip = MEM_callocN( sizeof(bActionStrip), "bActionStrip" ); strip->act = act; calc_action_range( strip->act, &strip->actstart, &strip->actend, 1 ); strip->start = (float)G.scene->r.cfra; strip->end = strip->start + ( strip->actend - strip->actstart ); /* simple prevention of zero strips */ if( strip->start > strip->end-2 ) strip->end = strip->start+100; strip->flag = ACTSTRIP_LOCK_ACTION; find_stridechannel(ob, strip); strip->repeat = 1.0; act->id.us++; BLI_addtail(&ob->nlastrips, strip); Py_RETURN_NONE; } /*****************************************************************************/ /* Python BPy_ActionStrips methods table: */ /*****************************************************************************/ static PyMethodDef BPy_ActionStrips_methods[] = { /* name, method, flags, doc */ {"append", ( PyCFunction ) ActionStrips_append, METH_VARARGS, "(action) - append a new actionstrip using existing action"}, {"remove", ( PyCFunction ) ActionStrips_remove, METH_VARARGS, "(strip) - remove an existing strip from this actionstrips"}, {"moveUp", ( PyCFunction ) ActionStrips_moveUp, METH_VARARGS, "(strip) - move an existing strip up in the actionstrips"}, {"moveDown", ( PyCFunction ) ActionStrips_moveDown, METH_VARARGS, "(strip) - move an existing strip down in the actionstrips"}, {NULL, NULL, 0, NULL} }; /*****************************************************************************/ /* Python ActionStrips_Type structure definition: */ /*****************************************************************************/ PyTypeObject ActionStrips_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "." */ "Blender.ActionStrips", /* char *tp_name; */ sizeof( BPy_ActionStrips ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ NULL, /* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ NULL, /* cmpfunc tp_compare; */ ( reprfunc ) NULL, /* reprfunc tp_repr; */ /* Method suites for standard classes */ NULL, /* PyNumberMethods *tp_as_number; */ &ActionStrips_as_sequence, /* PySequenceMethods *tp_as_sequence; */ NULL, /* PyMappingMethods *tp_as_mapping; */ /* More standard operations (here for binary compatibility) */ NULL, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ NULL, /* getattrofunc tp_getattro; */ NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ Py_TPFLAGS_DEFAULT, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ NULL, /* richcmpfunc tp_richcompare; */ /*** weak reference enabler ***/ 0, /* long tp_weaklistoffset; */ /*** Added in release 2.2 ***/ /* Iterators */ ( getiterfunc )ActionStrips_getIter, /* getiterfunc tp_iter; */ ( iternextfunc )ActionStrips_nextIter, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ BPy_ActionStrips_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ NULL, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ NULL, /* descrsetfunc tp_descr_set; */ 0, /* long tp_dictoffset; */ NULL, /* initproc tp_init; */ NULL, /* allocfunc tp_alloc; */ NULL, /* newfunc tp_new; */ /* Low-level free-memory routine */ NULL, /* freefunc tp_free; */ /* For PyObject_IS_GC */ NULL, /* inquiry tp_is_gc; */ NULL, /* PyObject *tp_bases; */ /* method resolution order */ NULL, /* PyObject *tp_mro; */ NULL, /* PyObject *tp_cache; */ NULL, /* PyObject *tp_subclasses; */ NULL, /* PyObject *tp_weaklist; */ NULL }; PyObject *ActionStrips_CreatePyObject( Object *ob ) { BPy_ActionStrips *pyseq; pyseq = ( BPy_ActionStrips * ) PyObject_NEW( BPy_ActionStrips, &ActionStrips_Type ); if( !pyseq ) return EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create BPy_ActionStrips object" ); pyseq->ob = ob; return ( PyObject * ) pyseq; } /*****************************************************************************/ /* Function: NLA_Init */ /*****************************************************************************/ PyObject *NLA_Init( void ) { PyObject *FlagsDict = M_ActionStrip_FlagsDict( ); PyObject *AxisDict = M_ActionStrip_AxisDict( ); PyObject *ModeDict = M_ActionStrip_ModeDict( ); PyObject *submodule; if( PyType_Ready( &Action_Type ) < 0 || PyType_Ready( &ActionStrip_Type ) < 0 || PyType_Ready( &ActionStrips_Type ) < 0 ) return NULL; submodule = Py_InitModule3( "Blender.Armature.NLA", M_NLA_methods, M_NLA_doc ); if( FlagsDict ) PyModule_AddObject( submodule, "Flags", FlagsDict ); if( AxisDict ) PyModule_AddObject( submodule, "StrideAxes", AxisDict ); if( ModeDict ) PyModule_AddObject( submodule, "Modes", ModeDict ); return submodule; }