|
Blender
V2.59
|
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