Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Steiblys <imbusy@imbusy.org>2009-10-02 02:29:15 +0400
committerLukas Steiblys <imbusy@imbusy.org>2009-10-02 02:29:15 +0400
commit0677398a649b6b8c293df3ce3c6668f0a3be3bc8 (patch)
tree9d510a5bd23559bf4fae670ed04d7e5d6c12578c /source/gameengine/Converter/BL_ArmatureObject.cpp
parent59248e9f62006ba05e3098e4d213f3dcb23fe711 (diff)
parentbc942eceacb638735dc4f4f68252c4c207147a70 (diff)
merge from 23153 to 23595soc-2009-imbusy
Diffstat (limited to 'source/gameengine/Converter/BL_ArmatureObject.cpp')
-rw-r--r--source/gameengine/Converter/BL_ArmatureObject.cpp496
1 files changed, 472 insertions, 24 deletions
diff --git a/source/gameengine/Converter/BL_ArmatureObject.cpp b/source/gameengine/Converter/BL_ArmatureObject.cpp
index cfd90813a16..a6066adc03e 100644
--- a/source/gameengine/Converter/BL_ArmatureObject.cpp
+++ b/source/gameengine/Converter/BL_ArmatureObject.cpp
@@ -29,9 +29,15 @@
#include "BL_ArmatureObject.h"
#include "BL_ActionActuator.h"
+#include "KX_BlenderSceneConverter.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_arithb.h"
+#include "BIK_api.h"
#include "BKE_action.h"
#include "BKE_armature.h"
+#include "BKE_utildefines.h"
+#include "BKE_constraint.h"
#include "GEN_Map.h"
#include "GEN_HashedPtr.h"
#include "MEM_guardedalloc.h"
@@ -39,6 +45,11 @@
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_nla_types.h"
+#include "DNA_constraint_types.h"
+#include "KX_PythonSeq.h"
+#include "KX_PythonInit.h"
+#include "KX_KetsjiEngine.h"
#include "MT_Matrix4x4.h"
@@ -46,6 +57,153 @@
#include <config.h>
#endif
+/**
+ * Move here pose function for game engine so that we can mix with GE objects
+ * Principle is as follow:
+ * Use Blender structures so that where_is_pose can be used unchanged
+ * Copy the constraint so that they can be enabled/disabled/added/removed at runtime
+ * Don't copy the constraints for the pose used by the Action actuator, it does not need them.
+ * Scan the constraint structures so that the KX equivalent of target objects are identified and
+ * stored in separate list.
+ * When it is about to evaluate the pose, set the KX object position in the obmat of the corresponding
+ * Blender objects and restore after the evaluation.
+ */
+void game_copy_pose(bPose **dst, bPose *src, int copy_constraint)
+{
+ bPose *out;
+ bPoseChannel *pchan, *outpchan;
+ GHash *ghash;
+
+ /* the game engine copies the current armature pose and then swaps
+ * the object pose pointer. this makes it possible to change poses
+ * without affecting the original blender data. */
+
+ if (!src) {
+ *dst=NULL;
+ return;
+ }
+ else if (*dst==src) {
+ printf("copy_pose source and target are the same\n");
+ *dst=NULL;
+ return;
+ }
+
+ out= (bPose*)MEM_dupallocN(src);
+ out->agroups.first= out->agroups.last= NULL;
+ out->ikdata = NULL;
+ out->ikparam = MEM_dupallocN(out->ikparam);
+ out->flag |= POSE_GAME_ENGINE;
+ BLI_duplicatelist(&out->chanbase, &src->chanbase);
+
+ /* remap pointers */
+ ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ pchan= (bPoseChannel*)src->chanbase.first;
+ outpchan= (bPoseChannel*)out->chanbase.first;
+ for (; pchan; pchan=pchan->next, outpchan=outpchan->next)
+ BLI_ghash_insert(ghash, pchan, outpchan);
+
+ for (pchan=(bPoseChannel*)out->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ pchan->parent= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->parent);
+ pchan->child= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->child);
+ pchan->path= NULL;
+
+ if (copy_constraint) {
+ ListBase listb;
+ // copy all constraint for backward compatibility
+ copy_constraints(&listb, &pchan->constraints); // copy_constraints NULLs listb
+ pchan->constraints= listb;
+ } else {
+ pchan->constraints.first = NULL;
+ pchan->constraints.last = NULL;
+ }
+ }
+
+ BLI_ghash_free(ghash, NULL, NULL);
+
+ *dst=out;
+}
+
+
+
+/* Only allowed for Poses with identical channels */
+void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/)
+{
+ short mode= ACTSTRIPMODE_BLEND;
+
+ bPoseChannel *dchan;
+ const bPoseChannel *schan;
+ bConstraint *dcon, *scon;
+ float dstweight;
+ int i;
+
+ switch (mode){
+ case ACTSTRIPMODE_BLEND:
+ dstweight = 1.0F - srcweight;
+ break;
+ case ACTSTRIPMODE_ADD:
+ dstweight = 1.0F;
+ break;
+ default :
+ dstweight = 1.0F;
+ }
+
+ schan= (bPoseChannel*)src->chanbase.first;
+ for (dchan = (bPoseChannel*)dst->chanbase.first; dchan; dchan=(bPoseChannel*)dchan->next, schan= (bPoseChannel*)schan->next){
+ // always blend on all channels since we don't know which one has been set
+ /* quat interpolation done separate */
+ if (schan->rotmode == ROT_MODE_QUAT) {
+ float dquat[4], squat[4];
+
+ QUATCOPY(dquat, dchan->quat);
+ QUATCOPY(squat, schan->quat);
+ if (mode==ACTSTRIPMODE_BLEND)
+ QuatInterpol(dchan->quat, dquat, squat, srcweight);
+ else {
+ QuatMulFac(squat, srcweight);
+ QuatMul(dchan->quat, dquat, squat);
+ }
+
+ NormalQuat(dchan->quat);
+ }
+
+ for (i=0; i<3; i++) {
+ /* blending for loc and scale are pretty self-explanatory... */
+ dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight);
+ dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight);
+
+ /* euler-rotation interpolation done here instead... */
+ // FIXME: are these results decent?
+ if (schan->rotmode)
+ dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight);
+ }
+ for(dcon= (bConstraint*)dchan->constraints.first, scon= (bConstraint*)schan->constraints.first; dcon && scon; dcon= (bConstraint*)dcon->next, scon= (bConstraint*)scon->next) {
+ /* no 'add' option for constraint blending */
+ dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight;
+ }
+ }
+
+ /* this pose is now in src time */
+ dst->ctime= src->ctime;
+}
+
+void game_free_pose(bPose *pose)
+{
+ if (pose) {
+ /* free pose-channels and constraints */
+ free_pose_channels(pose);
+
+ /* free IK solver state */
+ BIK_clear_data(pose);
+
+ /* free IK solver param */
+ if (pose->ikparam)
+ MEM_freeN(pose->ikparam);
+
+ MEM_freeN(pose);
+ }
+}
+
BL_ArmatureObject::BL_ArmatureObject(
void* sgReplicationInfo,
SG_Callbacks callbacks,
@@ -53,12 +211,17 @@ BL_ArmatureObject::BL_ArmatureObject(
Scene *scene)
: KX_GameObject(sgReplicationInfo,callbacks),
+ m_controlledConstraints(),
+ m_poseChannels(),
m_objArma(armature),
m_framePose(NULL),
m_scene(scene), // maybe remove later. needed for where_is_pose
m_lastframe(0.0),
+ m_timestep(0.040),
m_activeAct(NULL),
m_activePriority(999),
+ m_constraintNumber(0),
+ m_channelNumber(0),
m_lastapplyframe(0.0)
{
m_armature = (bArmature *)armature->data;
@@ -67,7 +230,177 @@ BL_ArmatureObject::BL_ArmatureObject(
* the original pose before calling into blender functions, to deal with
* replica's or other objects using the same blender object */
m_pose = NULL;
- game_copy_pose(&m_pose, m_objArma->pose);
+ game_copy_pose(&m_pose, m_objArma->pose, 1);
+ // store the original armature object matrix
+ memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat));
+}
+
+BL_ArmatureObject::~BL_ArmatureObject()
+{
+ BL_ArmatureConstraint* constraint;
+ while ((constraint = m_controlledConstraints.Remove()) != NULL) {
+ delete constraint;
+ }
+ BL_ArmatureChannel* channel;
+ while ((channel = static_cast<BL_ArmatureChannel*>(m_poseChannels.Remove())) != NULL) {
+ delete channel;
+ }
+ if (m_pose)
+ game_free_pose(m_pose);
+ if (m_framePose)
+ game_free_pose(m_framePose);
+}
+
+
+void BL_ArmatureObject::LoadConstraints(KX_BlenderSceneConverter* converter)
+{
+ // first delete any existing constraint (should not have any)
+ while (!m_controlledConstraints.Empty()) {
+ BL_ArmatureConstraint* constraint = m_controlledConstraints.Remove();
+ delete constraint;
+ }
+ m_constraintNumber = 0;
+
+ // list all the constraint and convert them to BL_ArmatureConstraint
+ // get the persistent pose structure
+ bPoseChannel* pchan;
+ bConstraint* pcon;
+ bConstraintTypeInfo* cti;
+ Object* blendtarget;
+ KX_GameObject* gametarget;
+ KX_GameObject* gamesubtarget;
+
+ // and locate the constraint
+ for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
+ if (pcon->flag & CONSTRAINT_DISABLE)
+ continue;
+ // which constraint should we support?
+ switch (pcon->type) {
+ case CONSTRAINT_TYPE_TRACKTO:
+ case CONSTRAINT_TYPE_KINEMATIC:
+ case CONSTRAINT_TYPE_ROTLIKE:
+ case CONSTRAINT_TYPE_LOCLIKE:
+ case CONSTRAINT_TYPE_MINMAX:
+ case CONSTRAINT_TYPE_SIZELIKE:
+ case CONSTRAINT_TYPE_LOCKTRACK:
+ case CONSTRAINT_TYPE_STRETCHTO:
+ case CONSTRAINT_TYPE_CLAMPTO:
+ case CONSTRAINT_TYPE_TRANSFORM:
+ case CONSTRAINT_TYPE_DISTLIMIT:
+ cti = constraint_get_typeinfo(pcon);
+ gametarget = gamesubtarget = NULL;
+ if (cti && cti->get_constraint_targets) {
+ ListBase listb = { NULL, NULL };
+ cti->get_constraint_targets(pcon, &listb);
+ if (listb.first) {
+ bConstraintTarget* target = (bConstraintTarget*)listb.first;
+ if (target->tar && target->tar != m_objArma) {
+ // only remember external objects, self target is handled automatically
+ blendtarget = target->tar;
+ gametarget = converter->FindGameObject(blendtarget);
+ }
+ if (target->next != NULL) {
+ // secondary target
+ target = (bConstraintTarget*)target->next;
+ if (target->tar && target->tar != m_objArma) {
+ // only track external object
+ blendtarget = target->tar;
+ gamesubtarget = converter->FindGameObject(blendtarget);
+ }
+ }
+ }
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(pcon, &listb, 1);
+ }
+ BL_ArmatureConstraint* constraint = new BL_ArmatureConstraint(this, pchan, pcon, gametarget, gamesubtarget);
+ m_controlledConstraints.AddBack(constraint);
+ m_constraintNumber++;
+ }
+ }
+ }
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannel, const char* constraintname)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ BL_ArmatureConstraint* constraint = *cit;
+ if (constraint->Match(posechannel, constraintname))
+ return constraint;
+ }
+ return NULL;
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannelconstraint)
+{
+ // performance: use hash string instead of plain string compare
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ BL_ArmatureConstraint* constraint = *cit;
+ if (!strcmp(constraint->GetName(), posechannelconstraint))
+ return constraint;
+ }
+ return NULL;
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(int index)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end() && index; ++cit, --index);
+ return (cit.end()) ? NULL : *cit;
+}
+
+/* this function is called to populate the m_poseChannels list */
+void BL_ArmatureObject::LoadChannels()
+{
+ if (m_poseChannels.Empty()) {
+ bPoseChannel* pchan;
+ BL_ArmatureChannel* proxy;
+
+ m_channelNumber = 0;
+ for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ proxy = new BL_ArmatureChannel(this, pchan);
+ m_poseChannels.AddBack(proxy);
+ m_channelNumber++;
+ }
+ }
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(bPoseChannel* pchan)
+{
+ LoadChannels();
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end(); ++cit)
+ {
+ BL_ArmatureChannel* channel = *cit;
+ if (channel->m_posechannel == pchan)
+ return channel;
+ }
+ return NULL;
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(const char* str)
+{
+ LoadChannels();
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end(); ++cit)
+ {
+ BL_ArmatureChannel* channel = *cit;
+ if (!strcmp(channel->m_posechannel->name, str))
+ return channel;
+ }
+ return NULL;
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(int index)
+{
+ LoadChannels();
+ if (index < 0 || index >= m_channelNumber)
+ return NULL;
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end() && index; ++cit, --index);
+ return (cit.end()) ? NULL : *cit;
}
CValue* BL_ArmatureObject::GetReplica()
@@ -83,22 +416,61 @@ void BL_ArmatureObject::ProcessReplica()
KX_GameObject::ProcessReplica();
m_pose = NULL;
- game_copy_pose(&m_pose, pose);
+ m_framePose = NULL;
+ game_copy_pose(&m_pose, pose, 1);
}
-BL_ArmatureObject::~BL_ArmatureObject()
+void BL_ArmatureObject::ReParentLogic()
{
- if (m_pose)
- game_free_pose(m_pose);
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->ReParent(this);
+ }
+ KX_GameObject::ReParentLogic();
+}
+
+void BL_ArmatureObject::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->Relink(obj_map);
+ }
+ KX_GameObject::Relink(obj_map);
+}
+
+bool BL_ArmatureObject::UnlinkObject(SCA_IObject* clientobj)
+{
+ // clientobj is being deleted, make sure we don't hold any reference to it
+ bool res = false;
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ res |= (*cit)->UnlinkObject(clientobj);
+ }
+ return res;
}
void BL_ArmatureObject::ApplyPose()
{
m_armpose = m_objArma->pose;
m_objArma->pose = m_pose;
+ // in the GE, we use ctime to store the timestep
+ m_pose->ctime = (float)m_timestep;
//m_scene->r.cfra++;
if(m_lastapplyframe != m_lastframe) {
+ // update the constraint if any, first put them all off so that only the active ones will be updated
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->UpdateTarget();
+ }
+ // update ourself
+ UpdateBlenderObjectMatrix(m_objArma);
where_is_pose(m_scene, m_objArma); // XXX
+ // restore ourself
+ memcpy(m_objArma->obmat, m_obmat, sizeof(m_obmat));
+ // restore active targets
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->RestoreTarget();
+ }
m_lastapplyframe = m_lastframe;
}
}
@@ -119,30 +491,37 @@ bool BL_ArmatureObject::SetActiveAction(BL_ActionActuator *act, short priority,
{
if (curtime != m_lastframe){
m_activePriority = 9999;
+ // compute the timestep for the underlying IK algorithm
+ m_timestep = curtime-m_lastframe;
m_lastframe= curtime;
m_activeAct = NULL;
// remember the pose at the start of the frame
- m_framePose = m_pose;
+ GetPose(&m_framePose);
}
- if (priority<=m_activePriority)
+ if (act)
{
- if (priority<m_activePriority)
- // this action overwrites the previous ones, start from initial pose to cancel their effects
- m_pose = m_framePose;
- if (m_activeAct && (m_activeAct!=act))
- m_activeAct->SetBlendTime(0.0); /* Reset the blend timer */
- m_activeAct = act;
- m_activePriority = priority;
- m_lastframe = curtime;
-
- return true;
- }
- else{
- act->SetBlendTime(0.0);
- return false;
+ if (priority<=m_activePriority)
+ {
+ if (priority<m_activePriority) {
+ // this action overwrites the previous ones, start from initial pose to cancel their effects
+ SetPose(m_framePose);
+ if (m_activeAct && (m_activeAct!=act))
+ /* Reset the blend timer since this new action cancels the old one */
+ m_activeAct->SetBlendTime(0.0);
+ }
+ m_activeAct = act;
+ m_activePriority = priority;
+ m_lastframe = curtime;
+
+ return true;
+ }
+ else{
+ act->SetBlendTime(0.0);
+ return false;
+ }
}
-
+ return false;
}
BL_ActionActuator * BL_ArmatureObject::GetActiveAction()
@@ -161,7 +540,7 @@ void BL_ArmatureObject::GetPose(bPose **pose)
a crash and memory leakage when
&BL_ActionActuator::m_pose is freed
*/
- game_copy_pose(pose, m_pose);
+ game_copy_pose(pose, m_pose, 0);
}
else {
if (*pose == m_pose)
@@ -178,7 +557,7 @@ void BL_ArmatureObject::GetMRDPose(bPose **pose)
/* Otherwise, copy the armature's pose channels into the caller-supplied pose */
if (!*pose)
- game_copy_pose(pose, m_pose);
+ game_copy_pose(pose, m_pose, 0);
else
extract_pose_from_pose(*pose, m_pose);
}
@@ -210,3 +589,72 @@ float BL_ArmatureObject::GetBoneLength(Bone* bone) const
{
return (float)(MT_Point3(bone->head) - MT_Point3(bone->tail)).length();
}
+
+#ifndef DISABLE_PYTHON
+
+// PYTHON
+
+PyTypeObject BL_ArmatureObject::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "BL_ArmatureObject",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,
+ &KX_GameObject::Sequence,
+ &KX_GameObject::Mapping,
+ 0,0,0,
+ NULL,
+ NULL,
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &KX_GameObject::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyMethodDef BL_ArmatureObject::Methods[] = {
+
+ KX_PYMETHODTABLE_NOARGS(BL_ArmatureObject, update),
+ {NULL,NULL} //Sentinel
+};
+
+PyAttributeDef BL_ArmatureObject::Attributes[] = {
+
+ KX_PYATTRIBUTE_RO_FUNCTION("constraints", BL_ArmatureObject, pyattr_get_constraints),
+ KX_PYATTRIBUTE_RO_FUNCTION("channels", BL_ArmatureObject, pyattr_get_channels),
+ {NULL} //Sentinel
+};
+
+PyObject* BL_ArmatureObject::pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CONSTRAINTS);
+}
+
+PyObject* BL_ArmatureObject::pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureObject* self = static_cast<BL_ArmatureObject*>(self_v);
+ self->LoadChannels(); // make sure we have the channels
+ return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CHANNELS);
+}
+
+KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update,
+ "update()\n"
+ "Make sure that the armature will be updated on next graphic frame.\n"
+ "This is automatically done if a KX_ArmatureActuator with mode run is active\n"
+ "or if an action is playing. This function is usefull in other cases.\n")
+{
+ SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime());
+ Py_RETURN_NONE;
+}
+
+#endif // DISABLE_PYTHON