This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/python/api2_2x/Armature.c

1387 lines
43 KiB
C

/*
* $Id$
*
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. The Blender
* Foundation also sells licenses for use in proprietary software under
* the Blender License. See http://www.blender.org/BL/ for information
* about this.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
#include "Armature.h" //This must come first
#include "BKE_main.h"
#include "BKE_global.h"
#include "BKE_armature.h"
#include "BKE_library.h"
#include "BKE_depsgraph.h"
#include "BKE_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.h"
#include "MEM_guardedalloc.h"
#include "Bone.h"
#include "NLA.h"
#include "gen_utils.h"
#include "DNA_object_types.h" //This must come before BIF_editarmature.h...
#include "BIF_editarmature.h"
//------------------EXTERNAL PROTOTYPES--------------------
extern void make_boneList(ListBase* list, ListBase *bones, EditBone *parent);
extern void editbones_to_armature (ListBase *list, Object *ob);
//------------------------ERROR CODES---------------------------------
//This is here just to make me happy and to have more consistant error strings :)
static const char sBoneDictError[] = "ArmatureType.bones - Error: ";
static const char sBoneDictBadArgs[] = "ArmatureType.bones - Bad Arguments: ";
static const char sArmatureError[] = "ArmatureType - Error: ";
static const char sArmatureBadArgs[] = "ArmatureType - Bad Arguments: ";
static const char sModuleError[] = "Blender.Armature - Error: ";
static const char sModuleBadArgs[] = "Blender.Armature - Bad Arguments: ";
//################## BonesDict_Type (internal) ########################
/*This is an internal psuedo-dictionary type that allows for manipulation
* of bones inside of an armature. It is a subobject of armature.
* i.e. Armature.bones['key']*/
//#####################################################################
//------------------METHOD IMPLEMENTATIONS-----------------------------
//------------------------Armature.bones.items()
//Returns a list of key:value pairs like dict.items()
static PyObject* BonesDict_items(BPy_BonesDict *self)
{
if (self->editmode_flag){
return PyDict_Items(self->editbonesMap);
}else{
return PyDict_Items(self->bonesMap);
}
}
//------------------------Armature.bones.keys()
//Returns a list of keys like dict.keys()
static PyObject* BonesDict_keys(BPy_BonesDict *self)
{
if (self->editmode_flag){
return PyDict_Keys(self->editbonesMap);
}else{
return PyDict_Keys(self->bonesMap);
}
}
//------------------------Armature.bones.values()
//Returns a list of values like dict.values()
static PyObject* BonesDict_values(BPy_BonesDict *self)
{
if (self->editmode_flag){
return PyDict_Values(self->editbonesMap);
}else{
return PyDict_Values(self->bonesMap);
}
}
//------------------ATTRIBUTE IMPLEMENTATION---------------------------
//------------------TYPE_OBECT IMPLEMENTATION-----------------------
//------------------------tp_doc
//The __doc__ string for this object
static char BPy_BonesDict_doc[] = "This is an internal subobject of armature\
designed to act as a Py_Bone dictionary.";
//------------------------tp_methods
//This contains a list of all methods the object contains
static PyMethodDef BPy_BonesDict_methods[] = {
{"items", (PyCFunction) BonesDict_items, METH_NOARGS,
"() - Returns the key:value pairs from the dictionary"},
{"keys", (PyCFunction) BonesDict_keys, METH_NOARGS,
"() - Returns the keys the dictionary"},
{"values", (PyCFunction) BonesDict_values, METH_NOARGS,
"() - Returns the values from the dictionary"},
{NULL, NULL, 0, NULL}
};
//-----------------(internal)
static int BoneMapping_Init(PyObject *dictionary, ListBase *bones){
Bone *bone = NULL;
PyObject *py_bone = NULL;
for (bone = bones->first; bone; bone = bone->next){
py_bone = PyBone_FromBone(bone);
if (!py_bone)
return -1;
if(PyDict_SetItem(dictionary,
PyString_FromString(bone->name), py_bone) == -1){
return -1;
}
Py_DECREF(py_bone);
if (bone->childbase.first)
BoneMapping_Init(dictionary, &bone->childbase);
}
return 0;
}
//-----------------(internal)
static int EditBoneMapping_Init(PyObject *dictionary, ListBase *editbones){
EditBone *editbone = NULL;
PyObject *py_editbone = NULL;
for (editbone = editbones->first; editbone; editbone = editbone->next){
py_editbone = PyEditBone_FromEditBone(editbone);
if (!py_editbone)
return -1;
if(PyDict_SetItem(dictionary,
PyString_FromString(editbone->name), py_editbone) == -1){
return -1;
}
Py_DECREF(py_editbone);
}
return 0;
}
//----------------- BonesDict_InitBones
static int BonesDict_InitBones(BPy_BonesDict *self)
{
PyDict_Clear(self->bonesMap);
if (BoneMapping_Init(self->bonesMap, self->bones) == -1)
return 0;
return 1;
}
//----------------- BonesDict_InitEditBones
static int BonesDict_InitEditBones(BPy_BonesDict *self)
{
PyDict_Clear(self->editbonesMap);
if (EditBoneMapping_Init(self->editbonesMap, &self->editbones) == -1)
return 0;
return 1;
}
//------------------------tp_repr
//This is the string representation of the object
static PyObject *BonesDict_repr(BPy_BonesDict *self)
{
char str[2048];
PyObject *key, *value;
int pos = 0;
char *p = str;
char *keys, *vals;
p += sprintf(str, "[Bone Dict: {");
if (self->editmode_flag){
while (PyDict_Next(self->editbonesMap, &pos, &key, &value)) {
keys = PyString_AsString(key);
vals = PyString_AsString(value->ob_type->tp_repr(value));
if( strlen(str) + strlen(keys) + strlen(vals) < sizeof(str)-20 )
p += sprintf(p, "%s : %s, ", keys, vals );
else {
p += sprintf(p, "...." );
break;
}
}
}else{
while (PyDict_Next(self->bonesMap, &pos, &key, &value)) {
keys = PyString_AsString(key);
vals = PyString_AsString(value->ob_type->tp_repr(value));
if( strlen(str) + strlen(keys) + strlen(vals) < sizeof(str)-20 )
p += sprintf(p, "%s : %s, ", keys, vals );
else {
p += sprintf(p, "...." );
break;
}
}
}
sprintf(p, "}]");
return PyString_FromString(str);
}
//------------------------tp_dealloc
//This tells how to 'tear-down' our object when ref count hits 0
static void BonesDict_dealloc(BPy_BonesDict * self)
{
Py_DECREF(self->bonesMap);
Py_DECREF(self->editbonesMap);
BLI_freelistN(&self->editbones);
BonesDict_Type.tp_free(self);
return;
}
//------------------------mp_length
//This gets the size of the dictionary
static int BonesDict_len(BPy_BonesDict *self)
{
if (self->editmode_flag){
return BLI_countlist(&self->editbones);
}else{
return BLI_countlist(self->bones);
}
}
//-----------------------mp_subscript
//This defines getting a bone from the dictionary - x = Bones['key']
static PyObject *BonesDict_GetItem(BPy_BonesDict *self, PyObject* key)
{
PyObject *value = NULL;
if (self->editmode_flag){
value = PyDict_GetItem(self->editbonesMap, key);
}else{
value = PyDict_GetItem(self->bonesMap, key);
}
if(value == NULL){ /* item not found in dict. throw exception */
char buffer[128];
char* key_str;
key_str = PyString_AsString( key );
if( !key_str ){ /* key not a py string */
key_str = ""; /* use empty string for printing */
}
PyOS_snprintf( buffer, sizeof(buffer),
"bone %s not found", key_str);
return EXPP_ReturnPyObjError(PyExc_KeyError, buffer );
}
return EXPP_incr_ret(value);
}
//-----------------------mp_ass_subscript
//This does dict assignment - Bones['key'] = value
static int BonesDict_SetItem(BPy_BonesDict *self, PyObject *key, PyObject *value)
{
BPy_EditBone *editbone_for_deletion;
struct EditBone *editbone = NULL;
char *key_str = "";
if (self->editmode_flag){
//Get the key name
if(key && PyString_Check(key)){
key_str = PyString_AsString(key);
}else{
goto AttributeError;
}
//parse value for assignment
if (value && EditBoneObject_Check(value)){
//create a new editbone
editbone = MEM_callocN(sizeof(EditBone), "eBone");
BLI_strncpy(editbone->name, key_str, 32);
unique_editbone_name(editbone->name);
editbone->dist = ((BPy_EditBone*)value)->dist;
editbone->ease1 = ((BPy_EditBone*)value)->ease1;
editbone->ease2 = ((BPy_EditBone*)value)->ease2;
editbone->flag = ((BPy_EditBone*)value)->flag;
editbone->parent = ((BPy_EditBone*)value)->parent;
editbone->rad_head = ((BPy_EditBone*)value)->rad_head;
editbone->rad_tail = ((BPy_EditBone*)value)->rad_tail;
editbone->roll = ((BPy_EditBone*)value)->roll;
editbone->segments = ((BPy_EditBone*)value)->segments;
editbone->weight = ((BPy_EditBone*)value)->weight;
editbone->xwidth = ((BPy_EditBone*)value)->xwidth;
editbone->zwidth = ((BPy_EditBone*)value)->zwidth;
VECCOPY(editbone->head, ((BPy_EditBone*)value)->head);
VECCOPY(editbone->tail, ((BPy_EditBone*)value)->tail);
// FIXME, should be exposed via python. this avoids creating bones with no layers.
editbone->layer= 1;
//set object pointer
((BPy_EditBone*)value)->editbone = editbone;
//fix the bone's head position if flags indicate that it is 'connected'
if (editbone->flag & BONE_CONNECTED){
if(!editbone->parent){
((BPy_EditBone*)value)->editbone = NULL;
MEM_freeN(editbone);
goto AttributeError3;
}else{
VECCOPY(editbone->head, editbone->parent->tail);
}
}
//set in editbonelist
BLI_addtail(&self->editbones, editbone);
//set the new editbone in the mapping
if(PyDict_SetItemString(self->editbonesMap, key_str, value) == -1){
((BPy_EditBone*)value)->editbone = NULL;
BLI_freelinkN(&self->editbones, editbone);
goto RuntimeError;
}
}else if(!value){
//they are trying to delete the bone using 'del'
if(PyDict_GetItem(self->editbonesMap, key) != NULL){
/*first kill the datastruct then remove the item from the dict
and wait for GC to pick it up.
We have to delete the datastruct here because the tp_dealloc
doesn't handle it*/
editbone_for_deletion = (BPy_EditBone*)PyDict_GetItem(self->editbonesMap, key);
/*this is ugly but you have to set the parent to NULL for else
editbones_to_armature will crash looking for this bone*/
for (editbone = self->editbones.first; editbone; editbone = editbone->next){
if (editbone->parent == editbone_for_deletion->editbone) {
editbone->parent = NULL;
/* remove the connected flag or else the 'root' ball
* doesn't get drawn */
editbone->flag &= ~BONE_CONNECTED;
}
}
BLI_freelinkN(&self->editbones, editbone_for_deletion->editbone);
if(PyDict_DelItem(self->editbonesMap, key) == -1)
goto RuntimeError;
}else{
goto KeyError;
}
}
return 0;
}else{
goto AttributeError2;
}
KeyError:
return EXPP_intError(PyExc_KeyError, "%s%s%s%s",
sBoneDictError, "The key: ", key_str, " is not present in this dictionary!");
RuntimeError:
return EXPP_intError(PyExc_RuntimeError, "%s%s",
sBoneDictError, "Unable to access dictionary!");
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sBoneDictBadArgs, "Expects EditboneType Object");
AttributeError2:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sBoneDictBadArgs, "You must call makeEditable() first");
AttributeError3:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sBoneDictBadArgs, "The 'connected' flag is set but the bone has no parent!");
}
//------------------TYPE_OBJECT DEFINITION--------------------------
//Mapping Protocol
static PyMappingMethods BonesDict_MapMethods = {
(inquiry) BonesDict_len, //mp_length
(binaryfunc)BonesDict_GetItem, //mp_subscript
(objobjargproc)BonesDict_SetItem, //mp_ass_subscript
};
//BonesDict TypeObject
PyTypeObject BonesDict_Type = {
PyObject_HEAD_INIT(NULL) //tp_head
0, //tp_internal
"BonesDict", //tp_name
sizeof(BPy_BonesDict), //tp_basicsize
0, //tp_itemsize
(destructor)BonesDict_dealloc, //tp_dealloc
0, //tp_print
0, //tp_getattr
0, //tp_setattr
0, //tp_compare
(reprfunc) BonesDict_repr, //tp_repr
0, //tp_as_number
0, //tp_as_sequence
&BonesDict_MapMethods, //tp_as_mapping
0, //tp_hash
0, //tp_call
0, //tp_str
0, //tp_getattro
0, //tp_setattro
0, //tp_as_buffer
Py_TPFLAGS_DEFAULT, //tp_flags
BPy_BonesDict_doc, //tp_doc
0, //tp_traverse
0, //tp_clear
0, //tp_richcompare
0, //tp_weaklistoffset
0, //tp_iter
0, //tp_iternext
BPy_BonesDict_methods, //tp_methods
0, //tp_members
0, //tp_getset
0, //tp_base
0, //tp_dict
0, //tp_descr_get
0, //tp_descr_set
0, //tp_dictoffset
0, //tp_init
0, //tp_alloc
0, //tp_new
0, //tp_free
0, //tp_is_gc
0, //tp_bases
0, //tp_mro
0, //tp_cache
0, //tp_subclasses
0, //tp_weaklist
0 //tp_del
};
//-----------------------PyBonesDict_FromPyArmature
static PyObject *PyBonesDict_FromPyArmature(BPy_Armature *py_armature)
{
BPy_BonesDict *py_BonesDict = NULL;
//create py object
py_BonesDict = (BPy_BonesDict *)BonesDict_Type.tp_alloc(&BonesDict_Type, 0);
if (!py_BonesDict)
goto RuntimeError;
//create internal dictionaries
py_BonesDict->bonesMap = PyDict_New();
py_BonesDict->editbonesMap = PyDict_New();
if (!py_BonesDict->bonesMap || !py_BonesDict->editbonesMap)
goto RuntimeError;
//set listbase pointer
py_BonesDict->bones = &py_armature->armature->bonebase;
//now that everything is setup - init the mappings
if (!BonesDict_InitBones(py_BonesDict))
goto RuntimeError;
if (!BonesDict_InitEditBones(py_BonesDict))
goto RuntimeError;
//set editmode flag
py_BonesDict->editmode_flag = 0;
return (PyObject*)py_BonesDict;
RuntimeError:
return EXPP_objError(PyExc_RuntimeError, "%s%s",
sBoneDictError, "Failed to create class");
}
//######################### Armature_Type #############################
/*This type represents a thin wrapper around bArmature data types
* internal to blender. It contains the psuedo-dictionary BonesDict
* as an assistant in manipulating it's own bone collection*/
//#################################################################
//------------------METHOD IMPLEMENTATION------------------------------
//------------------------Armature.makeEditable()
static PyObject *Armature_makeEditable(BPy_Armature *self)
{
if (self->armature->flag & ARM_EDITMODE)
goto AttributeError;
make_boneList(&self->Bones->editbones, self->Bones->bones, NULL);
if (!BonesDict_InitEditBones(self->Bones))
return NULL;
self->Bones->editmode_flag = 1;
return EXPP_incr_ret(Py_None);
AttributeError:
return EXPP_objError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "The armature cannot be placed manually in editmode before you call makeEditable()!");
}
//------------------------Armature.update()
//This is a bit ugly because you need an object link to do this
static PyObject *Armature_update(BPy_Armature *self)
{
Object *obj = NULL;
for (obj = G.main->object.first; obj; obj = obj->id.next){
if (obj->data == self->armature)
break;
}
if (obj){
editbones_to_armature (&self->Bones->editbones, obj);
if (!BonesDict_InitBones(self->Bones))
return NULL;
self->Bones->editmode_flag = 0;
BLI_freelistN(&self->Bones->editbones);
}else{
goto AttributeError;
}
return EXPP_incr_ret(Py_None);
AttributeError:
return EXPP_objError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "The armature must be linked to an object before you can save changes!");
}
//------------------------Armature.__copy__()
static PyObject *Armature_copy(BPy_Armature *self)
{
PyObject *py_armature = NULL;
bArmature *bl_armature;
bl_armature= copy_armature(self->armature);
bl_armature->id.us= 0;
py_armature= PyArmature_FromArmature( bl_armature );
return py_armature;
}
//------------------ATTRIBUTE IMPLEMENTATION---------------------------
//------------------------Armature.autoIK (getter)
static PyObject *Armature_getAutoIK(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_AUTO_IK)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.autoIK (setter)
static int Armature_setAutoIK(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_AUTO_IK;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_AUTO_IK;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.layers (getter)
static PyObject *Armature_getLayers(BPy_Armature *self, void *closure)
{
int layers, bit = 0, val = 0;
PyObject *item = NULL, *laylist = PyList_New( 0 );
if( !laylist )
return EXPP_ReturnPyObjError( PyExc_MemoryError,
"couldn't create pylist!" );
layers = self->armature->layer;
while( bit < 20 ) {
val = 1 << bit;
if( layers & val ) {
item = Py_BuildValue( "i", bit + 1 );
PyList_Append( laylist, item );
Py_DECREF( item );
}
bit++;
}
return laylist;
}
//------------------------Armature.layer (setter)
static int Armature_setLayers(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyList_Check(value)){
int layers = 0, len_list = 0;
int val;
PyObject *item = NULL;
len_list = PyList_Size(value);
if( len_list == 0 )
return EXPP_ReturnIntError( PyExc_AttributeError,
"list can't be empty, at least one layer must be set" );
while( len_list ) {
--len_list;
item = PyList_GetItem( value, len_list );
if( !PyInt_Check( item ) )
return EXPP_ReturnIntError( PyExc_AttributeError,
"list must contain only integer numbers" );
val = ( int ) PyInt_AsLong( item );
if( val < 1 || val > 20 )
return EXPP_ReturnIntError( PyExc_AttributeError,
"layer values must be in the range [1, 20]" );
layers |= 1 << ( val - 1 );
}
/* update any bases pointing to our object */
self->armature->layer = (short)layers;
return 0;
}
}
goto AttributeError;
AttributeError:
return EXPP_ReturnIntError( PyExc_TypeError,
"expected a list of integers" );
}
//------------------------Armature.mirrorEdit (getter)
static PyObject *Armature_getMirrorEdit(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_MIRROR_EDIT)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.mirrorEdit (setter)
static int Armature_setMirrorEdit(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_MIRROR_EDIT;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_MIRROR_EDIT;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.drawType (getter)
static PyObject *Armature_getDrawType(BPy_Armature *self, void *closure)
{
if (self->armature->drawtype == ARM_OCTA){
return EXPP_GetModuleConstant("Blender.Armature", "OCTAHEDRON") ;
}else if (self->armature->drawtype == ARM_LINE){
return EXPP_GetModuleConstant("Blender.Armature", "STICK") ;
}else if (self->armature->drawtype == ARM_B_BONE){
return EXPP_GetModuleConstant("Blender.Armature", "BBONE") ;
}else if (self->armature->drawtype == ARM_ENVELOPE){
return EXPP_GetModuleConstant("Blender.Armature", "ENVELOPE") ;
}else{
goto RuntimeError;
}
RuntimeError:
return EXPP_objError(PyExc_RuntimeError, "%s%s%s",
sArmatureError, "drawType: ", "Internal failure!");
}
//------------------------Armature.drawType (setter)
static int Armature_setDrawType(BPy_Armature *self, PyObject *value, void *closure)
{
PyObject *val = NULL, *name = NULL;
long numeric_value;
if(value){
if(BPy_Constant_Check(value)){
name = PyDict_GetItemString(((BPy_constant*)value)->dict, "name");
if (!STREQ2(PyString_AsString(name), "OCTAHEDRON", "STICK") &&
!STREQ2(PyString_AsString(name), "BBONE", "ENVELOPE"))
goto ValueError;
val = PyDict_GetItemString(((BPy_constant*)value)->dict, "value");
if (PyInt_Check(val)){
numeric_value = PyInt_AS_LONG(val);
self->armature->drawtype = (int)numeric_value;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects module constant");
ValueError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Argument must be the constant OCTAHEDRON, STICK, BBONE, or ENVELOPE");
}
//------------------------Armature.ghostStep (getter)
static PyObject *Armature_getStep(BPy_Armature *self, void *closure)
{
return PyInt_FromLong((long)self->armature->ghostsize);
}
//------------------------Armature.ghostStep (setter)
static int Armature_setStep(BPy_Armature *self, PyObject *value, void *closure)
{
long numerical_value;
if(value){
if(PyInt_Check(value)){
numerical_value = PyInt_AS_LONG(value);
if (numerical_value > 20.0f || numerical_value < 1.0f)
goto ValueError;
self->armature->ghostsize = (short)numerical_value;
return 0;
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects Integer");
ValueError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Argument must fall within 1-20");
}
//------------------------Armature.ghost (getter)
static PyObject *Armature_getGhost(BPy_Armature *self, void *closure)
{
return PyInt_FromLong((long)self->armature->ghostep);
}
//------------------------Armature.ghost (setter)
static int Armature_setGhost(BPy_Armature *self, PyObject *value, void *closure)
{
long numerical_value;
if(value){
if(PyInt_Check(value)){
numerical_value = PyInt_AS_LONG(value);
if (numerical_value > 30.0f || numerical_value < 0.0f)
goto ValueError;
self->armature->ghostep = (short)numerical_value;
return 0;
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects Integer");
ValueError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Argument must fall within 0-30");
}
//------------------------Armature.drawNames (getter)
static PyObject *Armature_getDrawNames(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_DRAWNAMES)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.drawNames (setter)
static int Armature_setDrawNames(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_DRAWNAMES;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_DRAWNAMES;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.drawAxes (getter)
static PyObject *Armature_getDrawAxes(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_DRAWAXES)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.drawAxes (setter)
static int Armature_setDrawAxes(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_DRAWAXES;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_DRAWAXES;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.delayDeform (getter)
static PyObject *Armature_getDelayDeform(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_DELAYDEFORM)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.delayDeform (setter)
static int Armature_setDelayDeform(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_DELAYDEFORM;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_DELAYDEFORM;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.restPosition (getter)
static PyObject *Armature_getRestPosition(BPy_Armature *self, void *closure)
{
if (self->armature->flag & ARM_RESTPOS)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.restPosition (setter)
static int Armature_setRestPosition(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->flag |= ARM_RESTPOS;
return 0;
}else if (value == Py_False){
self->armature->flag &= ~ARM_RESTPOS;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.envelopes (getter)
static PyObject *Armature_getEnvelopes(BPy_Armature *self, void *closure)
{
if (self->armature->deformflag & ARM_DEF_ENVELOPE)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.envelopes (setter)
static int Armature_setEnvelopes(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->deformflag |= ARM_DEF_ENVELOPE;
return 0;
}else if (value == Py_False){
self->armature->deformflag &= ~ARM_DEF_ENVELOPE;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.vertexGroups (getter)
static PyObject *Armature_getVertexGroups(BPy_Armature *self, void *closure)
{
if (self->armature->deformflag & ARM_DEF_VGROUP)
return EXPP_incr_ret(Py_True);
else
return EXPP_incr_ret(Py_False);
}
//------------------------Armature.vertexGroups (setter)
static int Armature_setVertexGroups(BPy_Armature *self, PyObject *value, void *closure)
{
if(value){
if(PyBool_Check(value)){
if (value == Py_True){
self->armature->deformflag |= ARM_DEF_VGROUP;
return 0;
}else if (value == Py_False){
self->armature->deformflag &= ~ARM_DEF_VGROUP;
return 0;
}
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects True or False");
}
//------------------------Armature.name (getter)
//Gets the name of the armature
static PyObject *Armature_getName(BPy_Armature *self, void *closure)
{
return PyString_FromString(self->armature->id.name +2); //*new*
}
//------------------------Armature.name (setter)
//Sets the name of the armature
static int Armature_setName(BPy_Armature *self, PyObject *value, void *closure)
{
char buffer[24];
char *name = "";
if(value){
if(PyString_Check(value)){
name = PyString_AsString(value);
PyOS_snprintf(buffer, sizeof(buffer), "%s", name);
rename_id(&self->armature->id, buffer);
return 0;
}
}
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureBadArgs, "Expects string");
}
//------------------------Armature.bones (getter)
//Gets the name of the armature
static PyObject *Armature_getBoneDict(BPy_Armature *self, void *closure)
{
return EXPP_incr_ret((PyObject*)self->Bones);
}
//------------------------Armature.bones (setter)
//Sets the name of the armature
/*TODO*/
/*Copy Bones through x = y*/
static int Armature_setBoneDict(BPy_Armature *self, PyObject *value, void *closure)
{
goto AttributeError;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s",
sArmatureError, "You are not allowed to change the .Bones attribute");
}
//------------------TYPE_OBECT IMPLEMENTATION--------------------------
//------------------------tp_doc
//The __doc__ string for this object
static char BPy_Armature_doc[] = "This object wraps a Blender Armature object.";
//------------------------tp_methods
//This contains a list of all methods the object contains
static PyMethodDef BPy_Armature_methods[] = {
{"makeEditable", (PyCFunction) Armature_makeEditable, METH_NOARGS,
"() - Unlocks the ability to modify armature bones"},
{"update", (PyCFunction) Armature_update, METH_NOARGS,
"() - Rebuilds the armature based on changes to bones since the last call to makeEditable"},
{"__copy__", (PyCFunction) Armature_copy, METH_NOARGS,
"() - Return a copy of the armature."},
{NULL, NULL, 0, NULL}
};
//------------------------tp_getset
//This contains methods for attributes that require checking
static PyGetSetDef BPy_Armature_getset[] = {
{"name", (getter)Armature_getName, (setter)Armature_setName,
"The armature's name", NULL},
{"bones", (getter)Armature_getBoneDict, (setter)Armature_setBoneDict,
"The armature's Bone dictionary", NULL},
{"vertexGroups", (getter)Armature_getVertexGroups, (setter)Armature_setVertexGroups,
"Enable/Disable vertex group defined deformation", NULL},
{"envelopes", (getter)Armature_getEnvelopes, (setter)Armature_setEnvelopes,
"Enable/Disable bone envelope defined deformation", NULL},
{"restPosition", (getter)Armature_getRestPosition, (setter)Armature_setRestPosition,
"Show armature rest position - disables posing", NULL},
{"delayDeform", (getter)Armature_getDelayDeform, (setter)Armature_setDelayDeform,
"Don't deform children when manipulating bones in pose mode", NULL},
{"drawAxes", (getter)Armature_getDrawAxes, (setter)Armature_setDrawAxes,
"Enable/Disable drawing the bone axes", NULL},
{"drawNames", (getter)Armature_getDrawNames, (setter)Armature_setDrawNames,
"Enable/Disable drawing the bone names", NULL},
{"ghost", (getter)Armature_getGhost, (setter)Armature_setGhost,
"Draw a number of ghosts around the current frame for current Action", NULL},
{"ghostStep", (getter)Armature_getStep, (setter)Armature_setStep,
"The number of frames between ghost instances", NULL},
{"drawType", (getter)Armature_getDrawType, (setter)Armature_setDrawType,
"The type of drawing currently applied to the armature", NULL},
{"mirrorEdit", (getter)Armature_getMirrorEdit, (setter)Armature_setMirrorEdit,
"Enable/Disable X-axis mirrored editing", NULL},
{"autoIK", (getter)Armature_getAutoIK, (setter)Armature_setAutoIK,
"Adds temporal IK chains while grabbing bones", NULL},
{"layers", (getter)Armature_getLayers, (setter)Armature_setLayers,
"List of layers for the armature", NULL},
{NULL, NULL, NULL, NULL, NULL}
};
//------------------------tp_new
//This methods creates a new object (note it does not initialize it - only the building)
//This can be called through python by myObject.__new__() however, tp_init is not called
static PyObject *Armature_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
BPy_Armature *py_armature = NULL;
bArmature *bl_armature;
bl_armature = add_armature();
if(bl_armature) {
bl_armature->id.us = 0; // return count to 0 - add_armature() inc'd it
py_armature = (BPy_Armature*)type->tp_alloc(type, 0); //*new*
if (py_armature == NULL)
goto RuntimeError;
py_armature->armature = bl_armature;
//create armature.bones
py_armature->Bones = (BPy_BonesDict*)PyBonesDict_FromPyArmature(py_armature);
if (!py_armature->Bones)
goto RuntimeError;
} else {
goto RuntimeError;
}
return (PyObject*)py_armature;
RuntimeError:
return EXPP_objError(PyExc_RuntimeError, "%s%s%s",
sArmatureError, " __new__: ", "couldn't create Armature Data in Blender");
}
//------------------------tp_init
//This methods does initialization of the new object
//This method will get called in python by 'myObject(argument, keyword=value)'
//tp_new will be automatically called before this
static int Armature_init(BPy_Armature *self, PyObject *args, PyObject *kwds)
{
char buf[21];
char *name = "myArmature";
static char *kwlist[] = {"name", NULL};
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, &name)){
goto AttributeError;
}
//rename the armature if a name is supplied
if(!BLI_streq(name, "myArmature")){
PyOS_snprintf(buf, sizeof(buf), "%s", name);
rename_id(&self->armature->id, buf);
}
return 0;
AttributeError:
return EXPP_intError(PyExc_AttributeError, "%s%s%s",
sArmatureBadArgs, " __init__: ", "Expects string(name)");
}
/*****************************************************************************/
/* Function: Armature_compare */
/* Description: This is a callback function for the BPy_Armature type. It */
/* compares two Armature_Type objects. Only the "==" and "!=" */
/* comparisons are meaninful. Returns 0 for equality and -1 if */
/* they don't point to the same Blender Object struct. */
/* In Python it becomes 1 if they are equal, 0 otherwise. */
/*****************************************************************************/
static int Armature_compare( BPy_Armature * a, BPy_Armature * b )
{
return ( a->armature == b->armature ) ? 0 : -1;
}
//------------------------tp_repr
//This is the string representation of the object
static PyObject *Armature_repr(BPy_Armature *self)
{
return PyString_FromFormat( "[Armature: \"%s\"]", self->armature->id.name + 2 ); //*new*
}
//------------------------tp_dealloc
//This tells how to 'tear-down' our object when ref count hits 0
///tp_dealloc
static void Armature_dealloc(BPy_Armature * self)
{
Py_DECREF(self->Bones);
Armature_Type.tp_free(self);
return;
}
//------------------TYPE_OBECT DEFINITION--------------------------
PyTypeObject Armature_Type = {
PyObject_HEAD_INIT(NULL) //tp_head
0, //tp_internal
"Armature", //tp_name
sizeof(BPy_Armature), //tp_basicsize
0, //tp_itemsize
(destructor)Armature_dealloc, //tp_dealloc
0, //tp_print
0, //tp_getattr
0, //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_hash
0, //tp_call
0, //tp_str
0, //tp_getattro
0, //tp_setattro
0, //tp_as_buffer
Py_TPFLAGS_DEFAULT, //tp_flags
BPy_Armature_doc, //tp_doc
0, //tp_traverse
0, //tp_clear
0, //tp_richcompare
0, //tp_weaklistoffset
0, //tp_iter
0, //tp_iternext
BPy_Armature_methods, //tp_methods
0, //tp_members
BPy_Armature_getset, //tp_getset
0, //tp_base
0, //tp_dict
0, //tp_descr_get
0, //tp_descr_set
0, //tp_dictoffset
(initproc)Armature_init, //tp_init
0, //tp_alloc
(newfunc)Armature_new, //tp_new
0, //tp_free
0, //tp_is_gc
0, //tp_bases
0, //tp_mro
0, //tp_cache
0, //tp_subclasses
0, //tp_weaklist
0 //tp_del
};
//-------------------MODULE METHODS IMPLEMENTATION------------------------
//----------------Blender.Armature.Get()
/* This function will return a Py_Armature when a single string is passed
* or else it will return a {key:value} dictionary when mutliple strings are passed
* or it will return a {key:value} dictionary of all armatures when nothing is passed*/
static PyObject *M_Armature_Get(PyObject * self, PyObject * args)
{
PyObject *seq = NULL, *item = NULL, *dict = NULL, *py_armature = NULL;
char *name = "", buffer[24];
int size = 0, i;
void *data;
//GET ARGUMENTS - () ('s') ('s',..) (['s',..]) are exceptable
size = PySequence_Length(args);
if (size == 1) {
seq = PySequence_GetItem(args, 0); //*new*
if (!seq)
goto RuntimeError;
if(!PyString_Check(seq)){
if (PySequence_Check(seq)) {
size = PySequence_Length(seq);
} else {
Py_DECREF(seq);
goto AttributeError;
}
}
} else {
seq = EXPP_incr_ret(args); //*take ownership*
}
//'seq' should be a list, empty tuple or string - check list for strings
if(!PyString_Check(seq)){
for(i = 0; i < size; i++){
item = PySequence_GetItem(seq, i); //*new*
if (!item) {
Py_DECREF(seq);
goto RuntimeError;
}
if(!PyString_Check(item)){
EXPP_decr2(item, seq);
goto AttributeError;
}
Py_DECREF(item);
}
}
//GET ARMATURES
if(size != 1){
dict = PyDict_New(); //*new*
if(!dict){
Py_DECREF(seq);
goto RuntimeError;
}
if(size == 0){ //GET ALL ARMATURES
data = G.main->armature.first; //get the first data ID from the armature library
while (data){
py_armature = PyArmature_FromArmature(data); //*new*
sprintf(buffer, "%s", ((bArmature*)data)->id.name +2);
if(PyDict_SetItemString(dict, buffer, py_armature) == -1){ //add to dictionary
EXPP_decr3(seq, dict, py_armature);
goto RuntimeError;
}
Py_DECREF(py_armature);
data = ((ID*)data)->next;
}
Py_DECREF(seq);
}else{ //GET ARMATURE LIST
for (i = 0; i < size; i++) {
item = PySequence_GetItem(seq, i); //*new*
name = PyString_AsString(item);
Py_DECREF(item);
data = find_id("AR", name); //get data from library
if (data != NULL){
py_armature = PyArmature_FromArmature(data); //*new*
if(PyDict_SetItemString(dict, name, py_armature) == -1){ //add to dictionary
EXPP_decr3(seq, dict, py_armature);
goto RuntimeError;
}
Py_DECREF(py_armature);
}else{
if(PyDict_SetItemString(dict, name, Py_None) == -1){ //add to dictionary
EXPP_decr2(seq, dict);
goto RuntimeError;
}
Py_DECREF(Py_None);
}
}
Py_DECREF(seq);
}
return dict;
}else{ //GET SINGLE ARMATURE
if(!PyString_Check(seq)){ //This handles the bizarre case where (['s']) is passed
item = PySequence_GetItem(seq, 0); //*new*
name = PyString_AsString(item);
Py_DECREF(item);
}else{
name = PyString_AsString(seq);
}
Py_DECREF(seq);
data = find_id("AR", name); //get data from library
if (data != NULL){
return PyArmature_FromArmature(data); //*new*
}else{
return EXPP_incr_ret(Py_None);
}
}
RuntimeError:
return EXPP_objError(PyExc_RuntimeError, "%s%s%s",
sModuleError, "Get(): ", "Internal Error Ocurred");
AttributeError:
return EXPP_objError(PyExc_AttributeError, "%s%s%s",
sModuleBadArgs, "Get(): ", "- Expects (optional) string sequence");
}
//----------------Blender.Armature.New()
static PyObject *M_Armature_New(PyObject * self, PyObject * args)
{
char *name = "Armature";
struct bArmature *armature;
BPy_Armature *obj;
char buf[21];
if( !PyArg_ParseTuple( args, "|s", &name ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected nothing or a string as argument" );
armature= add_armature();
armature->id.us = 0;
obj = (BPy_Armature *)PyArmature_FromArmature(armature); /*new*/
if( !obj )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"PyObject_New() failed" );
PyOS_snprintf( buf, sizeof( buf ), "%s", name );
rename_id( &armature->id, buf );
obj->armature = armature;
return (PyObject *)obj;
}
//-------------------MODULE METHODS DEFINITION-----------------------------
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_New_doc[] = "(name) - return a new armature object.";
struct PyMethodDef M_Armature_methods[] = {
{"Get", M_Armature_Get, METH_VARARGS, M_Armature_Get_doc},
{"New", M_Armature_New, METH_VARARGS, M_Armature_New_doc},
{NULL, NULL, 0, NULL}
};
//------------------VISIBLE PROTOTYPE IMPLEMENTATION-----------------------
//-----------------(internal)
//Converts a bArmature to a PyArmature
PyObject *PyArmature_FromArmature(struct bArmature *armature)
{
BPy_Armature *py_armature = NULL;
//create armature type
py_armature = (BPy_Armature*)Armature_Type.tp_alloc(&Armature_Type, 0); //*new*
if (!py_armature)
goto RuntimeError;
py_armature->armature = armature;
//create armature.bones
py_armature->Bones = (BPy_BonesDict*)PyBonesDict_FromPyArmature(py_armature);
if (!py_armature->Bones)
goto RuntimeError;
return (PyObject *) py_armature;
RuntimeError:
return EXPP_objError(PyExc_RuntimeError, "%s%s%s",
sModuleError, "PyArmature_FromArmature: ", "Internal Error Ocurred");
}
//-----------------(internal)
//Converts a PyArmature to a bArmature
struct bArmature *PyArmature_AsArmature(BPy_Armature *py_armature)
{
return (py_armature->armature);
}
//-------------------MODULE INITIALIZATION--------------------------------
PyObject *Armature_Init(void)
{
PyObject *module, *dict;
//Initializes TypeObject.ob_type
if (PyType_Ready(&Armature_Type) < 0 || PyType_Ready(&BonesDict_Type) < 0 ||
PyType_Ready(&EditBone_Type) < 0 || PyType_Ready(&Bone_Type) < 0) {
return EXPP_incr_ret(Py_None);
}
//Register the module
module = Py_InitModule3("Blender.Armature", M_Armature_methods,
"The Blender Armature module");
//Add TYPEOBJECTS to the module
PyModule_AddObject(module, "Armature",
EXPP_incr_ret((PyObject *)&Armature_Type)); //*steals*
PyModule_AddObject(module, "Bone",
EXPP_incr_ret((PyObject *)&Bone_Type)); //*steals*
PyModule_AddObject(module, "Editbone",
EXPP_incr_ret((PyObject *)&EditBone_Type)); //*steals*
//Add CONSTANTS to the module
PyModule_AddObject(module, "CONNECTED",
EXPP_incr_ret(PyConstant_NewInt("CONNECTED", BONE_CONNECTED)));
PyModule_AddObject(module, "HINGE",
EXPP_incr_ret(PyConstant_NewInt("HINGE", BONE_HINGE)));
PyModule_AddObject(module, "NO_DEFORM",
EXPP_incr_ret(PyConstant_NewInt("NO_DEFORM", BONE_NO_DEFORM)));
PyModule_AddObject(module, "MULTIPLY",
EXPP_incr_ret(PyConstant_NewInt("MULTIPLY", BONE_MULT_VG_ENV)));
PyModule_AddObject(module, "HIDDEN_EDIT",
EXPP_incr_ret(PyConstant_NewInt("HIDDEN_EDIT", BONE_HIDDEN_A)));
PyModule_AddObject(module, "ROOT_SELECTED",
EXPP_incr_ret(PyConstant_NewInt("ROOT_SELECTED", BONE_ROOTSEL)));
PyModule_AddObject(module, "BONE_SELECTED",
EXPP_incr_ret(PyConstant_NewInt("BONE_SELECTED", BONE_SELECTED)));
PyModule_AddObject(module, "TIP_SELECTED",
EXPP_incr_ret(PyConstant_NewInt("TIP_SELECTED", BONE_TIPSEL)));
PyModule_AddObject(module, "OCTAHEDRON",
EXPP_incr_ret(PyConstant_NewInt("OCTAHEDRON", ARM_OCTA)));
PyModule_AddObject(module, "STICK",
EXPP_incr_ret(PyConstant_NewInt("STICK", ARM_LINE)));
PyModule_AddObject(module, "BBONE",
EXPP_incr_ret(PyConstant_NewInt("BBONE", ARM_B_BONE)));
PyModule_AddObject(module, "ENVELOPE",
EXPP_incr_ret(PyConstant_NewInt("ENVELOPE", ARM_ENVELOPE)));
//Add SUBMODULES to the module
dict = PyModule_GetDict( module ); //borrowed
PyDict_SetItemString(dict, "NLA", NLA_Init()); //creates a *new* module
return module;
}