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