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