svn merge -r39781:39792 https://svn.blender.org/svnroot/bf-blender/trunk/blender...
[blender-staging.git] / source / gameengine / Converter / BL_ArmatureObject.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 /** \file gameengine/Converter/BL_ArmatureObject.cpp
31  *  \ingroup bgeconv
32  */
33
34
35 #include "BL_ArmatureObject.h"
36 #include "BL_ActionActuator.h"
37 #include "KX_BlenderSceneConverter.h"
38 #include "MEM_guardedalloc.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h"
41 #include "BLI_utildefines.h"
42 #include "BLI_ghash.h"
43 #include "BIK_api.h"
44 #include "BKE_action.h"
45 #include "BKE_armature.h"
46
47 #include "BKE_constraint.h"
48 #include "CTR_Map.h"
49 #include "CTR_HashedPtr.h"
50 #include "MEM_guardedalloc.h"
51 #include "DNA_action_types.h"
52 #include "DNA_armature_types.h"
53 #include "DNA_object_types.h"
54 #include "DNA_scene_types.h"
55 #include "DNA_nla_types.h"
56 #include "DNA_constraint_types.h"
57 #include "KX_PythonSeq.h"
58 #include "KX_PythonInit.h"
59 #include "KX_KetsjiEngine.h"
60
61 #include "MT_Matrix4x4.h"
62
63 /** 
64  * Move here pose function for game engine so that we can mix with GE objects
65  * Principle is as follow:
66  * Use Blender structures so that where_is_pose can be used unchanged
67  * Copy the constraint so that they can be enabled/disabled/added/removed at runtime
68  * Don't copy the constraints for the pose used by the Action actuator, it does not need them.
69  * Scan the constraint structures so that the KX equivalent of target objects are identified and 
70  * stored in separate list.
71  * When it is about to evaluate the pose, set the KX object position in the obmat of the corresponding
72  * Blender objects and restore after the evaluation.
73  */
74 void game_copy_pose(bPose **dst, bPose *src, int copy_constraint)
75 {
76         bPose *out;
77         bPoseChannel *pchan, *outpchan;
78         GHash *ghash;
79         
80         /* the game engine copies the current armature pose and then swaps
81          * the object pose pointer. this makes it possible to change poses
82          * without affecting the original blender data. */
83
84         if (!src) {
85                 *dst=NULL;
86                 return;
87         }
88         else if (*dst==src) {
89                 printf("copy_pose source and target are the same\n");
90                 *dst=NULL;
91                 return;
92         }
93         
94         out= (bPose*)MEM_dupallocN(src);
95         out->chanhash = NULL;
96         out->agroups.first= out->agroups.last= NULL;
97         out->ikdata = NULL;
98         out->ikparam = MEM_dupallocN(out->ikparam);
99         out->flag |= POSE_GAME_ENGINE;
100         BLI_duplicatelist(&out->chanbase, &src->chanbase);
101
102         /* remap pointers */
103         ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "game_copy_pose gh");
104
105         pchan= (bPoseChannel*)src->chanbase.first;
106         outpchan= (bPoseChannel*)out->chanbase.first;
107         for (; pchan; pchan=pchan->next, outpchan=outpchan->next)
108                 BLI_ghash_insert(ghash, pchan, outpchan);
109
110         for (pchan=(bPoseChannel*)out->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
111                 pchan->parent= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->parent);
112                 pchan->child= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->child);
113                 pchan->path= NULL;
114
115                 if (copy_constraint) {
116                         ListBase listb;
117                         // copy all constraint for backward compatibility
118                         copy_constraints(&listb, &pchan->constraints, FALSE);  // copy_constraints NULLs listb, no need to make extern for this operation.
119                         pchan->constraints= listb;
120                 } else {
121                         pchan->constraints.first = NULL;
122                         pchan->constraints.last = NULL;
123                 }
124
125                 // fails to link, props are not used in the BGE yet.
126                 /* if(pchan->prop)
127                         pchan->prop= IDP_CopyProperty(pchan->prop); */
128                 pchan->prop= NULL;
129         }
130
131         BLI_ghash_free(ghash, NULL, NULL);
132         // set acceleration structure for channel lookup
133         make_pose_channels_hash(out);
134         *dst=out;
135 }
136
137
138
139 /* Only allowed for Poses with identical channels */
140 void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/)
141 {
142         short mode= ACTSTRIPMODE_BLEND;
143         
144         bPoseChannel *dchan;
145         const bPoseChannel *schan;
146         bConstraint *dcon, *scon;
147         float dstweight;
148         int i;
149
150         switch (mode){
151         case ACTSTRIPMODE_BLEND:
152                 dstweight = 1.0F - srcweight;
153                 break;
154         case ACTSTRIPMODE_ADD:
155                 dstweight = 1.0F;
156                 break;
157         default :
158                 dstweight = 1.0F;
159         }
160         
161         schan= (bPoseChannel*)src->chanbase.first;
162         for (dchan = (bPoseChannel*)dst->chanbase.first; dchan; dchan=(bPoseChannel*)dchan->next, schan= (bPoseChannel*)schan->next){
163                 // always blend on all channels since we don't know which one has been set
164                 /* quat interpolation done separate */
165                 if (schan->rotmode == ROT_MODE_QUAT) {
166                         float dquat[4], squat[4];
167                         
168                         QUATCOPY(dquat, dchan->quat);
169                         QUATCOPY(squat, schan->quat);
170                         if (mode==ACTSTRIPMODE_BLEND)
171                                 interp_qt_qtqt(dchan->quat, dquat, squat, srcweight);
172                         else {
173                                 mul_fac_qt_fl(squat, srcweight);
174                                 mul_qt_qtqt(dchan->quat, dquat, squat);
175                         }
176                         
177                         normalize_qt(dchan->quat);
178                 }
179
180                 for (i=0; i<3; i++) {
181                         /* blending for loc and scale are pretty self-explanatory... */
182                         dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight);
183                         dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight);
184                         
185                         /* euler-rotation interpolation done here instead... */
186                         // FIXME: are these results decent?
187                         if (schan->rotmode)
188                                 dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight);
189                 }
190                 for(dcon= (bConstraint*)dchan->constraints.first, scon= (bConstraint*)schan->constraints.first; dcon && scon; dcon= (bConstraint*)dcon->next, scon= (bConstraint*)scon->next) {
191                         /* no 'add' option for constraint blending */
192                         dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight;
193                 }
194         }
195         
196         /* this pose is now in src time */
197         dst->ctime= src->ctime;
198 }
199
200 void game_free_pose(bPose *pose)
201 {
202         if (pose) {
203                 /* free pose-channels and constraints */
204                 free_pose_channels(pose);
205                 
206                 /* free IK solver state */
207                 BIK_clear_data(pose);
208
209                 /* free IK solver param */
210                 if (pose->ikparam)
211                         MEM_freeN(pose->ikparam);
212
213                 MEM_freeN(pose);
214         }
215 }
216
217 BL_ArmatureObject::BL_ArmatureObject(
218                                 void* sgReplicationInfo, 
219                                 SG_Callbacks callbacks, 
220                                 Object *armature,
221                                 Scene *scene,
222                                 int vert_deform_type)
223
224 :       KX_GameObject(sgReplicationInfo,callbacks),
225         m_controlledConstraints(),
226         m_poseChannels(),
227         m_objArma(armature),
228         m_framePose(NULL),
229         m_scene(scene), // maybe remove later. needed for where_is_pose
230         m_lastframe(0.0),
231         m_timestep(0.040),
232         m_activeAct(NULL),
233         m_activePriority(999),
234         m_constraintNumber(0),
235         m_channelNumber(0),
236         m_lastapplyframe(0.0),
237         m_vert_deform_type(vert_deform_type)
238 {
239         m_armature = (bArmature *)armature->data;
240
241         /* we make a copy of blender object's pose, and then always swap it with
242          * the original pose before calling into blender functions, to deal with
243          * replica's or other objects using the same blender object */
244         m_pose = NULL;
245         game_copy_pose(&m_pose, m_objArma->pose, 1);
246         // store the original armature object matrix
247         memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat));
248 }
249
250 BL_ArmatureObject::~BL_ArmatureObject()
251 {
252         BL_ArmatureConstraint* constraint;
253         while ((constraint = m_controlledConstraints.Remove()) != NULL) {
254                 delete constraint;
255         }
256         BL_ArmatureChannel* channel;
257         while ((channel = static_cast<BL_ArmatureChannel*>(m_poseChannels.Remove())) != NULL) {
258                 delete channel;
259         }
260         if (m_pose)
261                 game_free_pose(m_pose);
262         if (m_framePose)
263                 game_free_pose(m_framePose);
264 }
265
266
267 void BL_ArmatureObject::LoadConstraints(KX_BlenderSceneConverter* converter)
268 {
269         // first delete any existing constraint (should not have any)
270         while (!m_controlledConstraints.Empty()) {
271                 BL_ArmatureConstraint* constraint = m_controlledConstraints.Remove();
272                 delete constraint;
273         }
274         m_constraintNumber = 0;
275
276         // list all the constraint and convert them to BL_ArmatureConstraint
277         // get the persistent pose structure
278         bPoseChannel* pchan;
279         bConstraint* pcon;
280         bConstraintTypeInfo* cti;
281         Object* blendtarget;
282         KX_GameObject* gametarget;
283         KX_GameObject* gamesubtarget;
284
285         // and locate the constraint
286         for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
287                 for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
288                         if (pcon->flag & CONSTRAINT_DISABLE)
289                                 continue;
290                         // which constraint should we support?
291                         switch (pcon->type) {
292                         case CONSTRAINT_TYPE_TRACKTO:
293                         case CONSTRAINT_TYPE_KINEMATIC:
294                         case CONSTRAINT_TYPE_ROTLIKE:
295                         case CONSTRAINT_TYPE_LOCLIKE:
296                         case CONSTRAINT_TYPE_MINMAX:
297                         case CONSTRAINT_TYPE_SIZELIKE:
298                         case CONSTRAINT_TYPE_LOCKTRACK:
299                         case CONSTRAINT_TYPE_STRETCHTO:
300                         case CONSTRAINT_TYPE_CLAMPTO:
301                         case CONSTRAINT_TYPE_TRANSFORM:
302                         case CONSTRAINT_TYPE_DISTLIMIT:
303                         case CONSTRAINT_TYPE_TRANSLIKE:
304                                 cti = constraint_get_typeinfo(pcon);
305                                 gametarget = gamesubtarget = NULL;
306                                 if (cti && cti->get_constraint_targets) {
307                                         ListBase listb = { NULL, NULL };
308                                         cti->get_constraint_targets(pcon, &listb);
309                                         if (listb.first) {
310                                                 bConstraintTarget* target = (bConstraintTarget*)listb.first;
311                                                 if (target->tar && target->tar != m_objArma) {
312                                                         // only remember external objects, self target is handled automatically
313                                                         blendtarget = target->tar;
314                                                         gametarget = converter->FindGameObject(blendtarget);
315                                                 }
316                                                 if (target->next != NULL) {
317                                                         // secondary target
318                                                         target = (bConstraintTarget*)target->next;
319                                                         if (target->tar && target->tar != m_objArma) {
320                                                                 // only track external object
321                                                                 blendtarget = target->tar;
322                                                                 gamesubtarget = converter->FindGameObject(blendtarget);
323                                                         }
324                                                 }
325                                         }
326                                         if (cti->flush_constraint_targets)
327                                                 cti->flush_constraint_targets(pcon, &listb, 1);
328                                 }
329                                 BL_ArmatureConstraint* constraint = new BL_ArmatureConstraint(this, pchan, pcon, gametarget, gamesubtarget);
330                                 m_controlledConstraints.AddBack(constraint);
331                                 m_constraintNumber++;
332                         }
333                 }
334         }
335 }
336
337 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannel, const char* constraintname)
338 {
339         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
340         for (cit.begin(); !cit.end(); ++cit) {
341                 BL_ArmatureConstraint* constraint = *cit;
342                 if (constraint->Match(posechannel, constraintname))
343                         return constraint;
344         }
345         return NULL;
346 }
347
348 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannelconstraint)
349 {
350         // performance: use hash string instead of plain string compare
351         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
352         for (cit.begin(); !cit.end(); ++cit) {
353                 BL_ArmatureConstraint* constraint = *cit;
354                 if (!strcmp(constraint->GetName(), posechannelconstraint))
355                         return constraint;
356         }
357         return NULL;
358 }
359
360 BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(int index)
361 {
362         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
363         for (cit.begin(); !cit.end() && index; ++cit, --index);
364         return (cit.end()) ? NULL : *cit;
365 }
366
367 /* this function is called to populate the m_poseChannels list */
368 void BL_ArmatureObject::LoadChannels()
369 {
370         if (m_poseChannels.Empty()) {
371                 bPoseChannel* pchan;
372                 BL_ArmatureChannel* proxy;
373         
374                 m_channelNumber = 0;
375                 for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
376                         proxy = new BL_ArmatureChannel(this, pchan);
377                         m_poseChannels.AddBack(proxy);
378                         m_channelNumber++;
379                 }
380         }
381 }
382
383 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(bPoseChannel* pchan)
384 {
385         LoadChannels();
386         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
387         for (cit.begin(); !cit.end(); ++cit) 
388         {
389                 BL_ArmatureChannel* channel = *cit;
390                 if (channel->m_posechannel == pchan)
391                         return channel;
392         }
393         return NULL;
394 }
395
396 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(const char* str)
397 {
398         LoadChannels();
399         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
400         for (cit.begin(); !cit.end(); ++cit) 
401         {
402                 BL_ArmatureChannel* channel = *cit;
403                 if (!strcmp(channel->m_posechannel->name, str))
404                         return channel;
405         }
406         return NULL;
407 }
408
409 BL_ArmatureChannel* BL_ArmatureObject::GetChannel(int index)
410 {
411         LoadChannels();
412         if (index < 0 || index >= m_channelNumber)
413                 return NULL;
414         SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
415         for (cit.begin(); !cit.end() && index; ++cit, --index);
416         return (cit.end()) ? NULL : *cit;
417 }
418
419 CValue* BL_ArmatureObject::GetReplica()
420 {
421         BL_ArmatureObject* replica = new BL_ArmatureObject(*this);
422         replica->ProcessReplica();
423         return replica;
424 }
425
426 void BL_ArmatureObject::ProcessReplica()
427 {
428         bPose *pose= m_pose;
429         KX_GameObject::ProcessReplica();
430
431         m_pose = NULL;
432         m_framePose = NULL;
433         game_copy_pose(&m_pose, pose, 1);       
434 }
435
436 void BL_ArmatureObject::ReParentLogic()
437 {
438         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
439         for (cit.begin(); !cit.end(); ++cit) {
440                 (*cit)->ReParent(this);
441         }
442         KX_GameObject::ReParentLogic();
443 }
444
445 void BL_ArmatureObject::Relink(CTR_Map<CTR_HashedPtr, void*> *obj_map)
446 {
447         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
448         for (cit.begin(); !cit.end(); ++cit) {
449                 (*cit)->Relink(obj_map);
450         }
451         KX_GameObject::Relink(obj_map);
452 }
453
454 bool BL_ArmatureObject::UnlinkObject(SCA_IObject* clientobj)
455 {
456         // clientobj is being deleted, make sure we don't hold any reference to it
457         bool res = false;
458         SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
459         for (cit.begin(); !cit.end(); ++cit) {
460                 res |= (*cit)->UnlinkObject(clientobj);
461         }
462         return res;
463 }
464
465 void BL_ArmatureObject::ApplyPose()
466 {
467         m_armpose = m_objArma->pose;
468         m_objArma->pose = m_pose;
469         // in the GE, we use ctime to store the timestep
470         m_pose->ctime = (float)m_timestep;
471         //m_scene->r.cfra++;
472         if(m_lastapplyframe != m_lastframe) {
473                 // update the constraint if any, first put them all off so that only the active ones will be updated
474                 SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
475                 for (cit.begin(); !cit.end(); ++cit) {
476                         (*cit)->UpdateTarget();
477                 }
478                 // update ourself
479                 UpdateBlenderObjectMatrix(m_objArma);
480                 where_is_pose(m_scene, m_objArma); // XXX
481                 // restore ourself
482                 memcpy(m_objArma->obmat, m_obmat, sizeof(m_obmat));
483                 // restore active targets
484                 for (cit.begin(); !cit.end(); ++cit) {
485                         (*cit)->RestoreTarget();
486                 }
487                 m_lastapplyframe = m_lastframe;
488         }
489 }
490
491 void BL_ArmatureObject::RestorePose()
492 {
493         m_objArma->pose = m_armpose;
494         m_armpose = NULL;
495 }
496
497 void BL_ArmatureObject::SetPose(bPose *pose)
498 {
499         extract_pose_from_pose(m_pose, pose);
500         m_lastapplyframe = -1.0;
501 }
502
503 bool BL_ArmatureObject::SetActiveAction(BL_ActionActuator *act, short priority, double curtime)
504 {
505         if (curtime != m_lastframe){
506                 m_activePriority = 9999;
507                 // compute the timestep for the underlying IK algorithm
508                 m_timestep = curtime-m_lastframe;
509                 m_lastframe= curtime;
510                 m_activeAct = NULL;
511                 // remember the pose at the start of the frame
512                 GetPose(&m_framePose);
513         }
514
515         if (act) 
516         {
517                 if (priority<=m_activePriority)
518                 {
519                         if (priority<m_activePriority) {
520                                 // this action overwrites the previous ones, start from initial pose to cancel their effects
521                                 SetPose(m_framePose);
522                                 if (m_activeAct && (m_activeAct!=act))
523                                         /* Reset the blend timer since this new action cancels the old one */
524                                         m_activeAct->SetBlendTime(0.0); 
525                         }
526                         m_activeAct = act;
527                         m_activePriority = priority;
528                         m_lastframe = curtime;
529                 
530                         return true;
531                 }
532                 else{
533                         act->SetBlendTime(0.0);
534                         return false;
535                 }
536         }
537         return false;
538 }
539
540 BL_ActionActuator * BL_ArmatureObject::GetActiveAction()
541 {
542         return m_activeAct;
543 }
544
545 void BL_ArmatureObject::GetPose(bPose **pose)
546 {
547         /* If the caller supplies a null pose, create a new one. */
548         /* Otherwise, copy the armature's pose channels into the caller-supplied pose */
549                 
550         if (!*pose) {
551                 /*      probably not to good of an idea to
552                         duplicate everying, but it clears up 
553                         a crash and memory leakage when 
554                         &BL_ActionActuator::m_pose is freed
555                 */
556                 game_copy_pose(pose, m_pose, 0);
557         }
558         else {
559                 if (*pose == m_pose)
560                         // no need to copy if the pointers are the same
561                         return;
562
563                 extract_pose_from_pose(*pose, m_pose);
564         }
565 }
566
567 void BL_ArmatureObject::GetMRDPose(bPose **pose)
568 {
569         /* If the caller supplies a null pose, create a new one. */
570         /* Otherwise, copy the armature's pose channels into the caller-supplied pose */
571
572         if (!*pose)
573                 game_copy_pose(pose, m_pose, 0);
574         else
575                 extract_pose_from_pose(*pose, m_pose);
576 }
577
578 short BL_ArmatureObject::GetActivePriority()
579 {
580         return m_activePriority;
581 }
582
583 double BL_ArmatureObject::GetLastFrame()
584 {
585         return m_lastframe;
586 }
587
588 bool BL_ArmatureObject::GetBoneMatrix(Bone* bone, MT_Matrix4x4& matrix)
589 {
590         bPoseChannel *pchan;
591
592         ApplyPose();
593         pchan = get_pose_channel(m_objArma->pose, bone->name);
594         if(pchan)
595                 matrix.setValue(&pchan->pose_mat[0][0]);
596         RestorePose();
597
598         return (pchan != NULL);
599 }
600
601 float BL_ArmatureObject::GetBoneLength(Bone* bone) const
602 {
603         return (float)(MT_Point3(bone->head) - MT_Point3(bone->tail)).length();
604 }
605
606 #ifdef WITH_PYTHON
607
608 // PYTHON
609
610 PyTypeObject BL_ArmatureObject::Type = {
611         PyVarObject_HEAD_INIT(NULL, 0)
612         "BL_ArmatureObject",
613         sizeof(PyObjectPlus_Proxy),
614         0,
615         py_base_dealloc,
616         0,
617         0,
618         0,
619         0,
620         py_base_repr,
621         0,
622         &KX_GameObject::Sequence,
623         &KX_GameObject::Mapping,
624         0,0,0,
625         NULL,
626         NULL,
627         0,
628         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
629         0,0,0,0,0,0,0,
630         Methods,
631         0,
632         0,
633         &KX_GameObject::Type,
634         0,0,0,0,0,0,
635         py_base_new
636 };
637
638 PyMethodDef BL_ArmatureObject::Methods[] = {
639
640         KX_PYMETHODTABLE_NOARGS(BL_ArmatureObject, update),
641         {NULL,NULL} //Sentinel
642 };
643
644 PyAttributeDef BL_ArmatureObject::Attributes[] = {
645
646         KX_PYATTRIBUTE_RO_FUNCTION("constraints",               BL_ArmatureObject, pyattr_get_constraints),
647         KX_PYATTRIBUTE_RO_FUNCTION("channels",          BL_ArmatureObject, pyattr_get_channels),
648         {NULL} //Sentinel
649 };
650
651 PyObject* BL_ArmatureObject::pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
652 {
653         return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CONSTRAINTS);
654 }
655
656 PyObject* BL_ArmatureObject::pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
657 {
658         BL_ArmatureObject* self = static_cast<BL_ArmatureObject*>(self_v);
659         self->LoadChannels(); // make sure we have the channels
660         return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CHANNELS);
661 }
662
663 KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update, 
664                                                   "update()\n"
665                                                   "Make sure that the armature will be updated on next graphic frame.\n"
666                                                   "This is automatically done if a KX_ArmatureActuator with mode run is active\n"
667                                                   "or if an action is playing. This function is usefull in other cases.\n")
668 {
669         SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime());
670         Py_RETURN_NONE;
671 }
672
673 #endif // WITH_PYTHON