fa5f5215aa427d058c21238a096aaa72ae92dffa
[blender-staging.git] / source / gameengine / Ketsji / KX_RaySensor.cpp
1 /**
2  * Cast a ray and feel for objects
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL/BL DUAL 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include "KX_RaySensor.h"
36 #include "SCA_EventManager.h"
37 #include "SCA_RandomEventManager.h"
38 #include "SCA_LogicManager.h"
39 #include "SCA_IObject.h"
40 #include "KX_ClientObjectInfo.h"
41 #include "KX_GameObject.h"
42 #include "KX_Scene.h"
43
44 #include "KX_RayCast.h"
45 #include "PHY_IPhysicsEnvironment.h"
46 #include "PHY_IPhysicsController.h"
47 #include "KX_IPhysicsController.h"
48
49
50 #ifdef HAVE_CONFIG_H
51 #include <config.h>
52 #endif
53
54 KX_RaySensor::KX_RaySensor(class SCA_EventManager* eventmgr,
55                                         SCA_IObject* gameobj,
56                                         const STR_String& propname,
57                                         bool bFindMaterial,
58                                         double distance,
59                                         int axis,
60                                         KX_Scene* ketsjiScene,
61                                         PyTypeObject* T)
62                         : SCA_ISensor(gameobj,eventmgr, T),
63                                         m_propertyname(propname),
64                                         m_bFindMaterial(bFindMaterial),
65                                         m_distance(distance),
66                                         m_scene(ketsjiScene),
67                                         m_bTriggered(false),
68                                         m_axis(axis),
69                                         m_rayHit(false),
70                                         m_hitObject(NULL)
71
72                                 
73 {
74
75 }
76
77
78
79 KX_RaySensor::~KX_RaySensor() 
80 {
81     /* Nothing to be done here. */
82 }
83
84
85
86 CValue* KX_RaySensor::GetReplica()
87 {
88         CValue* replica = new KX_RaySensor(*this);
89         // this will copy properties and so on...
90         CValue::AddDataToReplica(replica);
91
92         return replica;
93 }
94
95
96
97 bool KX_RaySensor::IsPositiveTrigger()
98 {
99         bool result = m_rayHit;
100
101         if (m_invert)
102                 result = !result;
103         
104         return result;
105 }
106
107 bool KX_RaySensor::RayHit(KX_ClientObjectInfo* info, MT_Point3& hit_point, MT_Vector3& hit_normal, void* const data)
108 {
109         KX_GameObject* obj = (KX_GameObject*)GetParent();
110         SCA_IObject *hitgameobj = info->m_gameobject;
111         if (hitgameobj == obj || info->m_type > KX_ClientObjectInfo::ACTOR)
112         {
113                 // false hit
114                 return false;
115         }
116         
117         bool bFound = false;
118         if (m_propertyname.Length() == 0)
119         {
120                 bFound = true;
121         }
122         else
123         {
124                 if (m_bFindMaterial)
125                 {
126                         if (info->m_auxilary_info)
127                         {
128                                 bFound = (m_propertyname== ((char*)info->m_auxilary_info));
129                         }
130                 }
131                 else
132                 {
133                         bFound = hitgameobj->GetProperty(m_propertyname) != NULL;
134                 }
135         }
136
137         if (bFound)
138         {
139                 m_rayHit = true;
140                 m_hitObject = hitgameobj;
141                 m_hitPosition = hit_point;
142                 m_hitNormal = hit_normal;
143                         
144         }
145         
146         return true;
147 }
148
149 bool KX_RaySensor::Evaluate(CValue* event)
150 {
151         bool result = false;
152         m_rayHit = false; 
153         m_hitObject = NULL;
154         m_hitPosition = MT_Vector3(0,0,0);
155         m_hitNormal = MT_Vector3(1,0,0);
156         
157         KX_GameObject* obj = (KX_GameObject*)GetParent();
158         MT_Point3 frompoint = obj->NodeGetWorldPosition();
159         MT_Matrix3x3 matje = obj->NodeGetWorldOrientation();
160         MT_Matrix3x3 invmat = matje.inverse();
161         
162         MT_Vector3 todir;
163         switch (m_axis)
164         {
165         case 1: // X
166                 {
167                         todir[0] = invmat[0][0];
168                         todir[1] = invmat[0][1];
169                         todir[2] = invmat[0][2];
170                         break;
171                 }
172         case 0: // Y
173                 {
174                         todir[0] = invmat[1][0];
175                         todir[1] = invmat[1][1];
176                         todir[2] = invmat[1][2];
177                         break;
178                 }
179         case 2: // Z
180                 {
181                         todir[0] = invmat[2][0];
182                         todir[1] = invmat[2][1];
183                         todir[2] = invmat[2][2];
184                         break;
185                 }
186         case 3: // -X
187                 {
188                         todir[0] = -invmat[0][0];
189                         todir[1] = -invmat[0][1];
190                         todir[2] = -invmat[0][2];
191                         break;
192                 }
193         case 4: // -Y
194                 {
195                         todir[0] = -invmat[1][0];
196                         todir[1] = -invmat[1][1];
197                         todir[2] = -invmat[1][2];
198                         break;
199                 }
200         case 5: // -Z
201                 {
202                         todir[0] = -invmat[2][0];
203                         todir[1] = -invmat[2][1];
204                         todir[2] = -invmat[2][2];
205                         break;
206                 }
207         }
208         todir.normalize();
209         m_rayDirection = todir;
210
211         MT_Point3 topoint = frompoint + (m_distance) * todir;
212         MT_Point3 resultpoint;
213         MT_Vector3 resultnormal;
214         PHY_IPhysicsEnvironment* physics_environment = m_scene->GetPhysicsEnvironment();
215         if (!physics_environment)
216         {
217                 std::cout << "WARNING: Ray sensor " << GetName() << ":  There is no physics environment!" << std::endl;
218                 std::cout << "         Check universe for malfunction." << std::endl;
219                 return false;
220         } 
221         KX_IPhysicsController* physics_controller = obj->GetPhysicsController();
222
223         // Use the parent's physics controller if obj has no physics controller.
224         KX_GameObject *parent = obj->GetParent();
225         if (!physics_controller && parent)
226                 physics_controller = parent->GetPhysicsController();
227
228         if (parent)
229                 parent->Release();
230                 
231         KX_RayCast::RayTest(physics_controller, physics_environment, frompoint, topoint, resultpoint, resultnormal, KX_RayCast::Callback<KX_RaySensor>(this));
232
233 //      do {
234 //              PHY__Vector3 respos;
235 //              PHY__Vector3 resnormal;
236 // 
237 //              PHY_IPhysicsController* hitCtrl = spe->rayTest(physCtrl,
238 //                      frompoint.x(),frompoint.y(),frompoint.z(),
239 //                      topoint.x(),topoint.y(),topoint.z(),
240 //                      respos[0],respos[1],respos[2],
241 //                      resnormal[0],resnormal[1],resnormal[2]);
242 //                      
243 //              if (hitCtrl)
244 //              {
245 // 
246 //                      resultpoint = MT_Vector3(respos);
247 //                      resultnormal = MT_Vector3(resnormal);
248 //                      KX_ClientObjectInfo* info = static_cast<KX_ClientObjectInfo*>(hitCtrl->getNewClientInfo());
249 //                      bool bFound = false;
250 //                      
251 //                      if (!info)
252 //                      {
253 //                              std::cout<< "WARNING:  Ray sensor " << GetName() << " cannot sense PHY_IPhysicsController  - no client info.\n" << std::endl;
254 //                              ready = true;
255 //                              break;
256 //                      } 
257 //                      
258 //                      
259 // 
260 //              }
261 //              else
262 //              {
263 //                      ready = true;
264 //              }
265 //      }
266 //      while (!ready);
267 //      
268         
269         
270         /* now pass this result to some controller */
271         if (m_rayHit) 
272         {
273                 if (!m_bTriggered)
274                 {
275                         // notify logicsystem that ray is now hitting
276                         result = true;
277                         m_bTriggered = true;
278                 }
279         }
280         else
281         {
282                 if (m_bTriggered)
283                 {
284                         m_bTriggered = false;
285                         // notify logicsystem that ray is not hitting anymore
286                         result = true;
287                 }
288         }
289
290         return result;
291 }
292
293
294
295 /* ------------------------------------------------------------------------- */
296 /* Python functions                                                          */
297 /* ------------------------------------------------------------------------- */
298
299 /* Integration hooks ------------------------------------------------------- */
300 PyTypeObject KX_RaySensor::Type = {
301         PyObject_HEAD_INIT(&PyType_Type)
302         0,
303         "KX_RaySensor",
304         sizeof(KX_RaySensor),
305         0,
306         PyDestructor,
307         0,
308         __getattr,
309         __setattr,
310         0, //&MyPyCompare,
311         __repr,
312         0, //&cvalue_as_number,
313         0,
314         0,
315         0,
316         0
317 };
318
319 PyParentObject KX_RaySensor::Parents[] = {
320         &KX_RaySensor::Type,
321         &SCA_ISensor::Type,
322         &SCA_ILogicBrick::Type,
323         &CValue::Type,
324         NULL
325 };
326
327 PyMethodDef KX_RaySensor::Methods[] = {
328         {"getHitObject",(PyCFunction) KX_RaySensor::sPyGetHitObject,METH_VARARGS, GetHitObject_doc},
329         {"getHitPosition",(PyCFunction) KX_RaySensor::sPyGetHitPosition,METH_VARARGS, GetHitPosition_doc},
330         {"getHitNormal",(PyCFunction) KX_RaySensor::sPyGetHitNormal,METH_VARARGS, GetHitNormal_doc},
331         {"getRayDirection",(PyCFunction) KX_RaySensor::sPyGetRayDirection,METH_VARARGS, GetRayDirection_doc},
332         {NULL,NULL} //Sentinel
333 };
334
335 char KX_RaySensor::GetHitObject_doc[] = 
336 "getHitObject()\n"
337 "\tReturns the name of the object that was hit by this ray.\n";
338 PyObject* KX_RaySensor::PyGetHitObject(PyObject* self, 
339                                                                                    PyObject* args, 
340                                                                                    PyObject* kwds)
341 {
342         if (m_hitObject)
343         {
344                 return m_hitObject->AddRef();
345         }
346         Py_Return;
347 }
348
349
350 char KX_RaySensor::GetHitPosition_doc[] = 
351 "getHitPosition()\n"
352 "\tReturns the position (in worldcoordinates) where the object was hit by this ray.\n";
353 PyObject* KX_RaySensor::PyGetHitPosition(PyObject* self, 
354                                PyObject* args, 
355                                PyObject* kwds)
356 {
357
358         MT_Point3 pos = m_hitPosition;
359
360         PyObject* resultlist = PyList_New(3);
361         int index;
362         for (index=0;index<3;index++)
363         {
364                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
365         }
366         return resultlist;
367
368 }
369
370 char KX_RaySensor::GetRayDirection_doc[] = 
371 "getRayDirection()\n"
372 "\tReturns the direction from the ray (in worldcoordinates) .\n";
373 PyObject* KX_RaySensor::PyGetRayDirection(PyObject* self, 
374                                PyObject* args, 
375                                PyObject* kwds)
376 {
377
378         MT_Vector3 dir = m_rayDirection;
379
380         PyObject* resultlist = PyList_New(3);
381         int index;
382         for (index=0;index<3;index++)
383         {
384                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(dir[index]));
385         }
386         return resultlist;
387
388 }
389
390 char KX_RaySensor::GetHitNormal_doc[] = 
391 "getHitNormal()\n"
392 "\tReturns the normal (in worldcoordinates) of the object at the location where the object was hit by this ray.\n";
393 PyObject* KX_RaySensor::PyGetHitNormal(PyObject* self, 
394                                PyObject* args, 
395                                PyObject* kwds)
396 {
397         MT_Vector3 pos = m_hitNormal;
398
399         PyObject* resultlist = PyList_New(3);
400         int index;
401         for (index=0;index<3;index++)
402         {
403                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
404         }
405         return resultlist;
406
407 }
408
409
410
411 PyObject* KX_RaySensor::_getattr(const STR_String& attr) {
412         _getattr_up(SCA_ISensor);
413 }