Cycles: svn merge -r41225:41232 ^/trunk/blender
[blender.git] / source / gameengine / Ketsji / BL_Action.cpp
index 77cb5de13988e85aaed4640169723c2317bc7e64..2da3fdfd225f26e0e430466a51c1d3c0ea7cc5ce 100644 (file)
@@ -1,6 +1,4 @@
-/**
- * $Id$
- *
+/*
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): none yet.
+ * Contributor(s): Mitchell Stokes.
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 
+/** \file BL_Action.cpp
+ *  \ingroup ketsji
+ */
+
 #include <cstdlib>
 
 #include "BL_Action.h"
 #include "BL_ArmatureObject.h"
+#include "BL_DeformableGameObject.h"
+#include "BL_ShapeDeformer.h"
 #include "KX_IpoConvert.h"
 #include "KX_GameObject.h"
 
@@ -46,44 +45,46 @@ extern "C" {
 #include "RNA_define.h"
 }
 
-BL_Action::BL_Action(class KX_GameObject* gameobj,
-                                       const char* name,
-                                       float start,
-                                       float end,
-                                       float blendin,
-                                       short play_mode,
-                                       short blend_mode,
-                                       float playback_speed)
+BL_Action::BL_Action(class KX_GameObject* gameobj)
 :
+       m_action(NULL),
+       m_pose(NULL),
+       m_blendpose(NULL),
+       m_blendinpose(NULL),
+       m_ptrrna(NULL),
        m_obj(gameobj),
-       m_startframe(start),
-       m_endframe(end),
-       m_blendin(blendin),
-       m_playmode(play_mode),
+       m_startframe(0.f),
+       m_endframe(0.f),
        m_endtime(0.f),
-       m_localtime(start),
+       m_localtime(0.f),
+       m_blendin(0.f),
        m_blendframe(0.f),
        m_blendstart(0.f),
-       m_speed(playback_speed),
-       m_pose(NULL),
-       m_blendpose(NULL),
-       m_sg_contr(NULL),
-       m_done(false)
+       m_speed(0.f),
+       m_priority(0),
+       m_playmode(0),
+       m_ipo_flags(0),
+       m_done(true),
+       m_calc_localtime(true)
 {
-       m_starttime = KX_GetActiveEngine()->GetFrameTime();
-       m_action = (bAction*)KX_GetActiveScene()->GetLogicManager()->GetActionByName(name);
-
-       if (!m_action) printf("Failed to load action: %s\n", name);
-
-       if (m_obj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE)
+       if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
        {
-               // Create an SG_Controller
-               m_sg_contr = BL_CreateIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter());
-               m_obj->GetSGNode()->AddSGController(m_sg_contr);
-               m_sg_contr->SetObject(m_obj->GetSGNode());
-               InitIPO();
+               BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
+
+               m_ptrrna = new PointerRNA();
+               RNA_id_pointer_create(&obj->GetArmatureObject()->id, m_ptrrna);
        }
+       else
+       {
+               BL_DeformableGameObject *obj = (BL_DeformableGameObject*)m_obj;
+               BL_ShapeDeformer *shape_deformer = dynamic_cast<BL_ShapeDeformer*>(obj->GetDeformer());
 
+               if (shape_deformer)
+               {
+                       m_ptrrna = new PointerRNA();
+                       RNA_id_pointer_create(&shape_deformer->GetKey()->id, m_ptrrna);
+               }
+       }
 }
 
 BL_Action::~BL_Action()
@@ -92,20 +93,150 @@ BL_Action::~BL_Action()
                game_free_pose(m_pose);
        if (m_blendpose)
                game_free_pose(m_blendpose);
-       if (m_sg_contr)
+       if (m_blendinpose)
+               game_free_pose(m_blendinpose);
+       if (m_ptrrna)
+               delete m_ptrrna;
+       ClearControllerList();
+}
+
+void BL_Action::ClearControllerList()
+{
+       // Clear out the controller list
+       std::vector<SG_Controller*>::iterator it;
+       for (it = m_sg_contr_list.begin(); it != m_sg_contr_list.end(); it++)
+       {
+               m_obj->GetSGNode()->RemoveSGController((*it));
+               delete *it;
+       }
+
+       m_sg_contr_list.clear();
+}
+
+bool BL_Action::Play(const char* name,
+                                       float start,
+                                       float end,
+                                       short priority,
+                                       float blendin,
+                                       short play_mode,
+                                       float layer_weight,
+                                       short ipo_flags,
+                                       float playback_speed)
+{
+
+       // Only start playing a new action if we're done, or if
+       // the new action has a higher priority
+       if (priority != 0 && !IsDone() && priority >= m_priority)
+               return false;
+       m_priority = priority;
+       bAction* prev_action = m_action;
+
+       // First try to load the action
+       m_action = (bAction*)KX_GetActiveScene()->GetLogicManager()->GetActionByName(name);
+       if (!m_action)
+       {
+               printf("Failed to load action: %s\n", name);
+               m_done = true;
+               return false;
+       }
+
+       if (prev_action != m_action)
+       {
+               // First get rid of any old controllers
+               ClearControllerList();
+
+               // Create an SG_Controller
+               SG_Controller *sg_contr = BL_CreateIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter());
+               m_sg_contr_list.push_back(sg_contr);
+               m_obj->GetSGNode()->AddSGController(sg_contr);
+               sg_contr->SetObject(m_obj->GetSGNode());
+
+               // Extra controllers
+               if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_LIGHT)
+               {
+                       sg_contr = BL_CreateLampIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter());
+                       m_sg_contr_list.push_back(sg_contr);
+                       m_obj->GetSGNode()->AddSGController(sg_contr);
+                       sg_contr->SetObject(m_obj->GetSGNode());
+               }
+               else if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_CAMERA)
+               {
+                       sg_contr = BL_CreateCameraIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter());
+                       m_sg_contr_list.push_back(sg_contr);
+                       m_obj->GetSGNode()->AddSGController(sg_contr);
+                       sg_contr->SetObject(m_obj->GetSGNode());
+               }
+       }
+       
+       m_ipo_flags = ipo_flags;
+       InitIPO();
+
+       // Setup blendin shapes/poses
+       if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
        {
-               m_obj->GetSGNode()->RemoveSGController(m_sg_contr);
-               delete m_sg_contr;
+               BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
+               obj->GetMRDPose(&m_blendinpose);
        }
+       else
+       {
+               BL_DeformableGameObject *obj = (BL_DeformableGameObject*)m_obj;
+               BL_ShapeDeformer *shape_deformer = dynamic_cast<BL_ShapeDeformer*>(obj->GetDeformer());
+               
+               if (shape_deformer && shape_deformer->GetKey())
+               {
+                       obj->GetShape(m_blendinshape);
+
+                       // Now that we have the previous blend shape saved, we can clear out the key to avoid any
+                       // further interference.
+                       KeyBlock *kb;
+                       for (kb=(KeyBlock*)shape_deformer->GetKey()->block.first; kb; kb=(KeyBlock*)kb->next)
+                               kb->curval = 0.f;
+               }
+       }
+
+       // Now that we have an action, we have something we can play
+       m_starttime = KX_GetActiveEngine()->GetFrameTime();
+       m_startframe = m_localtime = start;
+       m_endframe = end;
+       m_blendin = blendin;
+       m_playmode = play_mode;
+       m_endtime = 0.f;
+       m_blendframe = 0.f;
+       m_blendstart = 0.f;
+       m_speed = playback_speed;
+       m_layer_weight = layer_weight;
+       
+       m_done = false;
+
+       return true;
+}
+
+void BL_Action::Stop()
+{
+       m_done = true;
+}
+
+bool BL_Action::IsDone()
+{
+       return m_done;
 }
 
 void BL_Action::InitIPO()
 {
-               // Initialize the IPO
-               m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_RESET, true);
-               m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, false);
-               m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_ADD, false);
-               m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, false);
+       // Initialize the IPOs
+       std::vector<SG_Controller*>::iterator it;
+       for (it = m_sg_contr_list.begin(); it != m_sg_contr_list.end(); it++)
+       {
+               (*it)->SetOption(SG_Controller::SG_CONTR_IPO_RESET, true);
+               (*it)->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, m_ipo_flags & ACT_IPOFLAG_FORCE);
+               (*it)->SetOption(SG_Controller::SG_CONTR_IPO_IPO_ADD, m_ipo_flags & ACT_IPOFLAG_ADD);
+               (*it)->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, m_ipo_flags & ACT_IPOFLAG_LOCAL);
+       }
+}
+
+bAction *BL_Action::GetAction()
+{
+       return (IsDone()) ? NULL : m_action;
 }
 
 float BL_Action::GetFrame()
@@ -115,24 +246,25 @@ float BL_Action::GetFrame()
 
 void BL_Action::SetFrame(float frame)
 {
-       float dt;
-
        // Clamp the frame to the start and end frame
        if (frame < min(m_startframe, m_endframe))
                frame = min(m_startframe, m_endframe);
        else if (frame > max(m_startframe, m_endframe))
                frame = max(m_startframe, m_endframe);
+       
+       m_localtime = frame;
+       m_calc_localtime = false;
+}
 
-       // We don't set m_localtime directly since it's recalculated
-       // in the next update. So, we modify the value (m_starttime) 
-       // used to calculate m_localtime the next time SetLocalTime() is called.
-
-       dt = frame-m_startframe;
-
-       if (m_endframe < m_startframe)
-               dt = -dt;
+void BL_Action::SetPlayMode(short play_mode)
+{
+       m_playmode = play_mode;
+}
 
-       m_starttime -= dt / (KX_KetsjiEngine::GetAnimFrameRate()*m_speed);
+void BL_Action::SetTimes(float start, float end)
+{
+       m_startframe = start;
+       m_endframe = end;
 }
 
 void BL_Action::SetLocalTime(float curtime)
@@ -145,6 +277,47 @@ void BL_Action::SetLocalTime(float curtime)
        m_localtime = m_startframe + dt;
 }
 
+void BL_Action::ResetStartTime(float curtime)
+{
+       float dt = m_localtime - m_startframe;
+
+       m_starttime = curtime - dt / (KX_KetsjiEngine::GetAnimFrameRate()*m_speed);
+       SetLocalTime(curtime);
+}
+
+void BL_Action::IncrementBlending(float curtime)
+{
+       // Setup m_blendstart if we need to
+       if (m_blendstart == 0.f)
+               m_blendstart = curtime;
+       
+       // Bump the blend frame
+       m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
+
+       // Clamp
+       if (m_blendframe>m_blendin)
+               m_blendframe = m_blendin;
+}
+
+
+void BL_Action::BlendShape(Key* key, float srcweight, std::vector<float>& blendshape)
+{
+       vector<float>::const_iterator it;
+       float dstweight;
+       KeyBlock *kb;
+       
+       dstweight = 1.0F - srcweight;
+       //printf("Dst: %f\tSrc: %f\n", srcweight, dstweight);
+       for (it=blendshape.begin(), kb = (KeyBlock*)key->block.first; 
+                kb && it != blendshape.end(); 
+                kb = (KeyBlock*)kb->next, it++) {
+               //printf("OirgKeys: %f\t%f\n", kb->curval, (*it));
+               kb->curval = kb->curval * dstweight + (*it) * srcweight;
+               //printf("NewKey: %f\n", kb->curval);
+       }
+       //printf("\n");
+}
+
 void BL_Action::Update(float curtime)
 {
        // Don't bother if we're done with the animation
@@ -153,7 +326,13 @@ void BL_Action::Update(float curtime)
 
        curtime -= KX_KetsjiEngine::GetSuspendedDelta();
 
-       SetLocalTime(curtime);
+       if (m_calc_localtime)
+               SetLocalTime(curtime);
+       else
+       {
+               ResetStartTime(curtime);
+               m_calc_localtime = true;
+       }
 
        // Handle wrap around
        if (m_localtime < min(m_startframe, m_endframe) || m_localtime > max(m_startframe, m_endframe))
@@ -180,86 +359,88 @@ void BL_Action::Update(float curtime)
 
                        break;
                }
-
-               if (!m_done && m_sg_contr)
-                       InitIPO();
        }
 
        if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
        {
-               bPose* prev_pose = NULL;
                BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
                obj->GetPose(&m_pose);
 
-               // Save the old pose if we need to do some layer blending
-               if (m_blendmode != ACT_BLEND_NONE)
-                       obj->GetMRDPose(&prev_pose);
-
                // Extract the pose from the action
                {
-                       struct PointerRNA id_ptr;
                        Object *arm = obj->GetArmatureObject();
                        bPose *temp = arm->pose;
 
                        arm->pose = m_pose;
-                       RNA_id_pointer_create((ID*)arm, &id_ptr);
-                       animsys_evaluate_action(&id_ptr, m_action, NULL, m_localtime);
+                       animsys_evaluate_action(m_ptrrna, m_action, NULL, m_localtime);
 
                        arm->pose = temp;
                }
 
-               // Handle blending between layers
-               switch(m_blendmode)
-               {
-               case ACT_BLEND_MIX:
-                       game_blend_poses(m_pose, prev_pose, 0.5f);
-                       break;
-               case ACT_BLEND_NONE:
-               default:
-                       break;
-               }
-
-               // Handle blending between actions
+               // Handle blending between armature actions
                if (m_blendin && m_blendframe<m_blendin)
                {
-                       if (!m_blendpose)
-                       {
-                               obj->GetMRDPose(&m_blendpose);
-                               m_blendstart = curtime;
-                       }
+                       IncrementBlending(curtime);
 
                        // Calculate weight
                        float weight = 1.f - (m_blendframe/m_blendin);
-                       game_blend_poses(m_pose, m_blendpose, weight);
 
-                       // Bump the blend frame
-                       m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
-
-                       // Clamp
-                       if (m_blendframe>m_blendin)
-                               m_blendframe = m_blendin;
+                       // Blend the poses
+                       game_blend_poses(m_pose, m_blendinpose, weight);
                }
-               else
+
+
+               // Handle layer blending
+               if (m_layer_weight >= 0)
                {
-                       if (m_blendpose)
-                       {
-                               game_free_pose(m_blendpose);
-                               m_blendpose = NULL;
-                       }
+                       obj->GetMRDPose(&m_blendpose);
+                       game_blend_poses(m_pose, m_blendpose, m_layer_weight);
                }
 
                obj->SetPose(m_pose);
 
                obj->SetActiveAction(NULL, 0, curtime);
-
-               if (prev_pose)
-                       game_free_pose(prev_pose);
        }
        else
        {
-               InitIPO();
-               m_sg_contr->SetSimulatedTime(m_localtime);
-               m_obj->GetSGNode()->UpdateWorldData(m_localtime);
-               m_obj->UpdateTransform();
+               BL_DeformableGameObject *obj = (BL_DeformableGameObject*)m_obj;
+               BL_ShapeDeformer *shape_deformer = dynamic_cast<BL_ShapeDeformer*>(obj->GetDeformer());
+
+               // Handle shape actions if we have any
+               if (shape_deformer && shape_deformer->GetKey())
+               {
+                       Key *key = shape_deformer->GetKey();
+
+
+                       animsys_evaluate_action(m_ptrrna, m_action, NULL, m_localtime);
+
+                       // Handle blending between shape actions
+                       if (m_blendin && m_blendframe < m_blendin)
+                       {
+                               IncrementBlending(curtime);
+
+                               float weight = 1.f - (m_blendframe/m_blendin);
+
+                               // We go through and clear out the keyblocks so there isn't any interference
+                               // from other shape actions
+                               KeyBlock *kb;
+                               for (kb=(KeyBlock*)key->block.first; kb; kb=(KeyBlock*)kb->next)
+                                       kb->curval = 0.f;
+
+                               // Now blend the shape
+                               BlendShape(key, weight, m_blendinshape);
+                       }
+
+                       // Handle layer blending
+                       if (m_layer_weight >= 0)
+                       {
+                               obj->GetShape(m_blendshape);
+                               BlendShape(key, m_layer_weight, m_blendshape);
+                       }
+
+                       obj->SetActiveAction(NULL, 0, curtime);
+               }
+
+               m_obj->UpdateIPO(m_localtime, m_ipo_flags & ACT_IPOFLAG_CHILD);
        }
 }