/* * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * This is a new part of Blender. * * Contributor(s): Jordi Rovira i Bonet, Joseph Gilbert * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include "Armature.h" #include "Bone.h" #include "NLA.h" #include #include #include #include #include #include #include #include #include #include "constant.h" #include "gen_utils.h" #include "modules.h" #include "Types.h" //--------------------------- Python API function prototypes for the Armature module----------- static PyObject *M_Armature_New (PyObject * self, PyObject * args); static PyObject *M_Armature_Get (PyObject * self, PyObject * args); //--------------------------- Python API Doc Strings for the Armature module---------------------- static char M_Armature_doc[] = "The Blender Armature module\n\n\ This module provides control over **Armature Data** objects in Blender.\n"; static char M_Armature_New_doc[] = "(name) - return a new Armature datablock of \n\ optional name 'name'."; static char M_Armature_Get_doc[] = "(name) - return the armature with the name 'name', \ returns None if not found.\n If 'name' is not specified, it returns a list of all armatures in the\ncurrent scene."; static char M_Armature_get_doc[] = "(name) - DEPRECATED. Use 'Get' instead. \ return the armature with the name 'name', returns None if not found.\n If 'name' is not specified, \ it returns a list of all armatures in the\ncurrent scene."; //----------------Python method structure definition for Blender.Armature module--------------- struct PyMethodDef M_Armature_methods[] = { {"New", (PyCFunction) M_Armature_New, METH_VARARGS, M_Armature_New_doc}, {"Get", M_Armature_Get, METH_VARARGS, M_Armature_Get_doc}, {"get", M_Armature_Get, METH_VARARGS, M_Armature_get_doc}, {NULL, NULL, 0, NULL} }; //----------------Python BPy_Armature methods declarations-------------------------------------------- static PyObject *Armature_getName (BPy_Armature * self); static PyObject *Armature_getBones (BPy_Armature * self); static PyObject *Armature_addBone(BPy_Armature *self, PyObject *args); static PyObject *Armature_setName (BPy_Armature * self, PyObject * args); static PyObject *Armature_drawAxes (BPy_Armature * self, PyObject * args); static PyObject *Armature_drawNames (BPy_Armature * self, PyObject * args); //----------------Python BPy_Armature methods table--------------------------------------------------- static PyMethodDef BPy_Armature_methods[] = { {"getName", (PyCFunction) Armature_getName, METH_NOARGS, "() - return Armature name"}, {"getBones", (PyCFunction) Armature_getBones, METH_NOARGS, "() - return Armature root bones"}, {"setName", (PyCFunction) Armature_setName, METH_VARARGS, "(str) - rename Armature"}, {"addBone", (PyCFunction)Armature_addBone, METH_VARARGS, "(bone)-add bone"}, {"drawAxes", (PyCFunction)Armature_drawAxes, METH_VARARGS, "will draw the axis of each bone in armature"}, {"drawNames", (PyCFunction)Armature_drawNames, METH_VARARGS, "will draw the names of each bone in armature"}, {NULL, NULL, 0, NULL} }; //----------------Python TypeArmature callback function prototypes------------------------------ static void Armature_dealloc (BPy_Armature * armature); static PyObject *Armature_getAttr (BPy_Armature * armature, char *name); static int Armature_setAttr (BPy_Armature * armature, char *name, PyObject * v); static int Armature_compare (BPy_Armature * a1, BPy_Armature * a2); static PyObject *Armature_repr (BPy_Armature * armature); static int doesBoneName_exist(char *name, bArmature* arm); //---------------- Python TypeArmature structure definition:------------------------------------------- PyTypeObject Armature_Type = { PyObject_HEAD_INIT (NULL) 0, /* ob_size */ "Blender Armature", /* tp_name */ sizeof (BPy_Armature), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor) Armature_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc) Armature_getAttr, /* tp_getattr */ (setattrfunc) Armature_setAttr, /* tp_setattr */ (cmpfunc) Armature_compare, /* tp_compare */ (reprfunc) Armature_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_as_hash */ 0, 0, 0, 0, 0, 0, 0, /* tp_doc */ 0, 0, 0, 0, 0, 0, BPy_Armature_methods, /* tp_methods */ 0, /* tp_members */ }; //-------------------Blender Armature Module Init------------------------------------------------------ PyObject * Armature_Init (void) { PyObject *submodule; PyObject *dict; Armature_Type.ob_type = &PyType_Type; submodule = Py_InitModule3 ("Blender.Armature", M_Armature_methods, M_Armature_doc); /* Add the Bone submodule to this module */ dict = PyModule_GetDict (submodule); PyDict_SetItemString (dict, "Bone", Bone_Init ()); PyDict_SetItemString (dict, "NLA", NLA_Init ()); return (submodule); } //--------------------------Blender Armature Module internal callbacks------------------------------ //------------------append_childrenToList------------------------------------------------------------------- static void append_childrenToList(Bone *parent, PyObject *listbones) { Bone *child = NULL; //append children for(child = parent->childbase.first; child; child = child->next){ PyList_Append (listbones, Bone_CreatePyObject (child)); if(child->childbase.first){ //has children? append_childrenToList(child, listbones); } } } //------------------unique_BoneName---------------------------------------------------------------------- static void unique_BoneName(char *name, bArmature* arm) { char tempname[64]; int number; char *dot; if (doesBoneName_exist(name, arm)){ /* Strip off the suffix */ dot=strchr(name, '.'); if (dot) *dot=0; for (number = 1; number <=999; number++){ sprintf (tempname, "%s.%03d", name, number); if (!doesBoneName_exist(tempname, arm)){ strcpy (name, tempname); return; } } } } //------------------doesBoneName_exist---------------------------------------------------------------------- static int doesBoneName_exist(char *name, bArmature* arm) { Bone *parent = NULL; Bone *child = NULL; for (parent = arm->bonebase.first; parent; parent = parent->next){ if (!strcmp (name, parent->name)) return 1; for (child = parent->childbase.first; child; child = child->next){ if (!strcmp (name, child->name)) return 1; } } return 0; } //------------------testChildInChildbase---------------------------------------------------------------------- static int testChildInChildbase(Bone *bone, Bone *test) { Bone *child; for(child = bone->childbase.first; child; child = child->next){ if(child == test){ return 1; }else{ if(child->childbase.first != NULL){ if(testChildInChildbase(child, test)){ return 1; } } } } return 0; } //------------------testBoneInArmature---------------------------------------------------------------------- static int testBoneInArmature(bArmature *arm, Bone *test) { Bone *root; for(root = arm->bonebase.first; root; root = root->next){ if(root == test){ return 1; }else{ if(root->childbase.first != NULL){ if(testChildInChildbase(root, test)){ return 1; } } } } return 0; } //-----------------testChildNameInChildbase------------------------------------------------------------------ static Bone * testChildNameInChildbase(Bone *bone, char *name) { Bone *child; Bone *test; for(child = bone->childbase.first; child; child = child->next){ if(BLI_streq(child->name, name)){ return child; }else{ if(child->childbase.first != NULL){ test = testChildNameInChildbase(child, name); if(test) return test; } } } return NULL; } //----------------testBoneNameInArmature------------------------------------------------------------------ static Bone * testBoneNameInArmature(bArmature *arm, char *name) { Bone *bone; Bone *test; for(bone = arm->bonebase.first; bone; bone = bone->next){ if(BLI_streq(bone->name, name)){ return bone; //found it }else{ if(bone->childbase.first != NULL){ test = testChildNameInChildbase(bone, name); if(test) return test; } } } return NULL; } //-------------------BPy_Armature internal methods-------------------------------------------------- //------------------dealloc-------------------------------------------------------------------------------------- static void Armature_dealloc (BPy_Armature * self) { PyObject_DEL (self); } //-----------------getattr----------------------------------------------------------------------------------------- static PyObject * Armature_getAttr (BPy_Armature * self, char *name) { PyObject *attr = Py_None; if (strcmp (name, "name") == 0) attr = Armature_getName (self); if (strcmp (name, "bones") == 0) attr = Armature_getBones (self); else if (strcmp (name, "__members__") == 0) { /* 2 entries */ attr = Py_BuildValue ("[s,s]", "name", "bones"); } if (!attr) return (EXPP_ReturnPyObjError (PyExc_MemoryError, "couldn't create PyObject")); if (attr != Py_None) return attr; /* member attribute found, return it */ /* not an attribute, search the methods table */ return Py_FindMethod (BPy_Armature_methods, (PyObject *) self, name); } //-----------------setattr----------------------------------------------------------------------------------------- static int Armature_setAttr (BPy_Armature * self, char *name, PyObject * value) { PyObject *valtuple; PyObject *error = NULL; valtuple = Py_BuildValue ("(O)", value); /*the set* functions expect a tuple */ if (!valtuple) return EXPP_ReturnIntError (PyExc_MemoryError, "ArmatureSetAttr: couldn't create tuple"); if (strcmp (name, "name") == 0) error = Armature_setName (self, valtuple); else { /* Error */ Py_DECREF (valtuple); /* ... member with the given name was found */ return (EXPP_ReturnIntError (PyExc_KeyError, "attribute not found")); } Py_DECREF (valtuple); if (error != Py_None) return -1; Py_DECREF (Py_None); /* was incref'ed by the called Armature_set* function */ return 0; /* normal exit */ } //-----------------repr----------------------------------------------------------------------------------------- static PyObject * Armature_repr (BPy_Armature * self) { return PyString_FromFormat ("[Armature \"%s\"]", self->armature->id.name + 2); } //-----------------compare----------------------------------------------------------------------------------------- static int Armature_compare (BPy_Armature * a, BPy_Armature * b) { bArmature *pa = a->armature, *pb = b->armature; return (pa == pb) ? 0 : -1; } //-----------------Armature_CreatePyObject------------------------------------------------------------------- PyObject * Armature_CreatePyObject (struct bArmature * obj) { BPy_Armature *blen_armature; blen_armature = (BPy_Armature *) PyObject_NEW (BPy_Armature, &Armature_Type); if (blen_armature == NULL) { return (NULL); } blen_armature->armature = obj; return ((PyObject *) blen_armature); } //-----------------Armature_CheckPyObject ------------------------------------------------------------------- int Armature_CheckPyObject (PyObject * py_obj) { return (py_obj->ob_type == &Armature_Type); } //-----------------Armature_FromPyObject ------------------------------------------------------------------- struct bArmature * Armature_FromPyObject (PyObject * py_obj) { BPy_Armature *blen_obj; blen_obj = (BPy_Armature *) py_obj; return (blen_obj->armature); } //-----------------Blender Module function prototypes------------------------------------------------- //----------------Blender.Armature.New()------------------------------------------------------------------- static PyObject * M_Armature_New (PyObject * self, PyObject * args) { char *name_str = "ArmatureData"; BPy_Armature *py_armature; /* for Armature Data object wrapper in Python */ bArmature *bl_armature; /* for actual Armature Data we create in Blender */ char buf[21]; if (!PyArg_ParseTuple(args, "|s", &name_str)) return (EXPP_ReturnPyObjError (PyExc_AttributeError, "expected string or empty argument")); bl_armature = add_armature(); /* first create in Blender */ if (bl_armature){ /* return user count to zero because add_armature() inc'd it */ bl_armature->id.us = 0; /* now create the wrapper obj in Python */ py_armature = (BPy_Armature *)PyObject_NEW(BPy_Armature, &Armature_Type); }else{ return (EXPP_ReturnPyObjError (PyExc_RuntimeError, "couldn't create Armature Data in Blender")); } if (py_armature == NULL) return (EXPP_ReturnPyObjError (PyExc_MemoryError, "couldn't create ArmaturePyObject")); /* link Python armature wrapper with Blender Armature: */ py_armature->armature = bl_armature; if (strcmp(name_str, "ArmatureData") == 0) return (PyObject *)py_armature; else { /* user gave us a name for the armature, use it */ PyOS_snprintf(buf, sizeof(buf), "%s", name_str); rename_id(&bl_armature->id, buf); } return (PyObject *)py_armature; } //----------------Blender.Armature.Get()------------------------------------------------------------------- static PyObject * M_Armature_Get (PyObject * self, PyObject * args) { char *name = NULL; bArmature *armature_iter; BPy_Armature *wanted_armature; if (!PyArg_ParseTuple (args, "|s", &name)) return (EXPP_ReturnPyObjError (PyExc_TypeError, "expected string argument (or nothing)")); armature_iter = G.main->armature.first; /* Use the name to search for the armature requested. */ if (name){ /* (name) - Search armature by name */ wanted_armature = NULL; while ((armature_iter) && (wanted_armature == NULL)){ if (strcmp (name, armature_iter->id.name + 2) == 0) { wanted_armature = (BPy_Armature *) PyObject_NEW (BPy_Armature, &Armature_Type); if (wanted_armature) wanted_armature->armature = armature_iter; } armature_iter = armature_iter->id.next; } if (wanted_armature == NULL){ /* Requested Armature doesn't exist */ char error_msg[64]; PyOS_snprintf (error_msg, sizeof (error_msg), "Armature \"%s\" not found", name); return (EXPP_ReturnPyObjError (PyExc_NameError, error_msg)); } return (PyObject *) wanted_armature; }else{ /* Return a list of with armatures in the scene */ int index = 0; PyObject *armlist, *pyobj; armlist = PyList_New (BLI_countlist (&(G.main->armature))); if (armlist == NULL) return (EXPP_ReturnPyObjError (PyExc_MemoryError, "couldn't create PyList")); while (armature_iter){ pyobj = Armature_CreatePyObject (armature_iter); if (!pyobj) return (EXPP_ReturnPyObjError (PyExc_MemoryError, "couldn't create PyString")); PyList_SET_ITEM (armlist, index, pyobj); armature_iter = armature_iter->id.next; index++; } return (armlist); } } //--------------------------Python BPy_Armature methods------------------------------------------ //---------------------BPy_Armature.getName()------------------------------------------------------- static PyObject * Armature_getName (BPy_Armature * self) { PyObject *attr = PyString_FromString (self->armature->id.name + 2); if (attr) return attr; return (EXPP_ReturnPyObjError (PyExc_RuntimeError, "couldn't get Armature.name attribute")); } //---------------------BPy_Armature.getBones()------------------------------------------------------- static PyObject * Armature_getBones (BPy_Armature * self) { PyObject *listbones = NULL; Bone *parent = NULL; listbones = PyList_New(0); //append root bones for (parent = self->armature->bonebase.first; parent; parent = parent->next){ PyList_Append (listbones, Bone_CreatePyObject (parent)); if(parent->childbase.first){ //has children? append_childrenToList(parent, listbones); } } return listbones; } //---------------------BPy_Armature.addBone()------------------------------------------------------- static PyObject *Armature_addBone(BPy_Armature *self, PyObject *args) { BPy_Bone* py_bone = NULL; float M_boneObjectspace[4][4]; float iM_parentRest[4][4]; Bone *blen_bone; char *parent_str = ""; Bone *parent; if (!PyArg_ParseTuple(args, "O!", &Bone_Type, &py_bone)) return (EXPP_ReturnPyObjError (PyExc_TypeError, "expected bone object argument (or nothing)")); if(py_bone->bone != NULL) return EXPP_ReturnPyObjError (PyExc_TypeError, "this bone has already been linked to an armature"); //check to see if we can parent this bone if it will be attempted otherwise exit if(!BLI_streq(py_bone->parent, parent_str)){ //parenting being attempted //get parent if exists in this armature parent = testBoneNameInArmature(self->armature, py_bone->parent); if(!parent){ //could find the parent's name return (EXPP_ReturnPyObjError (PyExc_TypeError, "cannot find parent's name in armature - check to see if name of parent is correct")); } }else{ //no parent for this bone parent = NULL; } //create a bone struct blen_bone = (Bone*)MEM_callocN(sizeof(Bone), "DefaultBone"); //set the bone struct pointer py_bone->bone = blen_bone; //update the bonestruct data from py data if(!updateBoneData(py_bone, parent)) return EXPP_ReturnPyObjError (PyExc_AttributeError , "bone struct empty"); //make sure the name is unique for this armature unique_BoneName(py_bone->bone->name, self->armature); //if bone has a parent.... if(py_bone->bone->parent){ //then check to see if parent has been added to the armature - bone loop test if(!testBoneInArmature(self->armature, py_bone->bone->parent)) return (EXPP_ReturnPyObjError (PyExc_TypeError, "cannot parent to a bone not yet added to armature!")); //add to parent's childbase BLI_addtail (&py_bone->bone->parent->childbase, py_bone->bone); //get the worldspace coords for the parent get_objectspace_bone_matrix(py_bone->bone->parent, M_boneObjectspace, 0,0); // Invert the parent rest matrix Mat4Invert (iM_parentRest, M_boneObjectspace); //transformation of local bone Mat4MulVecfl(iM_parentRest, py_bone->bone->tail); Mat4MulVecfl(iM_parentRest, py_bone->bone->head); }else //no parent.... BLI_addtail (&self->armature->bonebase,py_bone->bone); //rebuild_bone_parent_matrix(py_bone->bone); precalc_bonelist_irestmats(&self->armature->bonebase); precalc_armature_posemats(self->armature); precalc_bone_defmat (py_bone->bone); Py_INCREF(Py_None); return Py_None; } //---------------------BPy_Armature.setName()------------------------------------------------------- static PyObject * Armature_setName (BPy_Armature * self, PyObject * args) { char *name; char buf[21]; if (!PyArg_ParseTuple (args, "s", &name)) return (EXPP_ReturnPyObjError (PyExc_AttributeError, "expected string argument")); PyOS_snprintf (buf, sizeof (buf), "%s", name); rename_id (&self->armature->id, buf); Py_INCREF (Py_None); return Py_None; } //---------------------BPy_Armature.drawAxes()------------------------------------------------------- static PyObject * Armature_drawAxes (BPy_Armature * self, PyObject * args) { int toggle; if (!PyArg_ParseTuple (args, "i", &toggle)) return (EXPP_ReturnPyObjError (PyExc_AttributeError, "expected 1 or 0 as integer")); if(toggle) self->armature->flag |= ARM_DRAWAXES; else self->armature->flag &= ~ARM_DRAWAXES; Py_INCREF (Py_None); return Py_None; } //---------------------BPy_Armature.drawNames()------------------------------------------------------- static PyObject * Armature_drawNames (BPy_Armature * self, PyObject * args) { int toggle; if (!PyArg_ParseTuple (args, "i", &toggle)) return (EXPP_ReturnPyObjError (PyExc_AttributeError, "expected 1 or 0 as integer")); if(toggle) self->armature->flag |= ARM_DRAWNAMES; else self->armature->flag &= ~ARM_DRAWNAMES; Py_INCREF (Py_None); return Py_None; }