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).
This commit is contained in:
Mitchell Stokes 2014-04-06 16:30:59 -07:00
parent be8b4b8b0c
commit fe05f97841
14 changed files with 226 additions and 248 deletions

@ -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;

@ -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;

@ -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)

@ -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,47 +487,31 @@ 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)
{
@ -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;
}

@ -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__ */

@ -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;

@ -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;
}

@ -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();

@ -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())
// We do everything in UpdateInternal() now so we can thread it.
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;
}
RAS_Deformer *BL_SkinDeformer::GetReplica()
@ -342,15 +306,10 @@ bool BL_SkinDeformer::UpdateInternal(bool shape_applied)
m_armobj->ApplyPose();
switch (m_armobj->GetVertDeformType())
{
case ARM_VDEF_BGE_CPU:
if (m_armobj->GetVertDeformType() == ARM_VDEF_BGE_CPU)
BGEDeformVerts();
break;
case ARM_VDEF_BLENDER:
default:
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;
}

@ -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);
if (m_layer_weight >= 0)
obj->GetPose(&m_blendpose);
// 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);
arm->pose = temp;
}
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->BlendInPose(m_blendpose, m_layer_weight, m_blendmode);
obj->SetPose(m_pose);
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)

@ -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;

@ -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);
}

@ -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();

@ -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,13 +1598,14 @@ 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);
gameobj = (KX_GameObject*)taskdata;
// Non-armature updates are fast enough, so just update them
needs_update = gameobj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE;
@ -1610,8 +1613,7 @@ void KX_Scene::UpdateAnimations(double curtime)
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;
children = gameobj->GetChildren();
bool has_mesh = false, has_non_mesh = false;
@ -1639,9 +1641,31 @@ void KX_Scene::UpdateAnimations(double curtime)
children->Release();
}
if (needs_update)
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();
}
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)