Merge from trunk 16122-16307
[blender.git] / source / gameengine / Ketsji / KX_RaySensor.cpp
1 /**
2  * Cast a ray and feel for objects
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.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include "KX_RaySensor.h"
33 #include "SCA_EventManager.h"
34 #include "SCA_RandomEventManager.h"
35 #include "SCA_LogicManager.h"
36 #include "SCA_IObject.h"
37 #include "KX_ClientObjectInfo.h"
38 #include "KX_GameObject.h"
39 #include "KX_Scene.h"
40 #include "KX_RayCast.h"
41 #include "PHY_IPhysicsEnvironment.h"
42 #include "KX_IPhysicsController.h"
43 #include "PHY_IPhysicsController.h"
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48
49
50 KX_RaySensor::KX_RaySensor(class SCA_EventManager* eventmgr,
51                                         SCA_IObject* gameobj,
52                                         const STR_String& propname,
53                                         bool bFindMaterial,
54                                         bool bXRay,
55                                         double distance,
56                                         int axis,
57                                         KX_Scene* ketsjiScene,
58                                         PyTypeObject* T)
59                         : SCA_ISensor(gameobj,eventmgr, T),
60                                         m_propertyname(propname),
61                                         m_bFindMaterial(bFindMaterial),
62                                         m_bXRay(bXRay),
63                                         m_distance(distance),
64                                         m_scene(ketsjiScene),
65                                         m_axis(axis)
66
67                                 
68 {
69         Init();
70 }
71
72 void KX_RaySensor::Init()
73 {
74         m_bTriggered = (m_invert)?true:false;
75         m_rayHit = false;
76         m_hitObject = NULL;
77         m_reset = true;
78 }
79
80 KX_RaySensor::~KX_RaySensor() 
81 {
82     /* Nothing to be done here. */
83 }
84
85
86
87 CValue* KX_RaySensor::GetReplica()
88 {
89         KX_RaySensor* replica = new KX_RaySensor(*this);
90         // this will copy properties and so on...
91         CValue::AddDataToReplica(replica);
92         replica->Init();
93
94         return replica;
95 }
96
97
98
99 bool KX_RaySensor::IsPositiveTrigger()
100 {
101         bool result = m_rayHit;
102
103         if (m_invert)
104                 result = !result;
105         
106         return result;
107 }
108
109 bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
110 {
111
112         KX_GameObject* hitKXObj = client->m_gameobject;
113         bool bFound = false;
114
115         if (m_propertyname.Length() == 0)
116         {
117                 bFound = true;
118         }
119         else
120         {
121                 if (m_bFindMaterial)
122                 {
123                         if (client->m_auxilary_info)
124                         {
125                                 bFound = (m_propertyname== ((char*)client->m_auxilary_info));
126                         }
127                 }
128                 else
129                 {
130                         bFound = hitKXObj->GetProperty(m_propertyname) != NULL;
131                 }
132         }
133
134         if (bFound)
135         {
136                 m_rayHit = true;
137                 m_hitObject = hitKXObj;
138                 m_hitPosition = result->m_hitPoint;
139                 m_hitNormal = result->m_hitNormal;
140                         
141         }
142         // no multi-hit search yet
143         return true;
144 }
145
146 /* this function is used to pre-filter the object before casting the ray on them.
147    This is useful for "X-Ray" option when we want to see "through" unwanted object.
148  */
149 bool KX_RaySensor::NeedRayCast(KX_ClientObjectInfo* client)
150 {
151         if (client->m_type > KX_ClientObjectInfo::ACTOR)
152         {
153                 // Unknown type of object, skip it.
154                 // Should not occur as the sensor objects are filtered in RayTest()
155                 printf("Invalid client type %d found ray casting\n", client->m_type);
156                 return false;
157         }
158         if (m_bXRay && m_propertyname.Length() != 0)
159         {
160                 if (m_bFindMaterial)
161                 {
162                         // not quite correct: an object may have multiple material
163                         // should check all the material and not only the first one
164                         if (!client->m_auxilary_info || (m_propertyname != ((char*)client->m_auxilary_info)))
165                                 return false;
166                 }
167                 else
168                 {
169                         if (client->m_gameobject->GetProperty(m_propertyname) == NULL)
170                                 return false;
171                 }
172         }
173         return true;
174 }
175
176 bool KX_RaySensor::Evaluate(CValue* event)
177 {
178         bool result = false;
179         bool reset = m_reset && m_level;
180         m_rayHit = false; 
181         m_hitObject = NULL;
182         m_hitPosition = MT_Vector3(0,0,0);
183         m_hitNormal = MT_Vector3(1,0,0);
184         
185         KX_GameObject* obj = (KX_GameObject*)GetParent();
186         MT_Point3 frompoint = obj->NodeGetWorldPosition();
187         MT_Matrix3x3 matje = obj->NodeGetWorldOrientation();
188         MT_Matrix3x3 invmat = matje.inverse();
189         
190         MT_Vector3 todir;
191         m_reset = false;
192         switch (m_axis)
193         {
194         case 1: // X
195                 {
196                         todir[0] = invmat[0][0];
197                         todir[1] = invmat[0][1];
198                         todir[2] = invmat[0][2];
199                         break;
200                 }
201         case 0: // Y
202                 {
203                         todir[0] = invmat[1][0];
204                         todir[1] = invmat[1][1];
205                         todir[2] = invmat[1][2];
206                         break;
207                 }
208         case 2: // Z
209                 {
210                         todir[0] = invmat[2][0];
211                         todir[1] = invmat[2][1];
212                         todir[2] = invmat[2][2];
213                         break;
214                 }
215         case 3: // -X
216                 {
217                         todir[0] = -invmat[0][0];
218                         todir[1] = -invmat[0][1];
219                         todir[2] = -invmat[0][2];
220                         break;
221                 }
222         case 4: // -Y
223                 {
224                         todir[0] = -invmat[1][0];
225                         todir[1] = -invmat[1][1];
226                         todir[2] = -invmat[1][2];
227                         break;
228                 }
229         case 5: // -Z
230                 {
231                         todir[0] = -invmat[2][0];
232                         todir[1] = -invmat[2][1];
233                         todir[2] = -invmat[2][2];
234                         break;
235                 }
236         }
237         todir.normalize();
238         m_rayDirection = todir;
239
240         MT_Point3 topoint = frompoint + (m_distance) * todir;
241         PHY_IPhysicsEnvironment* pe = m_scene->GetPhysicsEnvironment();
242
243         if (!pe)
244         {
245                 std::cout << "WARNING: Ray sensor " << GetName() << ":  There is no physics environment!" << std::endl;
246                 std::cout << "         Check universe for malfunction." << std::endl;
247                 return false;
248         } 
249
250         KX_IPhysicsController *spc = obj->GetPhysicsController();
251         KX_GameObject *parent = obj->GetParent();
252         if (!spc && parent)
253                 spc = parent->GetPhysicsController();
254         
255         if (parent)
256                 parent->Release();
257         
258
259         PHY_IPhysicsEnvironment* physics_environment = this->m_scene->GetPhysicsEnvironment();
260         
261
262         KX_RayCast::Callback<KX_RaySensor> callback(this, spc);
263         KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback);
264
265         /* now pass this result to some controller */
266
267     if (m_rayHit)
268         {
269                 if (!m_bTriggered)
270                 {
271                         // notify logicsystem that ray is now hitting
272                         result = true;
273                         m_bTriggered = true;
274                 }
275                 else
276                   {
277                         // notify logicsystem that ray is STILL hitting ...
278                         result = false;
279                     
280                   }
281         }
282     else
283       {
284                 if (m_bTriggered)
285                 {
286                         m_bTriggered = false;
287                         // notify logicsystem that ray JUST left the Object
288                         result = true;
289                 }
290                 else
291                 {
292                         result = false;
293                 }
294         
295       }
296     if (reset)
297                 // force an event
298                 result = true;
299
300         return result;
301 }
302
303
304
305 /* ------------------------------------------------------------------------- */
306 /* Python functions                                                          */
307 /* ------------------------------------------------------------------------- */
308
309 /* Integration hooks ------------------------------------------------------- */
310 PyTypeObject KX_RaySensor::Type = {
311         PyObject_HEAD_INIT(&PyType_Type)
312         0,
313         "KX_RaySensor",
314         sizeof(KX_RaySensor),
315         0,
316         PyDestructor,
317         0,
318         __getattr,
319         __setattr,
320         0, //&MyPyCompare,
321         __repr,
322         0, //&cvalue_as_number,
323         0,
324         0,
325         0,
326         0
327 };
328
329 PyParentObject KX_RaySensor::Parents[] = {
330         &KX_RaySensor::Type,
331         &SCA_ISensor::Type,
332         &SCA_ILogicBrick::Type,
333         &CValue::Type,
334         NULL
335 };
336
337 PyMethodDef KX_RaySensor::Methods[] = {
338         {"getHitObject",(PyCFunction) KX_RaySensor::sPyGetHitObject,METH_VARARGS, GetHitObject_doc},
339         {"getHitPosition",(PyCFunction) KX_RaySensor::sPyGetHitPosition,METH_VARARGS, GetHitPosition_doc},
340         {"getHitNormal",(PyCFunction) KX_RaySensor::sPyGetHitNormal,METH_VARARGS, GetHitNormal_doc},
341         {"getRayDirection",(PyCFunction) KX_RaySensor::sPyGetRayDirection,METH_VARARGS, GetRayDirection_doc},
342         {NULL,NULL} //Sentinel
343 };
344
345 char KX_RaySensor::GetHitObject_doc[] = 
346 "getHitObject()\n"
347 "\tReturns the name of the object that was hit by this ray.\n";
348 PyObject* KX_RaySensor::PyGetHitObject(PyObject* self, 
349                                                                                    PyObject* args, 
350                                                                                    PyObject* kwds)
351 {
352         if (m_hitObject)
353         {
354                 return m_hitObject->AddRef();
355         }
356         Py_Return;
357 }
358
359
360 char KX_RaySensor::GetHitPosition_doc[] = 
361 "getHitPosition()\n"
362 "\tReturns the position (in worldcoordinates) where the object was hit by this ray.\n";
363 PyObject* KX_RaySensor::PyGetHitPosition(PyObject* self, 
364                                PyObject* args, 
365                                PyObject* kwds)
366 {
367
368         MT_Point3 pos = m_hitPosition;
369
370         PyObject* resultlist = PyList_New(3);
371         int index;
372         for (index=0;index<3;index++)
373         {
374                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
375         }
376         return resultlist;
377
378 }
379
380 char KX_RaySensor::GetRayDirection_doc[] = 
381 "getRayDirection()\n"
382 "\tReturns the direction from the ray (in worldcoordinates) .\n";
383 PyObject* KX_RaySensor::PyGetRayDirection(PyObject* self, 
384                                PyObject* args, 
385                                PyObject* kwds)
386 {
387
388         MT_Vector3 dir = m_rayDirection;
389
390         PyObject* resultlist = PyList_New(3);
391         int index;
392         for (index=0;index<3;index++)
393         {
394                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(dir[index]));
395         }
396         return resultlist;
397
398 }
399
400 char KX_RaySensor::GetHitNormal_doc[] = 
401 "getHitNormal()\n"
402 "\tReturns the normal (in worldcoordinates) of the object at the location where the object was hit by this ray.\n";
403 PyObject* KX_RaySensor::PyGetHitNormal(PyObject* self, 
404                                PyObject* args, 
405                                PyObject* kwds)
406 {
407         MT_Vector3 pos = m_hitNormal;
408
409         PyObject* resultlist = PyList_New(3);
410         int index;
411         for (index=0;index<3;index++)
412         {
413                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
414         }
415         return resultlist;
416
417 }
418
419
420
421 PyObject* KX_RaySensor::_getattr(const STR_String& attr) {
422         _getattr_up(SCA_ISensor);
423 }