diff options
author | Mitchell Stokes <mogurijin@gmail.com> | 2014-04-07 03:30:59 +0400 |
---|---|---|
committer | Mitchell Stokes <mogurijin@gmail.com> | 2014-04-07 03:30:59 +0400 |
commit | fe05f97841c0ee3e2a6e15f2252ad160fefc3509 (patch) | |
tree | 2a672e5d82448f43471ddb529a95fa547d5c311e /source | |
parent | be8b4b8b0c4d4fdeed6ebf88d8a3bd480e4c48ce (diff) |
BGE: Multi-threading animation updates and skinning.
This required BL_ArmatureObject to have tighter control over armatures and poses.
Also, (Blender) armature objects are now copied instead of shared between
BL_ArmatureObjects to avoid race conditions. Also, due to the armature copy,
shape key drivers need a bit of extra fiddling to get the correct armature copy.
Initially OpenMP was used for threading, but then BLI_task was used due to being
less compiler dependent.
This commit also places time spent on skinning updates in the Animation
profiler category (was previously under the Rasterizer category).
Diffstat (limited to 'source')
-rw-r--r-- | source/gameengine/Converter/BL_ActionActuator.cpp | 13 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ActionActuator.h | 3 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ArmatureActuator.cpp | 2 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ArmatureObject.cpp | 127 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ArmatureObject.h | 24 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_BlenderDataConversion.cpp | 21 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ShapeDeformer.cpp | 29 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_ShapeDeformer.h | 2 | ||||
-rw-r--r-- | source/gameengine/Converter/BL_SkinDeformer.cpp | 88 | ||||
-rw-r--r-- | source/gameengine/Ketsji/BL_Action.cpp | 57 | ||||
-rw-r--r-- | source/gameengine/Ketsji/BL_Action.h | 2 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 6 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.h | 6 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_Scene.cpp | 92 |
14 files changed, 225 insertions, 247 deletions
diff --git a/source/gameengine/Converter/BL_ActionActuator.cpp b/source/gameengine/Converter/BL_ActionActuator.cpp index a28906254a1..34d36846968 100644 --- a/source/gameengine/Converter/BL_ActionActuator.cpp +++ b/source/gameengine/Converter/BL_ActionActuator.cpp @@ -93,9 +93,6 @@ BL_ActionActuator::BL_ActionActuator(SCA_IObject *gameobj, m_priority(priority), m_layer(layer), m_ipo_flags(ipo_flags), - m_pose(NULL), - m_blendpose(NULL), - m_userpose(NULL), m_action(action), m_propname(propname), m_framepropname(framepropname) @@ -106,20 +103,12 @@ BL_ActionActuator::BL_ActionActuator(SCA_IObject *gameobj, BL_ActionActuator::~BL_ActionActuator() { - if (m_pose) - game_free_pose(m_pose); - if (m_userpose) - game_free_pose(m_userpose); - if (m_blendpose) - game_free_pose(m_blendpose); } void BL_ActionActuator::ProcessReplica() { SCA_IActuator::ProcessReplica(); - - m_pose = NULL; - m_blendpose = NULL; + m_localtime=m_startframe; m_lastUpdate=-1; diff --git a/source/gameengine/Converter/BL_ActionActuator.h b/source/gameengine/Converter/BL_ActionActuator.h index 4579a21f554..f488b0c76a6 100644 --- a/source/gameengine/Converter/BL_ActionActuator.h +++ b/source/gameengine/Converter/BL_ActionActuator.h @@ -134,9 +134,6 @@ protected: short m_priority; short m_layer; short m_ipo_flags; - struct bPose* m_pose; - struct bPose* m_blendpose; - struct bPose* m_userpose; struct bAction *m_action; STR_String m_propname; STR_String m_framepropname; diff --git a/source/gameengine/Converter/BL_ArmatureActuator.cpp b/source/gameengine/Converter/BL_ArmatureActuator.cpp index f0c4b3d32bb..e38cb6eadaf 100644 --- a/source/gameengine/Converter/BL_ArmatureActuator.cpp +++ b/source/gameengine/Converter/BL_ArmatureActuator.cpp @@ -155,7 +155,7 @@ bool BL_ArmatureActuator::Update(double curtime, bool frame) switch (m_type) { case ACT_ARM_RUN: result = true; - obj->SetActiveAction(NULL, 0, curtime); + obj->UpdateTimestep(curtime); break; case ACT_ARM_ENABLE: if (m_constraint) diff --git a/source/gameengine/Converter/BL_ArmatureObject.cpp b/source/gameengine/Converter/BL_ArmatureObject.cpp index 619797a8c69..3eb782961e3 100644 --- a/source/gameengine/Converter/BL_ArmatureObject.cpp +++ b/source/gameengine/Converter/BL_ArmatureObject.cpp @@ -42,7 +42,13 @@ #include "BIK_api.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_object.h" #include "BKE_library.h" +#include "BKE_global.h" + +extern "C" { +#include "BKE_animsys.h" +} #include "BKE_constraint.h" #include "CTR_Map.h" @@ -53,6 +59,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_constraint_types.h" +#include "RNA_access.h" #include "KX_PythonSeq.h" #include "KX_PythonInit.h" #include "KX_KetsjiEngine.h" @@ -70,7 +77,7 @@ * 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) +static void game_copy_pose(bPose **dst, bPose *src, int copy_constraint) { bPose *out; bPoseChannel *pchan, *outpchan; @@ -85,7 +92,7 @@ void game_copy_pose(bPose **dst, bPose *src, int copy_constraint) return; } else if (*dst==src) { - printf("BKE_pose_copy_data source and target are the same\n"); + printf("game_copy_pose source and target are the same\n"); *dst=NULL; return; } @@ -142,7 +149,7 @@ void game_copy_pose(bPose **dst, bPose *src, int copy_constraint) /* Only allowed for Poses with identical channels */ -void game_blend_poses(bPose *dst, bPose *src, float srcweight, short mode) +static void game_blend_poses(bPose *dst, bPose *src, float srcweight, short mode) { bPoseChannel *dchan; const bPoseChannel *schan; @@ -202,23 +209,6 @@ void game_blend_poses(bPose *dst, bPose *src, float srcweight, short mode) dst->ctime= src->ctime; } -void game_free_pose(bPose *pose) -{ - if (pose) { - /* free pose-channels and constraints */ - BKE_pose_channels_free(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, @@ -229,26 +219,18 @@ BL_ArmatureObject::BL_ArmatureObject( : KX_GameObject(sgReplicationInfo,callbacks), m_controlledConstraints(), m_poseChannels(), - m_objArma(armature), - m_framePose(NULL), m_scene(scene), // maybe remove later. needed for BKE_pose_where_is m_lastframe(0.0), m_timestep(0.040), - m_activeAct(NULL), - m_activePriority(999), m_vert_deform_type(vert_deform_type), m_constraintNumber(0), m_channelNumber(0), m_lastapplyframe(0.0) { - m_armature = (bArmature *)armature->data; - - /* we make a copy of blender object's pose, and then always swap it with - * 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, 1); - // store the original armature object matrix + m_origObjArma = armature; // Keep a copy of the original armature so we can fix drivers later + m_objArma = BKE_object_copy(armature); + m_objArma->data = BKE_armature_copy((bArmature *)armature->data); + m_pose = m_objArma->pose; memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat)); } @@ -262,10 +244,9 @@ BL_ArmatureObject::~BL_ArmatureObject() 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); + + if (m_objArma) + BKE_libblock_free(G.main, m_objArma); } @@ -431,12 +412,12 @@ CValue* BL_ArmatureObject::GetReplica() void BL_ArmatureObject::ProcessReplica() { - bPose *pose= m_pose; KX_GameObject::ProcessReplica(); - m_pose = NULL; - m_framePose = NULL; - game_copy_pose(&m_pose, pose, 1); + bArmature* tmp = (bArmature*)m_objArma->data; + m_objArma = BKE_object_copy(m_objArma); + m_objArma->data = BKE_armature_copy(tmp); + m_pose = m_objArma->pose; } void BL_ArmatureObject::ReParentLogic() @@ -506,48 +487,32 @@ void BL_ArmatureObject::SetPose(bPose *pose) m_lastapplyframe = -1.0; } -bool BL_ArmatureObject::SetActiveAction(BL_ActionActuator *act, short priority, double curtime) +void BL_ArmatureObject::SetPoseByAction(bAction *action, float localtime) +{ + Object *arm = GetArmatureObject(); + + PointerRNA ptrrna; + RNA_id_pointer_create(&arm->id, &ptrrna); + + animsys_evaluate_action(&ptrrna, action, NULL, localtime); +} + +void BL_ArmatureObject::BlendInPose(bPose *blend_pose, float weight, short mode) +{ + game_blend_poses(m_pose, blend_pose, weight, mode); +} + +bool BL_ArmatureObject::UpdateTimestep(double curtime) { 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 - GetPose(&m_framePose); } - if (act) - { - 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() -{ - return m_activeAct; -} - void BL_ArmatureObject::GetPose(bPose **pose) { /* If the caller supplies a null pose, create a new one. */ @@ -570,22 +535,6 @@ void BL_ArmatureObject::GetPose(bPose **pose) } } -void BL_ArmatureObject::GetMRDPose(bPose **pose) -{ - /* If the caller supplies a null pose, create a new one. */ - /* Otherwise, copy the armature's pose channels into the caller-supplied pose */ - - if (!*pose) - game_copy_pose(pose, m_pose, 0); - else - extract_pose_from_pose(*pose, m_pose); -} - -short BL_ArmatureObject::GetActivePriority() -{ - return m_activePriority; -} - double BL_ArmatureObject::GetLastFrame() { return m_lastframe; @@ -671,7 +620,7 @@ KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update, "This is automatically done if a KX_ArmatureActuator with mode run is active\n" "or if an action is playing. This function is useful in other cases.\n") { - SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime()); + UpdateTimestep(KX_GetActiveEngine()->GetFrameTime()); Py_RETURN_NONE; } diff --git a/source/gameengine/Converter/BL_ArmatureObject.h b/source/gameengine/Converter/BL_ArmatureObject.h index 81388355fc4..691e73d6bde 100644 --- a/source/gameengine/Converter/BL_ArmatureObject.h +++ b/source/gameengine/Converter/BL_ArmatureObject.h @@ -55,14 +55,11 @@ class BL_ArmatureObject : public KX_GameObject public: double GetLastFrame (); - short GetActivePriority(); virtual void ProcessReplica(); virtual void ReParentLogic(); virtual void Relink(CTR_Map<CTR_HashedPtr, void*> *obj_map); virtual bool UnlinkObject(SCA_IObject* clientobj); - class BL_ActionActuator * GetActiveAction(); - BL_ArmatureObject( void* sgReplicationInfo, SG_Callbacks callbacks, @@ -73,21 +70,23 @@ public: virtual ~BL_ArmatureObject(); virtual CValue* GetReplica(); - void GetMRDPose(struct bPose **pose); void GetPose(struct bPose **pose); void SetPose (struct bPose *pose); struct bPose *GetOrigPose() {return m_pose;} // never edit this, only for accessing names void ApplyPose(); + void SetPoseByAction(struct bAction* action, float localtime); + void BlendInPose(struct bPose *blend_pose, float weight, short mode); void RestorePose(); - bool SetActiveAction(class BL_ActionActuator *act, short priority, double curtime); + bool UpdateTimestep(double curtime); - struct bArmature *GetArmature() { return m_armature; } - const struct bArmature * GetArmature() const { return m_armature; } + struct bArmature *GetArmature() { return (bArmature*)m_objArma->data; } + const struct bArmature * GetArmature() const { return (bArmature*)m_objArma->data; } const struct Scene * GetScene() const { return m_scene; } Object* GetArmatureObject() {return m_objArma;} + Object* GetOrigArmatureObject() {return m_origObjArma;} int GetVertDeformType() {return m_vert_deform_type;} @@ -128,15 +127,12 @@ protected: /* list element: BL_ArmatureChannel. Use SG_DList to avoid list replication */ SG_DList m_poseChannels; Object *m_objArma; - struct bArmature *m_armature; + Object *m_origObjArma; struct bPose *m_pose; struct bPose *m_armpose; - struct bPose *m_framePose; struct Scene *m_scene; // need for BKE_pose_where_is double m_lastframe; double m_timestep; // delta since last pose evaluation. - class BL_ActionActuator *m_activeAct; - short m_activePriority; int m_vert_deform_type; size_t m_constraintNumber; size_t m_channelNumber; @@ -146,10 +142,4 @@ protected: double m_lastapplyframe; }; -/* Pose function specific to the game engine */ -void game_blend_poses(struct bPose *dst, struct bPose *src, float srcweight, short mode); /* was blend_poses */ -//void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); -void game_copy_pose(struct bPose **dst, struct bPose *src, int copy_con); -void game_free_pose(struct bPose *pose); - #endif /* __BL_ARMATUREOBJECT_H__ */ diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index 4b55d73178c..b283330266f 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -1937,15 +1937,11 @@ static KX_GameObject *gameobject_from_blenderobject( BL_ModifierDeformer *dcont = new BL_ModifierDeformer((BL_DeformableGameObject *)gameobj, kxscene->GetBlenderScene(), ob, meshobj); ((BL_DeformableGameObject*)gameobj)->SetDeformer(dcont); - if (bHasShapeKey && bHasArmature) - dcont->LoadShapeDrivers(ob->parent); } else if (bHasShapeKey) { // not that we can have shape keys without dvert! BL_ShapeDeformer *dcont = new BL_ShapeDeformer((BL_DeformableGameObject*)gameobj, ob, meshobj); ((BL_DeformableGameObject*)gameobj)->SetDeformer(dcont); - if (bHasArmature) - dcont->LoadShapeDrivers(ob->parent); } else if (bHasArmature) { BL_SkinDeformer *dcont = new BL_SkinDeformer((BL_DeformableGameObject*)gameobj, ob, meshobj); @@ -2640,12 +2636,25 @@ void BL_ConvertBlenderObjects(struct Main* maggie, gameobj->GetDeformer()->UpdateBuckets(); } - // Set up armature constraints + // Set up armature constraints and shapekey drivers for (i=0;i<sumolist->GetCount();++i) { KX_GameObject* gameobj = (KX_GameObject*) sumolist->GetValue(i); if (gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) - ((BL_ArmatureObject*)gameobj)->LoadConstraints(converter); + { + BL_ArmatureObject *armobj = (BL_ArmatureObject*)gameobj; + armobj->LoadConstraints(converter); + + CListValue *children = armobj->GetChildren(); + for (int j=0; j<children->GetCount();++j) + { + BL_ShapeDeformer *deform = dynamic_cast<BL_ShapeDeformer*>(((KX_GameObject*)children->GetValue(j))->GetDeformer()); + if (deform) + deform->LoadShapeDrivers(armobj); + } + + children->Release(); + } } bool processCompoundChildren = false; diff --git a/source/gameengine/Converter/BL_ShapeDeformer.cpp b/source/gameengine/Converter/BL_ShapeDeformer.cpp index ca5b26079b1..43f719d80c4 100644 --- a/source/gameengine/Converter/BL_ShapeDeformer.cpp +++ b/source/gameengine/Converter/BL_ShapeDeformer.cpp @@ -51,6 +51,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_key.h" +#include "BKE_fcurve.h" #include "BKE_ipo.h" #include "BKE_library.h" #include "MT_Point3.h" @@ -119,8 +120,29 @@ void BL_ShapeDeformer::ProcessReplica() m_key = BKE_key_copy(m_key); } -bool BL_ShapeDeformer::LoadShapeDrivers(Object* arma) +bool BL_ShapeDeformer::LoadShapeDrivers(KX_GameObject* parent) { + // Fix drivers since BL_ArmatureObject makes copies + if (parent->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { + BL_ArmatureObject *arma = (BL_ArmatureObject*)parent; + FCurve *fcu; + + for (fcu = (FCurve*)GetKey()->adt->drivers.first; fcu; fcu = (FCurve*)fcu->next) { + + DriverVar *dvar; + for (dvar = (DriverVar*)fcu->driver->variables.first; dvar; dvar = (DriverVar*)dvar->next) { + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->id) { + if ((Object*)dtar->id == arma->GetOrigArmatureObject()) + dtar->id = (ID*)arma->GetArmatureObject(); + } + } + DRIVER_TARGETS_LOOPER_END + } + } + } + // This used to check if we had drivers from this armature, // now we just assume we want to use shape drivers // and let the animsys handle things. @@ -132,15 +154,10 @@ bool BL_ShapeDeformer::LoadShapeDrivers(Object* arma) bool BL_ShapeDeformer::ExecuteShapeDrivers(void) { if (m_useShapeDrivers && PoseUpdated()) { - // the shape drivers use the bone matrix as input. Must - // update the matrix now - m_armobj->ApplyPose(); - // We don't need an actual time, just use 0 BKE_animsys_evaluate_animdata(NULL, &GetKey()->id, GetKey()->adt, 0.f, ADT_RECALC_DRIVERS); ForceUpdate(); - m_armobj->RestorePose(); m_bDynamic = true; return true; } diff --git a/source/gameengine/Converter/BL_ShapeDeformer.h b/source/gameengine/Converter/BL_ShapeDeformer.h index a506fb9c7f6..f6746dd2302 100644 --- a/source/gameengine/Converter/BL_ShapeDeformer.h +++ b/source/gameengine/Converter/BL_ShapeDeformer.h @@ -61,7 +61,7 @@ public: virtual ~BL_ShapeDeformer(); bool Update (void); - bool LoadShapeDrivers(Object* arma); + bool LoadShapeDrivers(KX_GameObject* parent); bool ExecuteShapeDrivers(void); struct Key *GetKey(); diff --git a/source/gameengine/Converter/BL_SkinDeformer.cpp b/source/gameengine/Converter/BL_SkinDeformer.cpp index e068a91bf7e..8e1f51238d5 100644 --- a/source/gameengine/Converter/BL_SkinDeformer.cpp +++ b/source/gameengine/Converter/BL_SkinDeformer.cpp @@ -152,44 +152,8 @@ void BL_SkinDeformer::Relink(CTR_Map<class CTR_HashedPtr, void*>*map) bool BL_SkinDeformer::Apply(RAS_IPolyMaterial *mat) { - RAS_MeshSlot::iterator it; - RAS_MeshMaterial *mmat; - RAS_MeshSlot *slot; - size_t i, nmat, imat; - - // update the vertex in m_transverts - if (!Update()) - return false; - - if (m_transverts) { - // the vertex cache is unique to this deformer, no need to update it - // if it wasn't updated! We must update all the materials at once - // because we will not get here again for the other material - nmat = m_pMeshObject->NumMaterials(); - for (imat=0; imat<nmat; imat++) { - mmat = m_pMeshObject->GetMeshMaterial(imat); - if (!mmat->m_slots[(void*)m_gameobj]) - continue; - - slot = *mmat->m_slots[(void*)m_gameobj]; - - // for each array - for (slot->begin(it); !slot->end(it); slot->next(it)) { - // for each vertex - // copy the untransformed data from the original mvert - for (i=it.startvertex; i<it.endvertex; i++) { - RAS_TexVert& v = it.vertex[i]; - v.SetXYZ(m_transverts[v.getOrigIndex()]); - if (m_copyNormals) - v.SetNormal(m_transnors[v.getOrigIndex()]); - } - } - } - - if (m_copyNormals) - m_copyNormals = false; - } - return true; + // We do everything in UpdateInternal() now so we can thread it. + return false; } RAS_Deformer *BL_SkinDeformer::GetReplica() @@ -331,7 +295,7 @@ bool BL_SkinDeformer::UpdateInternal(bool shape_applied) if (!shape_applied) { /* store verts locally */ VerifyStorage(); - + /* duplicate */ for (int v =0; v<m_bmesh->totvert; v++) { @@ -342,15 +306,10 @@ bool BL_SkinDeformer::UpdateInternal(bool shape_applied) m_armobj->ApplyPose(); - switch (m_armobj->GetVertDeformType()) - { - case ARM_VDEF_BGE_CPU: - BGEDeformVerts(); - break; - case ARM_VDEF_BLENDER: - default: - BlenderDeformVerts(); - } + if (m_armobj->GetVertDeformType() == ARM_VDEF_BGE_CPU) + BGEDeformVerts(); + else + BlenderDeformVerts(); /* Update the current frame */ m_lastArmaUpdate=m_armobj->GetLastFrame(); @@ -359,6 +318,39 @@ bool BL_SkinDeformer::UpdateInternal(bool shape_applied) /* dynamic vertex, cannot use display list */ m_bDynamic = true; /* indicate that the m_transverts and normals are up to date */ + RAS_MeshSlot::iterator it; + RAS_MeshMaterial *mmat; + RAS_MeshSlot *slot; + size_t i, nmat, imat; + + if (m_transverts) { + // the vertex cache is unique to this deformer, no need to update it + // if it wasn't updated! We must update all the materials at once + // because we will not get here again for the other material + nmat = m_pMeshObject->NumMaterials(); + for (imat=0; imat<nmat; imat++) { + mmat = m_pMeshObject->GetMeshMaterial(imat); + if (!mmat->m_slots[(void*)m_gameobj]) + continue; + + slot = *mmat->m_slots[(void*)m_gameobj]; + + // for each array + for (slot->begin(it); !slot->end(it); slot->next(it)) { + // for each vertex + // copy the untransformed data from the original mvert + for (i=it.startvertex; i<it.endvertex; i++) { + RAS_TexVert& v = it.vertex[i]; + v.SetXYZ(m_transverts[v.getOrigIndex()]); + if (m_copyNormals) + v.SetNormal(m_transnors[v.getOrigIndex()]); + } + } + } + + if (m_copyNormals) + m_copyNormals = false; + } return true; } diff --git a/source/gameengine/Ketsji/BL_Action.cpp b/source/gameengine/Ketsji/BL_Action.cpp index a974ffbf672..e4ab2d5ce28 100644 --- a/source/gameengine/Ketsji/BL_Action.cpp +++ b/source/gameengine/Ketsji/BL_Action.cpp @@ -51,10 +51,14 @@ extern "C" { #include "DNA_material_types.h" } +#include "MEM_guardedalloc.h" +#include "BKE_library.h" +#include "BKE_global.h" + BL_Action::BL_Action(class KX_GameObject* gameobj) : m_action(NULL), - m_pose(NULL), + m_tmpaction(NULL), m_blendpose(NULL), m_blendinpose(NULL), m_obj(gameobj), @@ -77,13 +81,16 @@ BL_Action::BL_Action(class KX_GameObject* gameobj) BL_Action::~BL_Action() { - if (m_pose) - game_free_pose(m_pose); if (m_blendpose) - game_free_pose(m_blendpose); + BKE_pose_free(m_blendpose); if (m_blendinpose) - game_free_pose(m_blendinpose); + BKE_pose_free(m_blendinpose); ClearControllerList(); + + if (m_tmpaction) { + BKE_libblock_free(G.main, m_tmpaction); + m_tmpaction = NULL; + } } void BL_Action::ClearControllerList() @@ -139,6 +146,13 @@ bool BL_Action::Play(const char* name, && m_priority == priority && m_speed == playback_speed) return false; + // Keep a copy of the action for threading purposes + if (m_tmpaction) { + BKE_libblock_free(G.main, m_tmpaction); + m_tmpaction = NULL; + } + m_tmpaction = BKE_action_copy(m_action); + // First get rid of any old controllers ClearControllerList(); @@ -208,7 +222,7 @@ bool BL_Action::Play(const char* name, if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj; - obj->GetMRDPose(&m_blendinpose); + obj->GetPose(&m_blendinpose); } else { @@ -402,22 +416,12 @@ void BL_Action::Update(float curtime) if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj; - obj->GetPose(&m_pose); - - // Extract the pose from the action - { - Object *arm = obj->GetArmatureObject(); - bPose *temp = arm->pose; - - arm->pose = m_pose; - PointerRNA ptrrna; - RNA_id_pointer_create(&arm->id, &ptrrna); - - animsys_evaluate_action(&ptrrna, m_action, NULL, m_localtime); + if (m_layer_weight >= 0) + obj->GetPose(&m_blendpose); - arm->pose = temp; - } + // Extract the pose from the action + obj->SetPoseByAction(m_tmpaction, m_localtime); // Handle blending between armature actions if (m_blendin && m_blendframe<m_blendin) @@ -428,20 +432,15 @@ void BL_Action::Update(float curtime) float weight = 1.f - (m_blendframe/m_blendin); // Blend the poses - game_blend_poses(m_pose, m_blendinpose, weight, ACT_BLEND_BLEND); + obj->BlendInPose(m_blendinpose, weight, ACT_BLEND_BLEND); } // Handle layer blending if (m_layer_weight >= 0) - { - obj->GetMRDPose(&m_blendpose); - game_blend_poses(m_pose, m_blendpose, m_layer_weight, m_blendmode); - } - - obj->SetPose(m_pose); + obj->BlendInPose(m_blendpose, m_layer_weight, m_blendmode); - obj->SetActiveAction(NULL, 0, curtime); + obj->UpdateTimestep(curtime); } else { @@ -456,7 +455,7 @@ void BL_Action::Update(float curtime) PointerRNA ptrrna; RNA_id_pointer_create(&key->id, &ptrrna); - animsys_evaluate_action(&ptrrna, m_action, NULL, m_localtime); + animsys_evaluate_action(&ptrrna, m_tmpaction, NULL, m_localtime); // Handle blending between shape actions if (m_blendin && m_blendframe < m_blendin) diff --git a/source/gameengine/Ketsji/BL_Action.h b/source/gameengine/Ketsji/BL_Action.h index e9d09916517..463d177c876 100644 --- a/source/gameengine/Ketsji/BL_Action.h +++ b/source/gameengine/Ketsji/BL_Action.h @@ -38,7 +38,7 @@ class BL_Action { private: struct bAction* m_action; - struct bPose* m_pose; + struct bAction* m_tmpaction; struct bPose* m_blendpose; struct bPose* m_blendinpose; std::vector<class SG_Controller*> m_sg_contr_list; diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index 231590cde83..96a3845a439 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -37,6 +37,8 @@ #include <iostream> #include <stdio.h> +#include "BLI_task.h" + #include "KX_KetsjiEngine.h" #include "ListValue.h" @@ -185,6 +187,7 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) m_pyprofiledict = PyDict_New(); #endif + m_taskscheduler = BLI_task_scheduler_create(TASK_SCHEDULER_AUTO_THREADS); } @@ -201,6 +204,9 @@ KX_KetsjiEngine::~KX_KetsjiEngine() #ifdef WITH_PYTHON Py_CLEAR(m_pyprofiledict); #endif + + if (m_taskscheduler) + BLI_task_scheduler_free(m_taskscheduler); } diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index 85cd8bba2de..9e5d1893320 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -42,6 +42,7 @@ #include "KX_WorldInfo.h" #include <vector> +struct TaskScheduler; class KX_TimeCategoryLogger; #define LEFT_EYE 1 @@ -195,6 +196,9 @@ private: /** Settings that doesn't go away with Game Actuator */ GlobalSettings m_globalsettings; + /** Task scheduler for multi-threading */ + TaskScheduler* m_taskscheduler; + void RenderFrame(KX_Scene* scene, KX_Camera* cam); void PostRenderScene(KX_Scene* scene); void RenderDebugProperties(); @@ -225,6 +229,8 @@ public: SCA_IInputDevice* GetKeyboardDevice() { return m_keyboarddevice; } SCA_IInputDevice* GetMouseDevice() { return m_mousedevice; } + TaskScheduler* GetTaskScheduler() { return m_taskscheduler; } + /// Dome functions void InitDome(short res, short mode, short angle, float resbuf, short tilt, struct Text* text); void EndDome(); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index a9ac13c755f..fbd9eeba8ed 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -102,6 +102,8 @@ #include <stdio.h> +#include "BLI_task.h" + static void *KX_SceneReplicationFunc(SG_IObject* node,void* gameobj,void* scene) { KX_GameObject* replica = ((KX_Scene*)scene)->AddNodeReplicaObject(node,(KX_GameObject*)gameobj); @@ -1196,7 +1198,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u static_cast<BL_ArmatureObject*>( parentobj ) ); releaseParent= false; - modifierDeformer->LoadShapeDrivers(blendobj->parent); + modifierDeformer->LoadShapeDrivers(parentobj); } else { @@ -1224,7 +1226,7 @@ void KX_Scene::ReplaceMesh(class CValue* obj,void* meshobj, bool use_gfx, bool u static_cast<BL_ArmatureObject*>( parentobj ) ); releaseParent= false; - shapeDeformer->LoadShapeDrivers(blendobj->parent); + shapeDeformer->LoadShapeDrivers(parentobj); } else { @@ -1596,52 +1598,74 @@ void KX_Scene::AddAnimatedObject(CValue* gameobj) m_animatedlist->Add(gameobj); } -void KX_Scene::UpdateAnimations(double curtime) +static void update_anim_thread_func(TaskPool *pool, void *taskdata, int UNUSED(threadid)) { - KX_GameObject *gameobj; + KX_GameObject *gameobj, *child; + CListValue *children; bool needs_update; + double curtime = *(double*)BLI_task_pool_userdata(pool); - for (int i=0; i<m_animatedlist->GetCount(); ++i) { - gameobj = (KX_GameObject*)m_animatedlist->GetValue(i); - - // Non-armature updates are fast enough, so just update them - needs_update = gameobj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE; + gameobj = (KX_GameObject*)taskdata; - if (!needs_update) { - // If we got here, we're looking to update an armature, so check its children meshes - // to see if we need to bother with a more expensive pose update - CListValue *children = gameobj->GetChildren(); - KX_GameObject *child; + // Non-armature updates are fast enough, so just update them + needs_update = gameobj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE; - bool has_mesh = false, has_non_mesh = false; + if (!needs_update) { + // If we got here, we're looking to update an armature, so check its children meshes + // to see if we need to bother with a more expensive pose update + children = gameobj->GetChildren(); - // Check for meshes that haven't been culled - for (int j=0; j<children->GetCount(); ++j) { - child = (KX_GameObject*)children->GetValue(j); + bool has_mesh = false, has_non_mesh = false; - if (!child->GetCulled()) { - needs_update = true; - break; - } + // Check for meshes that haven't been culled + for (int j=0; j<children->GetCount(); ++j) { + child = (KX_GameObject*)children->GetValue(j); - if (child->GetMeshCount() == 0) - has_non_mesh = true; - else - has_mesh = true; + if (!child->GetCulled()) { + needs_update = true; + break; } - // If we didn't find a non-culled mesh, check to see - // if we even have any meshes, and update if this - // armature has only non-mesh children. - if (!needs_update && !has_mesh && has_non_mesh) - needs_update = true; + if (child->GetMeshCount() == 0) + has_non_mesh = true; + else + has_mesh = true; + } + + // If we didn't find a non-culled mesh, check to see + // if we even have any meshes, and update if this + // armature has only non-mesh children. + if (!needs_update && !has_mesh && has_non_mesh) + needs_update = true; - children->Release(); + children->Release(); + } + + if (needs_update) { + gameobj->UpdateActionManager(curtime); + children = gameobj->GetChildren(); + + for (int j=0; j<children->GetCount(); ++j) { + child = (KX_GameObject*)children->GetValue(j); + + if (child->GetDeformer()) + child->GetDeformer()->Update(); } - if (needs_update) - gameobj->UpdateActionManager(curtime); + children->Release(); + } +} + +void KX_Scene::UpdateAnimations(double curtime) +{ + TaskPool *pool = BLI_task_pool_create(KX_GetActiveEngine()->GetTaskScheduler(), &curtime); + + for (int i=0; i<m_animatedlist->GetCount(); ++i) { + BLI_task_pool_push(pool, update_anim_thread_func, m_animatedlist->GetValue(i), false, TASK_PRIORITY_LOW); } + + BLI_task_pool_work_and_wait(pool); + BLI_task_pool_free(pool); } void KX_Scene::LogicUpdateFrame(double curtime, bool frame) |