Blender  V2.59
BL_ArmatureObject.cpp
Go to the documentation of this file.
00001 /*
00002  * $Id: BL_ArmatureObject.cpp 36523 2011-05-06 20:18:42Z blendix $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): none yet.
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00035 #include "BL_ArmatureObject.h"
00036 #include "BL_ActionActuator.h"
00037 #include "KX_BlenderSceneConverter.h"
00038 #include "MEM_guardedalloc.h"
00039 #include "BLI_blenlib.h"
00040 #include "BLI_math.h"
00041 #include "BLI_utildefines.h"
00042 #include "BLI_ghash.h"
00043 #include "BIK_api.h"
00044 #include "BKE_action.h"
00045 #include "BKE_armature.h"
00046 
00047 #include "BKE_constraint.h"
00048 #include "CTR_Map.h"
00049 #include "CTR_HashedPtr.h"
00050 #include "MEM_guardedalloc.h"
00051 #include "DNA_action_types.h"
00052 #include "DNA_armature_types.h"
00053 #include "DNA_object_types.h"
00054 #include "DNA_scene_types.h"
00055 #include "DNA_nla_types.h"
00056 #include "DNA_constraint_types.h"
00057 #include "KX_PythonSeq.h"
00058 #include "KX_PythonInit.h"
00059 #include "KX_KetsjiEngine.h"
00060 
00061 #include "MT_Matrix4x4.h"
00062 
00074 void game_copy_pose(bPose **dst, bPose *src, int copy_constraint)
00075 {
00076         bPose *out;
00077         bPoseChannel *pchan, *outpchan;
00078         GHash *ghash;
00079         
00080         /* the game engine copies the current armature pose and then swaps
00081          * the object pose pointer. this makes it possible to change poses
00082          * without affecting the original blender data. */
00083 
00084         if (!src) {
00085                 *dst=NULL;
00086                 return;
00087         }
00088         else if (*dst==src) {
00089                 printf("copy_pose source and target are the same\n");
00090                 *dst=NULL;
00091                 return;
00092         }
00093         
00094         out= (bPose*)MEM_dupallocN(src);
00095         out->chanhash = NULL;
00096         out->agroups.first= out->agroups.last= NULL;
00097         out->ikdata = NULL;
00098         out->ikparam = MEM_dupallocN(out->ikparam);
00099         out->flag |= POSE_GAME_ENGINE;
00100         BLI_duplicatelist(&out->chanbase, &src->chanbase);
00101 
00102         /* remap pointers */
00103         ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "game_copy_pose gh");
00104 
00105         pchan= (bPoseChannel*)src->chanbase.first;
00106         outpchan= (bPoseChannel*)out->chanbase.first;
00107         for (; pchan; pchan=pchan->next, outpchan=outpchan->next)
00108                 BLI_ghash_insert(ghash, pchan, outpchan);
00109 
00110         for (pchan=(bPoseChannel*)out->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
00111                 pchan->parent= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->parent);
00112                 pchan->child= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->child);
00113                 pchan->path= NULL;
00114 
00115                 if (copy_constraint) {
00116                         ListBase listb;
00117                         // copy all constraint for backward compatibility
00118                         copy_constraints(&listb, &pchan->constraints, FALSE);  // copy_constraints NULLs listb, no need to make extern for this operation.
00119                         pchan->constraints= listb;
00120                 } else {
00121                         pchan->constraints.first = NULL;
00122                         pchan->constraints.last = NULL;
00123                 }
00124 
00125                 // fails to link, props are not used in the BGE yet.
00126                 /* if(pchan->prop)
00127                         pchan->prop= IDP_CopyProperty(pchan->prop); */
00128                 pchan->prop= NULL;
00129         }
00130 
00131         BLI_ghash_free(ghash, NULL, NULL);
00132         // set acceleration structure for channel lookup
00133         make_pose_channels_hash(out);
00134         *dst=out;
00135 }
00136 
00137 
00138 
00139 /* Only allowed for Poses with identical channels */
00140 void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/)
00141 {
00142         short mode= ACTSTRIPMODE_BLEND;
00143         
00144         bPoseChannel *dchan;
00145         const bPoseChannel *schan;
00146         bConstraint *dcon, *scon;
00147         float dstweight;
00148         int i;
00149 
00150         switch (mode){
00151         case ACTSTRIPMODE_BLEND:
00152                 dstweight = 1.0F - srcweight;
00153                 break;
00154         case ACTSTRIPMODE_ADD:
00155                 dstweight = 1.0F;
00156                 break;
00157         default :
00158                 dstweight = 1.0F;
00159         }
00160         
00161         schan= (bPoseChannel*)src->chanbase.first;
00162         for (dchan = (bPoseChannel*)dst->chanbase.first; dchan; dchan=(bPoseChannel*)dchan->next, schan= (bPoseChannel*)schan->next){
00163                 // always blend on all channels since we don't know which one has been set
00164                 /* quat interpolation done separate */
00165                 if (schan->rotmode == ROT_MODE_QUAT) {
00166                         float dquat[4], squat[4];
00167                         
00168                         QUATCOPY(dquat, dchan->quat);
00169                         QUATCOPY(squat, schan->quat);
00170                         if (mode==ACTSTRIPMODE_BLEND)
00171                                 interp_qt_qtqt(dchan->quat, dquat, squat, srcweight);
00172                         else {
00173                                 mul_fac_qt_fl(squat, srcweight);
00174                                 mul_qt_qtqt(dchan->quat, dquat, squat);
00175                         }
00176                         
00177                         normalize_qt(dchan->quat);
00178                 }
00179 
00180                 for (i=0; i<3; i++) {
00181                         /* blending for loc and scale are pretty self-explanatory... */
00182                         dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight);
00183                         dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight);
00184                         
00185                         /* euler-rotation interpolation done here instead... */
00186                         // FIXME: are these results decent?
00187                         if (schan->rotmode)
00188                                 dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight);
00189                 }
00190                 for(dcon= (bConstraint*)dchan->constraints.first, scon= (bConstraint*)schan->constraints.first; dcon && scon; dcon= (bConstraint*)dcon->next, scon= (bConstraint*)scon->next) {
00191                         /* no 'add' option for constraint blending */
00192                         dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight;
00193                 }
00194         }
00195         
00196         /* this pose is now in src time */
00197         dst->ctime= src->ctime;
00198 }
00199 
00200 void game_free_pose(bPose *pose)
00201 {
00202         if (pose) {
00203                 /* free pose-channels and constraints */
00204                 free_pose_channels(pose);
00205                 
00206                 /* free IK solver state */
00207                 BIK_clear_data(pose);
00208 
00209                 /* free IK solver param */
00210                 if (pose->ikparam)
00211                         MEM_freeN(pose->ikparam);
00212 
00213                 MEM_freeN(pose);
00214         }
00215 }
00216 
00217 BL_ArmatureObject::BL_ArmatureObject(
00218                                 void* sgReplicationInfo, 
00219                                 SG_Callbacks callbacks, 
00220                                 Object *armature,
00221                                 Scene *scene)
00222 
00223 :       KX_GameObject(sgReplicationInfo,callbacks),
00224         m_controlledConstraints(),
00225         m_poseChannels(),
00226         m_objArma(armature),
00227         m_framePose(NULL),
00228         m_scene(scene), // maybe remove later. needed for where_is_pose
00229         m_lastframe(0.0),
00230         m_timestep(0.040),
00231         m_activeAct(NULL),
00232         m_activePriority(999),
00233         m_constraintNumber(0),
00234         m_channelNumber(0),
00235         m_lastapplyframe(0.0)
00236 {
00237         m_armature = (bArmature *)armature->data;
00238 
00239         /* we make a copy of blender object's pose, and then always swap it with
00240          * the original pose before calling into blender functions, to deal with
00241          * replica's or other objects using the same blender object */
00242         m_pose = NULL;
00243         game_copy_pose(&m_pose, m_objArma->pose, 1);
00244         // store the original armature object matrix
00245         memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat));
00246 }
00247 
00248 BL_ArmatureObject::~BL_ArmatureObject()
00249 {
00250         BL_ArmatureConstraint* constraint;
00251         while ((constraint = m_controlledConstraints.Remove()) != NULL) {
00252                 delete constraint;
00253         }
00254         BL_ArmatureChannel* channel;
00255         while ((channel = static_cast<BL_ArmatureChannel*>(m_poseChannels.Remove())) != NULL) {
00256                 delete channel;
00257         }
00258         if (m_pose)
00259                 game_free_pose(m_pose);
00260         if (m_framePose)
00261                 game_free_pose(m_framePose);
00262 }
00263 
00264 
00265 void BL_ArmatureObject::LoadConstraints(KX_BlenderSceneConverter* converter)
00266 {
00267         // first delete any existing constraint (should not have any)
00268         while (!m_controlledConstraints.Empty()) {
00269                 BL_ArmatureConstraint* constraint = m_controlledConstraints.Remove();
00270                 delete constraint;
00271         }
00272         m_constraintNumber = 0;
00273 
00274         // list all the constraint and convert them to BL_ArmatureConstraint
00275         // get the persistent pose structure
00276         bPoseChannel* pchan;
00277         bConstraint* pcon;
00278         bConstraintTypeInfo* cti;
00279         Object* blendtarget;
00280         KX_GameObject* gametarget;
00281         KX_GameObject* gamesubtarget;
00282 
00283         // and locate the constraint
00284         for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
00285                 for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
00286                         if (pcon->flag & CONSTRAINT_DISABLE)
00287                                 continue;
00288                         // which constraint should we support?
00289                         switch (pcon->type) {
00290                         case CONSTRAINT_TYPE_TRACKTO:
00291                         case CONSTRAINT_TYPE_KINEMATIC:
00292                         case CONSTRAINT_TYPE_ROTLIKE:
00293                         case CONSTRAINT_TYPE_LOCLIKE:
00294                         case CONSTRAINT_TYPE_MINMAX:
00295                         case CONSTRAINT_TYPE_SIZELIKE:
00296                         case CONSTRAINT_TYPE_LOCKTRACK:
00297                         case CONSTRAINT_TYPE_STRETCHTO:
00298                         case CONSTRAINT_TYPE_CLAMPTO:
00299                         case CONSTRAINT_TYPE_TRANSFORM:
00300                         case CONSTRAINT_TYPE_DISTLIMIT:
00301                                 cti = constraint_get_typeinfo(pcon);
00302                                 gametarget = gamesubtarget = NULL;
00303                                 if (cti && cti->get_constraint_targets) {
00304                                         ListBase listb = { NULL, NULL };
00305                                         cti->get_constraint_targets(pcon, &listb);
00306                                         if (listb.first) {
00307                                                 bConstraintTarget* target = (bConstraintTarget*)listb.first;
00308                                                 if (target->tar && target->tar != m_objArma) {
00309                                                         // only remember external objects, self target is handled automatically
00310                                                         blendtarget = target->tar;
00311                                                         gametarget = converter->FindGameObject(blendtarget);
00312                                                 }
00313                                                 if (target->next != NULL) {
00314                                                         // secondary target
00315                                                         target = (bConstraintTarget*)target->next;
00316                                                         if (target->tar && target->tar != m_objArma) {
00317                                                                 // only track external object
00318                                                                 blendtarget = target->tar;
00319                                                                 gamesubtarget = converter->FindGameObject(blendtarget);
00320                                                         }
00321                                                 }
00322                                         }
00323                                         if (cti->flush_constraint_targets)
00324                                                 cti->flush_constraint_targets(pcon, &listb, 1);
00325                                 }
00326                                 BL_ArmatureConstraint* constraint = new BL_ArmatureConstraint(this, pchan, pcon, gametarget, gamesubtarget);
00327                                 m_controlledConstraints.AddBack(constraint);
00328                                 m_constraintNumber++;
00329                         }
00330                 }
00331         }
00332 }
00333 
00334 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannel, const char* constraintname)
00335 {
00336         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00337         for (cit.begin(); !cit.end(); ++cit) {
00338                 BL_ArmatureConstraint* constraint = *cit;
00339                 if (constraint->Match(posechannel, constraintname))
00340                         return constraint;
00341         }
00342         return NULL;
00343 }
00344 
00345 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannelconstraint)
00346 {
00347         // performance: use hash string instead of plain string compare
00348         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00349         for (cit.begin(); !cit.end(); ++cit) {
00350                 BL_ArmatureConstraint* constraint = *cit;
00351                 if (!strcmp(constraint->GetName(), posechannelconstraint))
00352                         return constraint;
00353         }
00354         return NULL;
00355 }
00356 
00357 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(int index)
00358 {
00359         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00360         for (cit.begin(); !cit.end() && index; ++cit, --index);
00361         return (cit.end()) ? NULL : *cit;
00362 }
00363 
00364 /* this function is called to populate the m_poseChannels list */
00365 void BL_ArmatureObject::LoadChannels()
00366 {
00367         if (m_poseChannels.Empty()) {
00368                 bPoseChannel* pchan;
00369                 BL_ArmatureChannel* proxy;
00370         
00371                 m_channelNumber = 0;
00372                 for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
00373                         proxy = new BL_ArmatureChannel(this, pchan);
00374                         m_poseChannels.AddBack(proxy);
00375                         m_channelNumber++;
00376                 }
00377         }
00378 }
00379 
00380 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(bPoseChannel* pchan)
00381 {
00382         LoadChannels();
00383         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
00384         for (cit.begin(); !cit.end(); ++cit) 
00385         {
00386                 BL_ArmatureChannel* channel = *cit;
00387                 if (channel->m_posechannel == pchan)
00388                         return channel;
00389         }
00390         return NULL;
00391 }
00392 
00393 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(const char* str)
00394 {
00395         LoadChannels();
00396         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
00397         for (cit.begin(); !cit.end(); ++cit) 
00398         {
00399                 BL_ArmatureChannel* channel = *cit;
00400                 if (!strcmp(channel->m_posechannel->name, str))
00401                         return channel;
00402         }
00403         return NULL;
00404 }
00405 
00406 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(int index)
00407 {
00408         LoadChannels();
00409         if (index < 0 || index >= m_channelNumber)
00410                 return NULL;
00411         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
00412         for (cit.begin(); !cit.end() && index; ++cit, --index);
00413         return (cit.end()) ? NULL : *cit;
00414 }
00415 
00416 CValue* BL_ArmatureObject::GetReplica()
00417 {
00418         BL_ArmatureObject* replica = new BL_ArmatureObject(*this);
00419         replica->ProcessReplica();
00420         return replica;
00421 }
00422 
00423 void BL_ArmatureObject::ProcessReplica()
00424 {
00425         bPose *pose= m_pose;
00426         KX_GameObject::ProcessReplica();
00427 
00428         m_pose = NULL;
00429         m_framePose = NULL;
00430         game_copy_pose(&m_pose, pose, 1);       
00431 }
00432 
00433 void BL_ArmatureObject::ReParentLogic()
00434 {
00435         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00436         for (cit.begin(); !cit.end(); ++cit) {
00437                 (*cit)->ReParent(this);
00438         }
00439         KX_GameObject::ReParentLogic();
00440 }
00441 
00442 void BL_ArmatureObject::Relink(CTR_Map<CTR_HashedPtr, void*> *obj_map)
00443 {
00444         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00445         for (cit.begin(); !cit.end(); ++cit) {
00446                 (*cit)->Relink(obj_map);
00447         }
00448         KX_GameObject::Relink(obj_map);
00449 }
00450 
00451 bool BL_ArmatureObject::UnlinkObject(SCA_IObject* clientobj)
00452 {
00453         // clientobj is being deleted, make sure we don't hold any reference to it
00454         bool res = false;
00455         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00456         for (cit.begin(); !cit.end(); ++cit) {
00457                 res |= (*cit)->UnlinkObject(clientobj);
00458         }
00459         return res;
00460 }
00461 
00462 void BL_ArmatureObject::ApplyPose()
00463 {
00464         m_armpose = m_objArma->pose;
00465         m_objArma->pose = m_pose;
00466         // in the GE, we use ctime to store the timestep
00467         m_pose->ctime = (float)m_timestep;
00468         //m_scene->r.cfra++;
00469         if(m_lastapplyframe != m_lastframe) {
00470                 // update the constraint if any, first put them all off so that only the active ones will be updated
00471                 SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
00472                 for (cit.begin(); !cit.end(); ++cit) {
00473                         (*cit)->UpdateTarget();
00474                 }
00475                 // update ourself
00476                 UpdateBlenderObjectMatrix(m_objArma);
00477                 where_is_pose(m_scene, m_objArma); // XXX
00478                 // restore ourself
00479                 memcpy(m_objArma->obmat, m_obmat, sizeof(m_obmat));
00480                 // restore active targets
00481                 for (cit.begin(); !cit.end(); ++cit) {
00482                         (*cit)->RestoreTarget();
00483                 }
00484                 m_lastapplyframe = m_lastframe;
00485         }
00486 }
00487 
00488 void BL_ArmatureObject::RestorePose()
00489 {
00490         m_objArma->pose = m_armpose;
00491         m_armpose = NULL;
00492 }
00493 
00494 void BL_ArmatureObject::SetPose(bPose *pose)
00495 {
00496         extract_pose_from_pose(m_pose, pose);
00497         m_lastapplyframe = -1.0;
00498 }
00499 
00500 bool BL_ArmatureObject::SetActiveAction(BL_ActionActuator *act, short priority, double curtime)
00501 {
00502         if (curtime != m_lastframe){
00503                 m_activePriority = 9999;
00504                 // compute the timestep for the underlying IK algorithm
00505                 m_timestep = curtime-m_lastframe;
00506                 m_lastframe= curtime;
00507                 m_activeAct = NULL;
00508                 // remember the pose at the start of the frame
00509                 GetPose(&m_framePose);
00510         }
00511 
00512         if (act) 
00513         {
00514                 if (priority<=m_activePriority)
00515                 {
00516                         if (priority<m_activePriority) {
00517                                 // this action overwrites the previous ones, start from initial pose to cancel their effects
00518                                 SetPose(m_framePose);
00519                                 if (m_activeAct && (m_activeAct!=act))
00520                                         /* Reset the blend timer since this new action cancels the old one */
00521                                         m_activeAct->SetBlendTime(0.0); 
00522                         }
00523                         m_activeAct = act;
00524                         m_activePriority = priority;
00525                         m_lastframe = curtime;
00526                 
00527                         return true;
00528                 }
00529                 else{
00530                         act->SetBlendTime(0.0);
00531                         return false;
00532                 }
00533         }
00534         return false;
00535 }
00536 
00537 BL_ActionActuator * BL_ArmatureObject::GetActiveAction()
00538 {
00539         return m_activeAct;
00540 }
00541 
00542 void BL_ArmatureObject::GetPose(bPose **pose)
00543 {
00544         /* If the caller supplies a null pose, create a new one. */
00545         /* Otherwise, copy the armature's pose channels into the caller-supplied pose */
00546                 
00547         if (!*pose) {
00548                 /*      probably not to good of an idea to
00549                         duplicate everying, but it clears up 
00550                         a crash and memory leakage when 
00551                         &BL_ActionActuator::m_pose is freed
00552                 */
00553                 game_copy_pose(pose, m_pose, 0);
00554         }
00555         else {
00556                 if (*pose == m_pose)
00557                         // no need to copy if the pointers are the same
00558                         return;
00559 
00560                 extract_pose_from_pose(*pose, m_pose);
00561         }
00562 }
00563 
00564 void BL_ArmatureObject::GetMRDPose(bPose **pose)
00565 {
00566         /* If the caller supplies a null pose, create a new one. */
00567         /* Otherwise, copy the armature's pose channels into the caller-supplied pose */
00568 
00569         if (!*pose)
00570                 game_copy_pose(pose, m_pose, 0);
00571         else
00572                 extract_pose_from_pose(*pose, m_pose);
00573 }
00574 
00575 short BL_ArmatureObject::GetActivePriority()
00576 {
00577         return m_activePriority;
00578 }
00579 
00580 double BL_ArmatureObject::GetLastFrame()
00581 {
00582         return m_lastframe;
00583 }
00584 
00585 bool BL_ArmatureObject::GetBoneMatrix(Bone* bone, MT_Matrix4x4& matrix)
00586 {
00587         bPoseChannel *pchan;
00588 
00589         ApplyPose();
00590         pchan = get_pose_channel(m_objArma->pose, bone->name);
00591         if(pchan)
00592                 matrix.setValue(&pchan->pose_mat[0][0]);
00593         RestorePose();
00594 
00595         return (pchan != NULL);
00596 }
00597 
00598 float BL_ArmatureObject::GetBoneLength(Bone* bone) const
00599 {
00600         return (float)(MT_Point3(bone->head) - MT_Point3(bone->tail)).length();
00601 }
00602 
00603 #ifdef WITH_PYTHON
00604 
00605 // PYTHON
00606 
00607 PyTypeObject BL_ArmatureObject::Type = {
00608         PyVarObject_HEAD_INIT(NULL, 0)
00609         "BL_ArmatureObject",
00610         sizeof(PyObjectPlus_Proxy),
00611         0,
00612         py_base_dealloc,
00613         0,
00614         0,
00615         0,
00616         0,
00617         py_base_repr,
00618         0,
00619         &KX_GameObject::Sequence,
00620         &KX_GameObject::Mapping,
00621         0,0,0,
00622         NULL,
00623         NULL,
00624         0,
00625         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00626         0,0,0,0,0,0,0,
00627         Methods,
00628         0,
00629         0,
00630         &KX_GameObject::Type,
00631         0,0,0,0,0,0,
00632         py_base_new
00633 };
00634 
00635 PyMethodDef BL_ArmatureObject::Methods[] = {
00636 
00637         KX_PYMETHODTABLE_NOARGS(BL_ArmatureObject, update),
00638         {NULL,NULL} //Sentinel
00639 };
00640 
00641 PyAttributeDef BL_ArmatureObject::Attributes[] = {
00642 
00643         KX_PYATTRIBUTE_RO_FUNCTION("constraints",               BL_ArmatureObject, pyattr_get_constraints),
00644         KX_PYATTRIBUTE_RO_FUNCTION("channels",          BL_ArmatureObject, pyattr_get_channels),
00645         {NULL} //Sentinel
00646 };
00647 
00648 PyObject* BL_ArmatureObject::pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00649 {
00650         return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CONSTRAINTS);
00651 }
00652 
00653 PyObject* BL_ArmatureObject::pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00654 {
00655         BL_ArmatureObject* self = static_cast<BL_ArmatureObject*>(self_v);
00656         self->LoadChannels(); // make sure we have the channels
00657         return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CHANNELS);
00658 }
00659 
00660 KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update, 
00661                                                   "update()\n"
00662                                                   "Make sure that the armature will be updated on next graphic frame.\n"
00663                                                   "This is automatically done if a KX_ArmatureActuator with mode run is active\n"
00664                                                   "or if an action is playing. This function is usefull in other cases.\n")
00665 {
00666         SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime());
00667         Py_RETURN_NONE;
00668 }
00669 
00670 #endif // WITH_PYTHON