BGE Animations: BL_Action now creates a PointerRNA only when constructed instead...
[blender.git] / source / gameengine / Ketsji / BL_Action.cpp
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <cstdlib>
31
32 #include "BL_Action.h"
33 #include "BL_ArmatureObject.h"
34 #include "KX_IpoConvert.h"
35 #include "KX_GameObject.h"
36
37 // These three are for getting the action from the logic manager
38 #include "KX_Scene.h"
39 #include "KX_PythonInit.h"
40 #include "SCA_LogicManager.h"
41
42 extern "C" {
43 #include "BKE_animsys.h"
44 #include "BKE_action.h"
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47 }
48
49 BL_Action::BL_Action(class KX_GameObject* gameobj)
50 :
51         m_obj(gameobj),
52         m_startframe(0.f),
53         m_endframe(0.f),
54         m_blendin(0.f),
55         m_playmode(0),
56         m_endtime(0.f),
57         m_localtime(0.f),
58         m_blendframe(0.f),
59         m_blendstart(0.f),
60         m_speed(0.f),
61         m_ipo_flags(0),
62         m_pose(NULL),
63         m_blendpose(NULL),
64         m_sg_contr(NULL),
65         m_ptrrna(NULL),
66         m_done(true)
67 {
68         if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
69         {
70                 BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
71
72                 m_ptrrna = new PointerRNA();
73                 RNA_id_pointer_create((ID*)obj->GetArmatureObject(), m_ptrrna);
74         }
75 }
76
77 BL_Action::~BL_Action()
78 {
79         if (m_pose)
80                 game_free_pose(m_pose);
81         if (m_blendpose)
82                 game_free_pose(m_blendpose);
83         if (m_sg_contr)
84         {
85                 m_obj->GetSGNode()->RemoveSGController(m_sg_contr);
86                 delete m_sg_contr;
87         }
88         if (m_ptrrna)
89                 delete m_ptrrna;
90 }
91
92 void BL_Action::Play(const char* name,
93                                         float start,
94                                         float end,
95                                         float blendin,
96                                         short play_mode,
97                                         short blend_mode,
98                                         short ipo_flags,
99                                         float playback_speed)
100 {
101         bAction* prev_action = m_action;
102
103         // First try to load the action
104         m_action = (bAction*)KX_GetActiveScene()->GetLogicManager()->GetActionByName(name);
105         if (!m_action)
106         {
107                 printf("Failed to load action: %s\n", name);
108                 m_done = true;
109                 return;
110         }
111
112         if (prev_action != m_action)
113         {
114                 // Create an SG_Controller
115                 m_sg_contr = BL_CreateIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter());
116                 m_obj->GetSGNode()->AddSGController(m_sg_contr);
117                 m_sg_contr->SetObject(m_obj->GetSGNode());
118         }
119         
120         m_ipo_flags = ipo_flags;
121         InitIPO();
122
123         // Now that we have an action, we have something we can play
124         m_starttime = KX_GetActiveEngine()->GetFrameTime();
125         m_startframe = m_localtime = start;
126         m_endframe = end;
127         m_blendin = blendin;
128         m_playmode = play_mode;
129         m_endtime = 0.f;
130         m_blendframe = 0.f;
131         m_blendstart = 0.f;
132         m_speed = playback_speed;
133         
134         m_done = false;
135 }
136
137 void BL_Action::Stop()
138 {
139         m_done = true;
140 }
141
142 void BL_Action::InitIPO()
143 {
144                 // Initialize the IPO
145                 m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_RESET, true);
146                 m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, m_ipo_flags & ACT_IPOFLAG_FORCE);
147                 m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_ADD, m_ipo_flags & ACT_IPOFLAG_ADD);
148                 m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, m_ipo_flags & ACT_IPOFLAG_LOCAL);
149 }
150
151 float BL_Action::GetFrame()
152 {
153         return m_localtime;
154 }
155
156 void BL_Action::SetFrame(float frame)
157 {
158         float dt;
159
160         // Clamp the frame to the start and end frame
161         if (frame < min(m_startframe, m_endframe))
162                 frame = min(m_startframe, m_endframe);
163         else if (frame > max(m_startframe, m_endframe))
164                 frame = max(m_startframe, m_endframe);
165
166         // We don't set m_localtime directly since it's recalculated
167         // in the next update. So, we modify the value (m_starttime) 
168         // used to calculate m_localtime the next time SetLocalTime() is called.
169
170         dt = frame-m_startframe;
171
172         if (m_endframe < m_startframe)
173                 dt = -dt;
174
175         m_starttime -= dt / (KX_KetsjiEngine::GetAnimFrameRate()*m_speed);
176 }
177
178 void BL_Action::SetLocalTime(float curtime)
179 {
180         float dt = (curtime-m_starttime)*KX_KetsjiEngine::GetAnimFrameRate()*m_speed;
181
182         if (m_endframe < m_startframe)
183                 dt = -dt;
184
185         m_localtime = m_startframe + dt;
186 }
187
188 void BL_Action::Update(float curtime)
189 {
190         // Don't bother if we're done with the animation
191         if (m_done)
192                 return;
193
194         curtime -= KX_KetsjiEngine::GetSuspendedDelta();
195
196         SetLocalTime(curtime);
197
198         // Handle wrap around
199         if (m_localtime < min(m_startframe, m_endframe) || m_localtime > max(m_startframe, m_endframe))
200         {
201                 switch(m_playmode)
202                 {
203                 case ACT_MODE_PLAY:
204                         // Clamp
205                         m_localtime = m_endframe;
206                         m_done = true;
207                         break;
208                 case ACT_MODE_LOOP:
209                         // Put the time back to the beginning
210                         m_localtime = m_startframe;
211                         m_starttime = curtime;
212                         break;
213                 case ACT_MODE_PING_PONG:
214                         // Swap the start and end frames
215                         float temp = m_startframe;
216                         m_startframe = m_endframe;
217                         m_endframe = temp;
218
219                         m_starttime = curtime;
220
221                         break;
222                 }
223
224                 if (!m_done && m_sg_contr)
225                         InitIPO();
226         }
227
228         if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
229         {
230                 BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
231                 obj->GetPose(&m_pose);
232
233                 // Extract the pose from the action
234                 {
235                         Object *arm = obj->GetArmatureObject();
236                         bPose *temp = arm->pose;
237
238                         arm->pose = m_pose;
239                         animsys_evaluate_action(m_ptrrna, m_action, NULL, m_localtime);
240
241                         arm->pose = temp;
242                 }
243
244                 // Handle blending between actions
245                 if (m_blendin && m_blendframe<m_blendin)
246                 {
247                         if (!m_blendpose)
248                         {
249                                 obj->GetMRDPose(&m_blendpose);
250                                 m_blendstart = curtime;
251                         }
252
253                         // Calculate weight
254                         float weight = 1.f - (m_blendframe/m_blendin);
255                         game_blend_poses(m_pose, m_blendpose, weight);
256
257                         // Bump the blend frame
258                         m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
259
260                         // Clamp
261                         if (m_blendframe>m_blendin)
262                                 m_blendframe = m_blendin;
263                 }
264
265                 obj->SetPose(m_pose);
266
267                 obj->SetActiveAction(NULL, 0, curtime);
268         }
269         else
270         {
271                 InitIPO();
272                 m_obj->UpdateIPO(m_localtime, m_ipo_flags & ACT_IPOFLAG_CHILD);
273         }
274 }