Blender V2.61 - r43446
|
00001 /* 00002 * ***** BEGIN GPL LICENSE BLOCK ***** 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 * 00018 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00019 * All rights reserved. 00020 * 00021 * The Original Code is: all of this file. 00022 * 00023 * Contributor(s): none yet. 00024 * 00025 * ***** END GPL LICENSE BLOCK ***** 00026 */ 00027 00033 #include "BL_ArmatureObject.h" 00034 #include "BL_ActionActuator.h" 00035 #include "KX_BlenderSceneConverter.h" 00036 #include "MEM_guardedalloc.h" 00037 #include "BLI_blenlib.h" 00038 #include "BLI_math.h" 00039 #include "BLI_utildefines.h" 00040 #include "BLI_ghash.h" 00041 #include "BIK_api.h" 00042 #include "BKE_action.h" 00043 #include "BKE_armature.h" 00044 00045 #include "BKE_constraint.h" 00046 #include "CTR_Map.h" 00047 #include "CTR_HashedPtr.h" 00048 #include "MEM_guardedalloc.h" 00049 #include "DNA_action_types.h" 00050 #include "DNA_armature_types.h" 00051 #include "DNA_object_types.h" 00052 #include "DNA_scene_types.h" 00053 #include "DNA_nla_types.h" 00054 #include "DNA_constraint_types.h" 00055 #include "KX_PythonSeq.h" 00056 #include "KX_PythonInit.h" 00057 #include "KX_KetsjiEngine.h" 00058 00059 #include "MT_Matrix4x4.h" 00060 00072 void game_copy_pose(bPose **dst, bPose *src, int copy_constraint) 00073 { 00074 bPose *out; 00075 bPoseChannel *pchan, *outpchan; 00076 GHash *ghash; 00077 00078 /* the game engine copies the current armature pose and then swaps 00079 * the object pose pointer. this makes it possible to change poses 00080 * without affecting the original blender data. */ 00081 00082 if (!src) { 00083 *dst=NULL; 00084 return; 00085 } 00086 else if (*dst==src) { 00087 printf("copy_pose source and target are the same\n"); 00088 *dst=NULL; 00089 return; 00090 } 00091 00092 out= (bPose*)MEM_dupallocN(src); 00093 out->chanhash = NULL; 00094 out->agroups.first= out->agroups.last= NULL; 00095 out->ikdata = NULL; 00096 out->ikparam = MEM_dupallocN(out->ikparam); 00097 out->flag |= POSE_GAME_ENGINE; 00098 BLI_duplicatelist(&out->chanbase, &src->chanbase); 00099 00100 /* remap pointers */ 00101 ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "game_copy_pose gh"); 00102 00103 pchan= (bPoseChannel*)src->chanbase.first; 00104 outpchan= (bPoseChannel*)out->chanbase.first; 00105 for (; pchan; pchan=pchan->next, outpchan=outpchan->next) 00106 BLI_ghash_insert(ghash, pchan, outpchan); 00107 00108 for (pchan=(bPoseChannel*)out->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) { 00109 pchan->parent= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->parent); 00110 pchan->child= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->child); 00111 00112 if (copy_constraint) { 00113 ListBase listb; 00114 // copy all constraint for backward compatibility 00115 copy_constraints(&listb, &pchan->constraints, FALSE); // copy_constraints NULLs listb, no need to make extern for this operation. 00116 pchan->constraints= listb; 00117 } else { 00118 pchan->constraints.first = NULL; 00119 pchan->constraints.last = NULL; 00120 } 00121 00122 // fails to link, props are not used in the BGE yet. 00123 /* if(pchan->prop) 00124 pchan->prop= IDP_CopyProperty(pchan->prop); */ 00125 pchan->prop= NULL; 00126 } 00127 00128 BLI_ghash_free(ghash, NULL, NULL); 00129 // set acceleration structure for channel lookup 00130 make_pose_channels_hash(out); 00131 *dst=out; 00132 } 00133 00134 00135 00136 /* Only allowed for Poses with identical channels */ 00137 void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/) 00138 { 00139 short mode= ACTSTRIPMODE_BLEND; 00140 00141 bPoseChannel *dchan; 00142 const bPoseChannel *schan; 00143 bConstraint *dcon, *scon; 00144 float dstweight; 00145 int i; 00146 00147 switch (mode){ 00148 case ACTSTRIPMODE_BLEND: 00149 dstweight = 1.0F - srcweight; 00150 break; 00151 case ACTSTRIPMODE_ADD: 00152 dstweight = 1.0F; 00153 break; 00154 default : 00155 dstweight = 1.0F; 00156 } 00157 00158 schan= (bPoseChannel*)src->chanbase.first; 00159 for (dchan = (bPoseChannel*)dst->chanbase.first; dchan; dchan=(bPoseChannel*)dchan->next, schan= (bPoseChannel*)schan->next){ 00160 // always blend on all channels since we don't know which one has been set 00161 /* quat interpolation done separate */ 00162 if (schan->rotmode == ROT_MODE_QUAT) { 00163 float dquat[4], squat[4]; 00164 00165 copy_qt_qt(dquat, dchan->quat); 00166 copy_qt_qt(squat, schan->quat); 00167 if (mode==ACTSTRIPMODE_BLEND) 00168 interp_qt_qtqt(dchan->quat, dquat, squat, srcweight); 00169 else { 00170 mul_fac_qt_fl(squat, srcweight); 00171 mul_qt_qtqt(dchan->quat, dquat, squat); 00172 } 00173 00174 normalize_qt(dchan->quat); 00175 } 00176 00177 for (i=0; i<3; i++) { 00178 /* blending for loc and scale are pretty self-explanatory... */ 00179 dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight); 00180 dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight); 00181 00182 /* euler-rotation interpolation done here instead... */ 00183 // FIXME: are these results decent? 00184 if (schan->rotmode) 00185 dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight); 00186 } 00187 for(dcon= (bConstraint*)dchan->constraints.first, scon= (bConstraint*)schan->constraints.first; dcon && scon; dcon= (bConstraint*)dcon->next, scon= (bConstraint*)scon->next) { 00188 /* no 'add' option for constraint blending */ 00189 dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight; 00190 } 00191 } 00192 00193 /* this pose is now in src time */ 00194 dst->ctime= src->ctime; 00195 } 00196 00197 void game_free_pose(bPose *pose) 00198 { 00199 if (pose) { 00200 /* free pose-channels and constraints */ 00201 free_pose_channels(pose); 00202 00203 /* free IK solver state */ 00204 BIK_clear_data(pose); 00205 00206 /* free IK solver param */ 00207 if (pose->ikparam) 00208 MEM_freeN(pose->ikparam); 00209 00210 MEM_freeN(pose); 00211 } 00212 } 00213 00214 BL_ArmatureObject::BL_ArmatureObject( 00215 void* sgReplicationInfo, 00216 SG_Callbacks callbacks, 00217 Object *armature, 00218 Scene *scene, 00219 int vert_deform_type) 00220 00221 : KX_GameObject(sgReplicationInfo,callbacks), 00222 m_controlledConstraints(), 00223 m_poseChannels(), 00224 m_objArma(armature), 00225 m_framePose(NULL), 00226 m_scene(scene), // maybe remove later. needed for where_is_pose 00227 m_lastframe(0.0), 00228 m_timestep(0.040), 00229 m_activeAct(NULL), 00230 m_activePriority(999), 00231 m_vert_deform_type(vert_deform_type), 00232 m_constraintNumber(0), 00233 m_channelNumber(0), 00234 m_lastapplyframe(0.0) 00235 { 00236 m_armature = (bArmature *)armature->data; 00237 00238 /* we make a copy of blender object's pose, and then always swap it with 00239 * the original pose before calling into blender functions, to deal with 00240 * replica's or other objects using the same blender object */ 00241 m_pose = NULL; 00242 game_copy_pose(&m_pose, m_objArma->pose, 1); 00243 // store the original armature object matrix 00244 memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat)); 00245 } 00246 00247 BL_ArmatureObject::~BL_ArmatureObject() 00248 { 00249 BL_ArmatureConstraint* constraint; 00250 while ((constraint = m_controlledConstraints.Remove()) != NULL) { 00251 delete constraint; 00252 } 00253 BL_ArmatureChannel* channel; 00254 while ((channel = static_cast<BL_ArmatureChannel*>(m_poseChannels.Remove())) != NULL) { 00255 delete channel; 00256 } 00257 if (m_pose) 00258 game_free_pose(m_pose); 00259 if (m_framePose) 00260 game_free_pose(m_framePose); 00261 } 00262 00263 00264 void BL_ArmatureObject::LoadConstraints(KX_BlenderSceneConverter* converter) 00265 { 00266 // first delete any existing constraint (should not have any) 00267 while (!m_controlledConstraints.Empty()) { 00268 BL_ArmatureConstraint* constraint = m_controlledConstraints.Remove(); 00269 delete constraint; 00270 } 00271 m_constraintNumber = 0; 00272 00273 // list all the constraint and convert them to BL_ArmatureConstraint 00274 // get the persistent pose structure 00275 bPoseChannel* pchan; 00276 bConstraint* pcon; 00277 bConstraintTypeInfo* cti; 00278 Object* blendtarget; 00279 KX_GameObject* gametarget; 00280 KX_GameObject* gamesubtarget; 00281 00282 // and locate the constraint 00283 for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) { 00284 for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) { 00285 if (pcon->flag & CONSTRAINT_DISABLE) 00286 continue; 00287 // which constraint should we support? 00288 switch (pcon->type) { 00289 case CONSTRAINT_TYPE_TRACKTO: 00290 case CONSTRAINT_TYPE_KINEMATIC: 00291 case CONSTRAINT_TYPE_ROTLIKE: 00292 case CONSTRAINT_TYPE_LOCLIKE: 00293 case CONSTRAINT_TYPE_MINMAX: 00294 case CONSTRAINT_TYPE_SIZELIKE: 00295 case CONSTRAINT_TYPE_LOCKTRACK: 00296 case CONSTRAINT_TYPE_STRETCHTO: 00297 case CONSTRAINT_TYPE_CLAMPTO: 00298 case CONSTRAINT_TYPE_TRANSFORM: 00299 case CONSTRAINT_TYPE_DISTLIMIT: 00300 case CONSTRAINT_TYPE_TRANSLIKE: 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 useful in other cases.\n") 00665 { 00666 SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime()); 00667 Py_RETURN_NONE; 00668 } 00669 00670 #endif // WITH_PYTHON