doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.git] / source / gameengine / Ketsji / KX_IPO_SGController.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  * Scenegraph controller for ipos.
29  */
30
31 #if defined(_WIN64)
32 typedef unsigned __int64 uint_ptr;
33 #else
34 typedef unsigned long uint_ptr;
35 #endif
36
37 #if defined(WIN32) && !defined(FREE_WINDOWS)
38 // This warning tells us about truncation of __long__ stl-generated names.
39 // It can occasionally cause DevStudio to have internal compiler warnings.
40 #pragma warning( disable : 4786 )     
41 #endif
42
43 #include "KX_IPO_SGController.h"
44 #include "KX_ScalarInterpolator.h"
45 #include "KX_GameObject.h"
46 #include "KX_IPhysicsController.h"
47 #include "DNA_ipo_types.h"
48 #include "BLI_math.h"
49
50 // All objects should start on frame 1! Will we ever need an object to 
51 // start on another frame, the 1.0 should change.
52 KX_IpoSGController::KX_IpoSGController() 
53 : m_ipo_as_force(false),
54   m_ipo_add(false),
55   m_ipo_local(false),
56   m_modified(true),
57   m_ipotime(1.0),
58   m_ipo_start_initialized(false),
59   m_ipo_start_euler(0.0,0.0,0.0),
60   m_ipo_euler_initialized(false)
61 {
62         m_game_object = NULL;
63         for (int i=0; i < KX_MAX_IPO_CHANNELS; i++)
64                 m_ipo_channels_active[i] = false;
65 }
66
67
68 void KX_IpoSGController::SetOption(
69         int option,
70         int value)
71 {
72         switch (option) {
73         case SG_CONTR_IPO_IPO_AS_FORCE:
74                 m_ipo_as_force = (value != 0);
75                 m_modified = true;
76                 break;
77         case SG_CONTR_IPO_IPO_ADD:
78                 m_ipo_add = (value != 0);
79                 m_modified = true;
80                 break;
81         case SG_CONTR_IPO_RESET:
82                 if (m_ipo_start_initialized && value) {
83                         m_ipo_start_initialized = false;
84                         m_modified = true;
85                 }
86                 break;
87         case SG_CONTR_IPO_LOCAL:
88                 if (value/* && ((SG_Node*)m_pObject)->GetSGParent() == NULL*/) {
89                         // only accept local Ipo if the object has no parent
90                         m_ipo_local = true;
91                 } else {
92                         m_ipo_local = false;
93                 }
94                 m_modified = true;
95                 break;
96         default:
97                 ; /* just ignore the rest */
98         }
99 }
100
101         void 
102 KX_IpoSGController::UpdateSumoReference(
103         )
104 {
105         if (m_game_object) {
106
107         }
108 }
109
110         void 
111 KX_IpoSGController::SetGameObject(
112         KX_GameObject* go
113         )
114 {
115         m_game_object = go;
116 }
117
118
119
120 bool KX_IpoSGController::Update(double currentTime)
121 {
122         if (m_modified)
123         {
124                 T_InterpolatorList::iterator i;
125                 for (i = m_interpolators.begin(); !(i == m_interpolators.end()); ++i) {
126                         (*i)->Execute(m_ipotime);//currentTime);
127                 }
128                 
129                 SG_Spatial* ob = (SG_Spatial*)m_pObject;
130
131                 //initialization on the first frame of the IPO
132                 if (! m_ipo_start_initialized && currentTime > 0.0) {
133                         m_ipo_start_point = ob->GetLocalPosition();
134                         m_ipo_start_orient = ob->GetLocalOrientation();
135                         m_ipo_start_scale = ob->GetLocalScale();
136                         m_ipo_start_initialized = true;
137                         if (!m_ipo_euler_initialized) {
138                                 // do it only once to avoid angle discontinuities
139                                 m_ipo_start_orient.getEuler(m_ipo_start_euler[0], m_ipo_start_euler[1], m_ipo_start_euler[2]);
140                                 m_ipo_euler_initialized = true;
141                         }
142                 }
143
144                 //modifies position?
145                 if (m_ipo_channels_active[OB_LOC_X] || m_ipo_channels_active[OB_LOC_Y] || m_ipo_channels_active[OB_LOC_Z] || m_ipo_channels_active[OB_DLOC_X] || m_ipo_channels_active[OB_DLOC_Y] || m_ipo_channels_active[OB_DLOC_Z])
146                 {
147                         if (m_ipo_as_force == true) 
148                         {
149                                 if (m_game_object && ob && m_game_object->GetPhysicsController()) 
150                                 {
151                                         m_game_object->GetPhysicsController()->ApplyForce(m_ipo_local ?
152                                                 ob->GetWorldOrientation() * m_ipo_xform.GetPosition() :
153                                                 m_ipo_xform.GetPosition(), false);
154                                 }
155                         } 
156                         else
157                         {
158                                 // Local ipo should be defined with the object position at (0,0,0)
159                                 // Local transform is applied to the object based on initial position
160                                 MT_Point3 newPosition(0.0,0.0,0.0);
161                                 
162                                 if (!m_ipo_add)
163                                         newPosition = ob->GetLocalPosition();
164                                 //apply separate IPO channels if there is any data in them
165                                 //Loc and dLoc act by themselves or are additive
166                                 //LocX and dLocX
167                                 if (m_ipo_channels_active[OB_LOC_X]) {
168                                         newPosition[0] = (m_ipo_channels_active[OB_DLOC_X] ? m_ipo_xform.GetPosition()[0] + m_ipo_xform.GetDeltaPosition()[0] : m_ipo_xform.GetPosition()[0]);
169                                 }
170                                 else if (m_ipo_channels_active[OB_DLOC_X] && m_ipo_start_initialized) {
171                                         newPosition[0] = (((!m_ipo_add)?m_ipo_start_point[0]:0.0) + m_ipo_xform.GetDeltaPosition()[0]);
172                                 }
173                                 //LocY and dLocY
174                                 if (m_ipo_channels_active[OB_LOC_Y]) {
175                                         newPosition[1] = (m_ipo_channels_active[OB_DLOC_Y] ? m_ipo_xform.GetPosition()[1] + m_ipo_xform.GetDeltaPosition()[1] : m_ipo_xform.GetPosition()[1]);
176                                 }
177                                 else if (m_ipo_channels_active[OB_DLOC_Y] && m_ipo_start_initialized) {
178                                         newPosition[1] = (((!m_ipo_add)?m_ipo_start_point[1]:0.0) + m_ipo_xform.GetDeltaPosition()[1]);
179                                 }
180                                 //LocZ and dLocZ
181                                 if (m_ipo_channels_active[OB_LOC_Z]) {
182                                         newPosition[2] = (m_ipo_channels_active[OB_DLOC_Z] ? m_ipo_xform.GetPosition()[2] + m_ipo_xform.GetDeltaPosition()[2] : m_ipo_xform.GetPosition()[2]);
183                                 }
184                                 else if (m_ipo_channels_active[OB_DLOC_Z] && m_ipo_start_initialized) {
185                                         newPosition[2] = (((!m_ipo_add)?m_ipo_start_point[2]:0.0) + m_ipo_xform.GetDeltaPosition()[2]);
186                                 }
187                                 if (m_ipo_add) {
188                                         if (m_ipo_local)
189                                                 newPosition = m_ipo_start_point + m_ipo_start_scale*(m_ipo_start_orient*newPosition);
190                                         else
191                                                 newPosition = m_ipo_start_point + newPosition;
192                                 }
193                                 if (m_game_object)
194                                         m_game_object->NodeSetLocalPosition(newPosition);
195                         }
196                 }
197                 //modifies orientation?
198                 if (m_ipo_channels_active[OB_ROT_X] || m_ipo_channels_active[OB_ROT_Y] || m_ipo_channels_active[OB_ROT_Z] || m_ipo_channels_active[OB_DROT_X] || m_ipo_channels_active[OB_DROT_Y] || m_ipo_channels_active[OB_DROT_Z]) {
199                         if (m_ipo_as_force) {
200                                 
201                                 if (m_game_object && ob) {
202                                         m_game_object->ApplyTorque(m_ipo_local ?
203                                                 ob->GetWorldOrientation() * m_ipo_xform.GetEulerAngles() :
204                                                 m_ipo_xform.GetEulerAngles(), false);
205                                 }
206                         } else if (m_ipo_add) {
207                                 if (m_ipo_start_initialized) {
208                                         double yaw=0, pitch=0,  roll=0; //delta Euler angles
209
210                                         //RotX and dRotX
211                                         if (m_ipo_channels_active[OB_ROT_X])
212                                                 yaw += m_ipo_xform.GetEulerAngles()[0];
213                                         if (m_ipo_channels_active[OB_DROT_X])
214                                                 yaw += m_ipo_xform.GetDeltaEulerAngles()[0];
215
216                                         //RotY dRotY
217                                         if (m_ipo_channels_active[OB_ROT_Y])
218                                                 pitch += m_ipo_xform.GetEulerAngles()[1];
219                                         if (m_ipo_channels_active[OB_DROT_Y])
220                                                 pitch += m_ipo_xform.GetDeltaEulerAngles()[1];
221                                         
222                                         //RotZ and dRotZ
223                                         if (m_ipo_channels_active[OB_ROT_Z])
224                                                 roll += m_ipo_xform.GetEulerAngles()[2];
225                                         if (m_ipo_channels_active[OB_DROT_Z])
226                                                 roll += m_ipo_xform.GetDeltaEulerAngles()[2];
227
228                                         MT_Matrix3x3 rotation(MT_Vector3(yaw, pitch, roll));
229                                         if (m_ipo_local)
230                                                 rotation = m_ipo_start_orient * rotation;
231                                         else
232                                                 rotation = rotation * m_ipo_start_orient;
233                                         if (m_game_object)
234                                                 m_game_object->NodeSetLocalOrientation(rotation);
235                                 }
236                         } else if (m_ipo_channels_active[OB_ROT_X] || m_ipo_channels_active[OB_ROT_Y] || m_ipo_channels_active[OB_ROT_Z]) {
237                                 if (m_ipo_euler_initialized) {
238                                         // assume all channel absolute
239                                         // All 3 channels should be specified but if they are not, we will take 
240                                         // the value at the start of the game to avoid angle sign reversal 
241                                         double yaw=m_ipo_start_euler[0], pitch=m_ipo_start_euler[1], roll=m_ipo_start_euler[2];
242
243                                         //RotX and dRotX
244                                         if (m_ipo_channels_active[OB_ROT_X]) {
245                                                 yaw = (m_ipo_channels_active[OB_DROT_X] ? (m_ipo_xform.GetEulerAngles()[0] + m_ipo_xform.GetDeltaEulerAngles()[0]) : m_ipo_xform.GetEulerAngles()[0] );
246                                         }
247                                         else if (m_ipo_channels_active[OB_DROT_X]) {
248                                                 yaw += m_ipo_xform.GetDeltaEulerAngles()[0];
249                                         }
250
251                                         //RotY dRotY
252                                         if (m_ipo_channels_active[OB_ROT_Y]) {
253                                                 pitch = (m_ipo_channels_active[OB_DROT_Y] ? (m_ipo_xform.GetEulerAngles()[1] + m_ipo_xform.GetDeltaEulerAngles()[1]) : m_ipo_xform.GetEulerAngles()[1] );
254                                         }
255                                         else if (m_ipo_channels_active[OB_DROT_Y]) {
256                                                 pitch += m_ipo_xform.GetDeltaEulerAngles()[1];
257                                         }
258                                         
259                                         //RotZ and dRotZ
260                                         if (m_ipo_channels_active[OB_ROT_Z]) {
261                                                 roll = (m_ipo_channels_active[OB_DROT_Z] ? (m_ipo_xform.GetEulerAngles()[2] + m_ipo_xform.GetDeltaEulerAngles()[2]) : m_ipo_xform.GetEulerAngles()[2] );
262                                         }
263                                         else if (m_ipo_channels_active[OB_DROT_Z]) {
264                                                 roll += m_ipo_xform.GetDeltaEulerAngles()[2];
265                                         }
266                                         if (m_game_object)
267                                                 m_game_object->NodeSetLocalOrientation(MT_Vector3(yaw, pitch, roll));
268                                 }
269                         } else if (m_ipo_start_initialized) {
270                                 // only DROT, treat as Add
271                                 double yaw=0, pitch=0,  roll=0; //delta Euler angles
272
273                                 //dRotX
274                                 if (m_ipo_channels_active[OB_DROT_X])
275                                         yaw = m_ipo_xform.GetDeltaEulerAngles()[0];
276
277                                 //dRotY
278                                 if (m_ipo_channels_active[OB_DROT_Y])
279                                         pitch = m_ipo_xform.GetDeltaEulerAngles()[1];
280                                 
281                                 //dRotZ
282                                 if (m_ipo_channels_active[OB_DROT_Z])
283                                         roll = m_ipo_xform.GetDeltaEulerAngles()[2];
284
285                                 // dRot are always local
286                                 MT_Matrix3x3 rotation(MT_Vector3(yaw, pitch, roll));
287                                 rotation = m_ipo_start_orient * rotation;
288                                 if (m_game_object)
289                                         m_game_object->NodeSetLocalOrientation(rotation);
290                         }
291                 }
292                 //modifies scale?
293                 if (m_ipo_channels_active[OB_SIZE_X] || m_ipo_channels_active[OB_SIZE_Y] || m_ipo_channels_active[OB_SIZE_Z] || m_ipo_channels_active[OB_DSIZE_X] || m_ipo_channels_active[OB_DSIZE_Y] || m_ipo_channels_active[OB_DSIZE_Z]) {
294                         //default is no scale change
295                         MT_Vector3 newScale(1.0,1.0,1.0);
296                         if (!m_ipo_add)
297                                 newScale = ob->GetLocalScale();
298
299                         if (m_ipo_channels_active[OB_SIZE_X]) {
300                                 newScale[0] = (m_ipo_channels_active[OB_DSIZE_X] ? (m_ipo_xform.GetScaling()[0] + m_ipo_xform.GetDeltaScaling()[0]) : m_ipo_xform.GetScaling()[0]);
301                         }
302                         else if (m_ipo_channels_active[OB_DSIZE_X] && m_ipo_start_initialized) {
303                                 newScale[0] = (m_ipo_xform.GetDeltaScaling()[0] + ((!m_ipo_add)?m_ipo_start_scale[0]:0.0));
304                         }
305
306                         //RotY dRotY
307                         if (m_ipo_channels_active[OB_SIZE_Y]) {
308                                 newScale[1] = (m_ipo_channels_active[OB_DSIZE_Y] ? (m_ipo_xform.GetScaling()[1] + m_ipo_xform.GetDeltaScaling()[1]): m_ipo_xform.GetScaling()[1]);
309                         }
310                         else if (m_ipo_channels_active[OB_DSIZE_Y] && m_ipo_start_initialized) {
311                                 newScale[1] = (m_ipo_xform.GetDeltaScaling()[1] + ((!m_ipo_add)?m_ipo_start_scale[1]:0.0));
312                         }
313                         
314                         //RotZ and dRotZ
315                         if (m_ipo_channels_active[OB_SIZE_Z]) {
316                                 newScale[2] = (m_ipo_channels_active[OB_DSIZE_Z] ? (m_ipo_xform.GetScaling()[2] + m_ipo_xform.GetDeltaScaling()[2]) : m_ipo_xform.GetScaling()[2]);
317                         }
318                         else if (m_ipo_channels_active[OB_DSIZE_Z] && m_ipo_start_initialized) {
319                                 newScale[2] = (m_ipo_xform.GetDeltaScaling()[2] + ((!m_ipo_add)?m_ipo_start_scale[2]:1.0));
320                         }
321
322                         if (m_ipo_add) {
323                                 newScale = m_ipo_start_scale * newScale;
324                         }
325                         if (m_game_object)
326                                 m_game_object->NodeSetLocalScale(newScale);
327                 }
328
329                 m_modified=false;
330         }
331         return false;
332 }
333
334
335 void KX_IpoSGController::AddInterpolator(KX_IInterpolator* interp)
336 {
337         this->m_interpolators.push_back(interp);
338 }
339
340 SG_Controller*  KX_IpoSGController::GetReplica(class SG_Node* destnode)
341 {
342         KX_IpoSGController* iporeplica = new KX_IpoSGController(*this);
343         // clear object that ipo acts on in the replica.
344         iporeplica->ClearObject();
345         iporeplica->SetGameObject((KX_GameObject*)destnode->GetSGClientObject());
346
347         // dirty hack, ask Gino for a better solution in the ipo implementation
348         // hacken en zagen, in what we call datahiding, not written for replication :(
349
350         T_InterpolatorList oldlist = m_interpolators;
351         iporeplica->m_interpolators.clear();
352
353         T_InterpolatorList::iterator i;
354         for (i = oldlist.begin(); !(i == oldlist.end()); ++i) {
355                 KX_ScalarInterpolator* copyipo = new KX_ScalarInterpolator(*((KX_ScalarInterpolator*)*i));
356                 iporeplica->AddInterpolator(copyipo);
357
358                 MT_Scalar* scaal = ((KX_ScalarInterpolator*)*i)->GetTarget();
359                 uint_ptr orgbase = (uint_ptr)&m_ipo_xform;
360                 uint_ptr orgloc = (uint_ptr)scaal;
361                 uint_ptr offset = orgloc-orgbase;
362                 uint_ptr newaddrbase = (uint_ptr)&iporeplica->m_ipo_xform;
363                 newaddrbase += offset;
364                 MT_Scalar* blaptr = (MT_Scalar*) newaddrbase;
365                 copyipo->SetNewTarget((MT_Scalar*)blaptr);
366         }
367         
368         return iporeplica;
369 }
370
371 KX_IpoSGController::~KX_IpoSGController()
372 {
373
374         T_InterpolatorList::iterator i;
375         for (i = m_interpolators.begin(); !(i == m_interpolators.end()); ++i) {
376                 delete (*i);
377         }
378         
379 }