78b6247b775988ab5883c59e5766de1dfe4c1231
[blender.git] / source / gameengine / Ketsji / KX_SteeringActuator.cpp
1 /**
2 * Add steering behaviors
3 *
4 * $Id$
5 *
6 * ***** BEGIN GPL LICENSE BLOCK *****
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version. The Blender
12 * Foundation also sells licenses for use in proprietary software under
13 * the Blender License.  See http://www.blender.org/BL/ for information
14 * about this.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 *
25 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26 * All rights reserved.
27 *
28 * The Original Code is: all of this file.
29 *
30 * Contributor(s): none yet.
31 *
32 * ***** END GPL LICENSE BLOCK *****
33 */
34
35 #include "BLI_math.h"
36 #include "KX_SteeringActuator.h"
37 #include "KX_GameObject.h"
38 #include "KX_NavMeshObject.h"
39 #include "KX_ObstacleSimulation.h"
40 #include "KX_PythonInit.h"
41
42
43 /* ------------------------------------------------------------------------- */
44 /* Native functions                                                          */
45 /* ------------------------------------------------------------------------- */
46
47 KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj, 
48                                                                         int mode,
49                                                                         KX_GameObject *target,
50                                                                         KX_GameObject *navmesh,
51                                                                         float distance,
52                                                                         float velocity, 
53                                                                         float acceleration,                                                                     
54                                                                         float turnspeed,
55                                                                         bool  isSelfTerminated,
56                                                                         int pathUpdatePeriod,
57                                                                         KX_ObstacleSimulation* simulation,
58                                                                         bool enableVisualization)        : 
59         SCA_IActuator(gameobj, KX_ACT_STEERING),
60         m_mode(mode),
61         m_target(target),
62         m_distance(distance),
63         m_velocity(velocity),
64         m_acceleration(acceleration),
65         m_turnspeed(turnspeed),
66         m_isSelfTerminated(isSelfTerminated),
67         m_pathUpdatePeriod(pathUpdatePeriod),
68         m_updateTime(0),
69         m_isActive(false),      
70         m_simulation(simulation),       
71         m_enableVisualization(enableVisualization),
72         m_obstacle(NULL),
73         m_pathLen(0),
74         m_wayPointIdx(-1)
75 {
76         m_navmesh = static_cast<KX_NavMeshObject*>(navmesh);
77         if (m_navmesh)
78                 m_navmesh->RegisterActuator(this);
79         if (m_target)
80                 m_target->RegisterActuator(this);
81         
82         if (m_simulation)
83                 m_obstacle = m_simulation->GetObstacle((KX_GameObject*)gameobj);
84
85
86 KX_SteeringActuator::~KX_SteeringActuator()
87 {
88         if (m_navmesh)
89                 m_navmesh->UnregisterActuator(this);
90         if (m_target)
91                 m_target->UnregisterActuator(this);
92
93
94 CValue* KX_SteeringActuator::GetReplica()
95 {
96         KX_SteeringActuator* replica = new KX_SteeringActuator(*this);
97         // replication just copy the m_base pointer => common random generator
98         replica->ProcessReplica();
99         return replica;
100 }
101
102 void KX_SteeringActuator::ProcessReplica()
103 {
104         if (m_target)
105                 m_target->RegisterActuator(this);
106         if (m_navmesh)
107                 m_navmesh->RegisterActuator(this);
108         SCA_IActuator::ProcessReplica();
109 }
110
111
112 bool KX_SteeringActuator::UnlinkObject(SCA_IObject* clientobj)
113 {
114         if (clientobj == m_target)
115         {
116                 m_target = NULL;
117                 return true;
118         }
119         else if (clientobj == m_navmesh)
120         {
121                 m_navmesh = NULL;
122                 return true;
123         }
124         return false;
125 }
126
127 void KX_SteeringActuator::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
128 {
129         void **h_obj = (*obj_map)[m_target];
130         if (h_obj) {
131                 if (m_target)
132                         m_target->UnregisterActuator(this);
133                 m_target = (KX_GameObject*)(*h_obj);
134                 m_target->RegisterActuator(this);
135         }
136
137         h_obj = (*obj_map)[m_navmesh];
138         if (h_obj) {
139                 if (m_navmesh)
140                         m_navmesh->UnregisterActuator(this);
141                 m_navmesh = (KX_NavMeshObject*)(*h_obj);
142                 m_navmesh->RegisterActuator(this);
143         }
144 }
145
146 bool KX_SteeringActuator::Update(double curtime, bool frame)
147 {
148         if (frame)
149         {
150                 double delta =  curtime - m_updateTime;
151                 m_updateTime = curtime;
152                 
153                 if (m_posevent && !m_isActive)
154                 {
155                         delta = 0;
156                         m_pathUpdateTime = -1;
157                         m_updateTime = curtime;
158                         m_isActive = true;
159                 }
160                 bool bNegativeEvent = IsNegativeEvent();
161                 if (bNegativeEvent)
162                         m_isActive = false;
163
164                 RemoveAllEvents();
165
166                 if (!delta)
167                         return true;
168
169                 if (bNegativeEvent || !m_target)
170                         return false; // do nothing on negative events
171
172                 KX_GameObject *obj = (KX_GameObject*) GetParent();
173                 const MT_Point3& mypos = obj->NodeGetWorldPosition();
174                 const MT_Point3& targpos = m_target->NodeGetWorldPosition();
175                 MT_Vector3 vectotarg = targpos - mypos;
176                 MT_Vector3 steervec = MT_Vector3(0, 0, 0);
177                 bool apply_steerforce = false;
178                 bool terminate = true;
179
180                 switch (m_mode) {
181                         case KX_STEERING_SEEK:
182                                 if (vectotarg.length2()>m_distance*m_distance)
183                                 {
184                                         terminate = false;
185                                         steervec = vectotarg;
186                                         steervec.normalize();
187                                         apply_steerforce = true;
188                                 }
189                                 break;
190                         case KX_STEERING_FLEE:
191                                 if (vectotarg.length2()<m_distance*m_distance)
192                                 {
193                                         terminate = false;
194                                         steervec = -vectotarg;
195                                         steervec.normalize();
196                                         apply_steerforce = true;
197                                 }
198                                 break;
199                         case KX_STEERING_PATHFOLLOWING:
200                                 if (m_navmesh && vectotarg.length2()>m_distance*m_distance)
201                                 {
202                                         terminate = false;
203
204                                         static const MT_Scalar WAYPOINT_RADIUS(0.25);
205
206                                         if (m_pathUpdateTime<0 || (m_pathUpdatePeriod>=0 && 
207                                                                                                 curtime - m_pathUpdateTime>((double)m_pathUpdatePeriod/1000)))
208                                         {
209                                                 m_pathUpdateTime = curtime;
210                                                 m_pathLen = m_navmesh->FindPath(mypos, targpos, m_path, MAX_PATH_LENGTH);
211                                                 m_wayPointIdx = m_pathLen > 1 ? 1 : -1;
212                                         }
213
214                                         if (m_wayPointIdx>0)
215                                         {
216                                                 MT_Vector3 waypoint(&m_path[3*m_wayPointIdx]);
217                                                 if ((waypoint-mypos).length2()<WAYPOINT_RADIUS*WAYPOINT_RADIUS)
218                                                 {
219                                                         m_wayPointIdx++;
220                                                         if (m_wayPointIdx>=m_pathLen)
221                                                         {
222                                                                 m_wayPointIdx = -1;
223                                                                 terminate = true;
224                                                         }
225                                                         else
226                                                                 waypoint.setValue(&m_path[3*m_wayPointIdx]);
227                                                 }
228
229                                                 steervec = waypoint - mypos;
230                                                 apply_steerforce = true;
231
232                                                 
233                                                 if (m_enableVisualization)
234                                                 {
235                                                         //debug draw
236                                                         static const MT_Vector3 PATH_COLOR(1,0,0);
237                                                         m_navmesh->DrawPath(m_path, m_pathLen, PATH_COLOR);
238                                                 }
239                                         }       
240                                         
241                                 }
242                                 break;
243                 }
244
245                 if (apply_steerforce)
246                 {
247                         bool isdyna = obj->IsDynamic();
248                         if (isdyna)
249                                 steervec.z() = 0;
250                         if (!steervec.fuzzyZero())
251                                 steervec.normalize();
252                         MT_Vector3 newvel = m_velocity*steervec;
253
254                         //adjust velocity to avoid obstacles
255                         if (m_simulation && m_obstacle && !newvel.fuzzyZero())
256                         {
257                                 if (m_enableVisualization)
258                                         KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(1.,0.,0.));
259                                 m_simulation->AdjustObstacleVelocity(m_obstacle, m_mode!=KX_STEERING_PATHFOLLOWING ? m_navmesh : NULL, 
260                                                                 newvel, m_acceleration*delta, m_turnspeed/180.0f*M_PI*delta);
261                                 if (m_enableVisualization)
262                                         KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(0.,1.,0.));
263                         }
264
265                         if (isdyna)
266                         {
267                                 //temporary solution: set 2D steering velocity directly to obj
268                                 //correct way is to apply physical force
269                                 MT_Vector3 curvel = obj->GetLinearVelocity();
270                                 newvel.z() = curvel.z();                        
271                                 obj->setLinearVelocity(newvel, false);
272                         }
273                         else
274                         {
275                                 MT_Vector3 movement = delta*newvel;
276                                 obj->ApplyMovement(movement, false);
277                         }
278                 }
279
280                 if (terminate && m_isSelfTerminated)
281                         return false;
282         }
283
284         return true;
285 }
286
287 #ifndef DISABLE_PYTHON
288
289 /* ------------------------------------------------------------------------- */
290 /* Python functions                                                          */
291 /* ------------------------------------------------------------------------- */
292
293 /* Integration hooks ------------------------------------------------------- */
294 PyTypeObject KX_SteeringActuator::Type = {
295         PyVarObject_HEAD_INIT(NULL, 0)
296         "KX_SteeringActuator",
297         sizeof(PyObjectPlus_Proxy),
298         0,
299         py_base_dealloc,
300         0,
301         0,
302         0,
303         0,
304         py_base_repr,
305         0,0,0,0,0,0,0,0,0,
306         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
307         0,0,0,0,0,0,0,
308         Methods,
309         0,
310         0,
311         &SCA_IActuator::Type,
312         0,0,0,0,0,0,
313         py_base_new
314 };
315
316 PyMethodDef KX_SteeringActuator::Methods[] = {
317         {NULL,NULL} //Sentinel
318 };
319
320 PyAttributeDef KX_SteeringActuator::Attributes[] = {
321         KX_PYATTRIBUTE_INT_RW("behaviour", KX_STEERING_NODEF+1, KX_STEERING_MAX-1, true, KX_SteeringActuator, m_mode),
322         KX_PYATTRIBUTE_RW_FUNCTION("target", KX_SteeringActuator, pyattr_get_target, pyattr_set_target),
323         KX_PYATTRIBUTE_RW_FUNCTION("navmesh", KX_SteeringActuator, pyattr_get_navmesh, pyattr_set_navmesh),
324         KX_PYATTRIBUTE_FLOAT_RW("distance", 0.0f, 1000.0f, KX_SteeringActuator, m_distance),
325         KX_PYATTRIBUTE_FLOAT_RW("velocity", 0.0f, 1000.0f, KX_SteeringActuator, m_velocity),
326         KX_PYATTRIBUTE_FLOAT_RW("acceleration", 0.0f, 1000.0f, KX_SteeringActuator, m_acceleration),
327         KX_PYATTRIBUTE_FLOAT_RW("turnspeed", 0.0f, 720.0f, KX_SteeringActuator, m_turnspeed),
328         KX_PYATTRIBUTE_BOOL_RW("selfterminated", KX_SteeringActuator, m_isSelfTerminated),
329         KX_PYATTRIBUTE_BOOL_RW("enableVisualization", KX_SteeringActuator, m_enableVisualization),
330         KX_PYATTRIBUTE_INT_RW("pathUpdatePeriod", -1, 100000, true, KX_SteeringActuator, m_pathUpdatePeriod),
331         { NULL }        //Sentinel
332 };
333
334 PyObject* KX_SteeringActuator::pyattr_get_target(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
335 {
336         KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
337         if (!actuator->m_target)        
338                 Py_RETURN_NONE;
339         else
340                 return actuator->m_target->GetProxy();
341 }
342
343 int KX_SteeringActuator::pyattr_set_target(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
344 {
345         KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
346         KX_GameObject *gameobj;
347
348         if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: KX_SteeringActuator"))
349                 return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
350
351         if (actuator->m_target != NULL)
352                 actuator->m_target->UnregisterActuator(actuator);       
353
354         actuator->m_target = (KX_GameObject*) gameobj;
355
356         if (actuator->m_target)
357                 actuator->m_target->RegisterActuator(actuator);
358
359         return PY_SET_ATTR_SUCCESS;
360 }
361
362 PyObject* KX_SteeringActuator::pyattr_get_navmesh(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
363 {
364         KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
365         if (!actuator->m_navmesh)       
366                 Py_RETURN_NONE;
367         else
368                 return actuator->m_navmesh->GetProxy();
369 }
370
371 int KX_SteeringActuator::pyattr_set_navmesh(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
372 {
373         KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
374         KX_GameObject *gameobj;
375
376         if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: KX_SteeringActuator"))
377                 return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
378
379         if (!PyObject_TypeCheck(value, &KX_NavMeshObject::Type))
380         {
381                 PyErr_Format(PyExc_TypeError, "KX_NavMeshObject is expected");
382                 return PY_SET_ATTR_FAIL;
383         }
384
385         if (actuator->m_navmesh != NULL)
386                 actuator->m_navmesh->UnregisterActuator(actuator);      
387
388         actuator->m_navmesh = static_cast<KX_NavMeshObject*>(gameobj);
389
390         if (actuator->m_navmesh)
391                 actuator->m_navmesh->RegisterActuator(actuator);
392
393         return PY_SET_ATTR_SUCCESS;
394 }
395
396 #endif // DISABLE_PYTHON
397
398 /* eof */
399