8616145d709aff3ee8c6d907ce9d04a5f1865212
[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 "KX_PyMath.h"
42 #include "PHY_IPhysicsEnvironment.h"
43 #include "KX_IPhysicsController.h"
44 #include "PHY_IPhysicsController.h"
45
46 #ifdef HAVE_CONFIG_H
47 #include <config.h>
48 #endif
49
50
51 KX_RaySensor::KX_RaySensor(class SCA_EventManager* eventmgr,
52                                         SCA_IObject* gameobj,
53                                         const STR_String& propname,
54                                         bool bFindMaterial,
55                                         bool bXRay,
56                                         double distance,
57                                         int axis,
58                                         KX_Scene* ketsjiScene)
59                         : SCA_ISensor(gameobj,eventmgr),
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         replica->ProcessReplica();
91         replica->Init();
92
93         return replica;
94 }
95
96
97
98 bool KX_RaySensor::IsPositiveTrigger()
99 {
100         bool result = m_rayHit;
101
102         if (m_invert)
103                 result = !result;
104         
105         return result;
106 }
107
108 bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
109 {
110
111         KX_GameObject* hitKXObj = client->m_gameobject;
112         bool bFound = false;
113
114         if (m_propertyname.Length() == 0)
115         {
116                 bFound = true;
117         }
118         else
119         {
120                 if (m_bFindMaterial)
121                 {
122                         if (client->m_auxilary_info)
123                         {
124                                 bFound = (m_propertyname== ((char*)client->m_auxilary_info));
125                         }
126                 }
127                 else
128                 {
129                         bFound = hitKXObj->GetProperty(m_propertyname) != NULL;
130                 }
131         }
132
133         if (bFound)
134         {
135                 m_rayHit = true;
136                 m_hitObject = hitKXObj;
137                 m_hitPosition[0] = result->m_hitPoint[0];
138                 m_hitPosition[1] = result->m_hitPoint[1];
139                 m_hitPosition[2] = result->m_hitPoint[2];
140
141                 m_hitNormal[0] = result->m_hitNormal[0];
142                 m_hitNormal[1] = result->m_hitNormal[1];
143                 m_hitNormal[2] = result->m_hitNormal[2];
144                         
145         }
146         // no multi-hit search yet
147         return true;
148 }
149
150 /* this function is used to pre-filter the object before casting the ray on them.
151    This is useful for "X-Ray" option when we want to see "through" unwanted object.
152  */
153 bool KX_RaySensor::NeedRayCast(KX_ClientObjectInfo* client)
154 {
155         if (client->m_type > KX_ClientObjectInfo::ACTOR)
156         {
157                 // Unknown type of object, skip it.
158                 // Should not occur as the sensor objects are filtered in RayTest()
159                 printf("Invalid client type %d found ray casting\n", client->m_type);
160                 return false;
161         }
162         if (m_bXRay && m_propertyname.Length() != 0)
163         {
164                 if (m_bFindMaterial)
165                 {
166                         // not quite correct: an object may have multiple material
167                         // should check all the material and not only the first one
168                         if (!client->m_auxilary_info || (m_propertyname != ((char*)client->m_auxilary_info)))
169                                 return false;
170                 }
171                 else
172                 {
173                         if (client->m_gameobject->GetProperty(m_propertyname) == NULL)
174                                 return false;
175                 }
176         }
177         return true;
178 }
179
180 bool KX_RaySensor::Evaluate()
181 {
182         bool result = false;
183         bool reset = m_reset && m_level;
184         m_rayHit = false; 
185         m_hitObject = NULL;
186         m_hitPosition[0] = 0;
187         m_hitPosition[1] = 0;
188         m_hitPosition[2] = 0;
189
190         m_hitNormal[0] = 1;
191         m_hitNormal[1] = 0;
192         m_hitNormal[2] = 0;
193         
194         KX_GameObject* obj = (KX_GameObject*)GetParent();
195         MT_Point3 frompoint = obj->NodeGetWorldPosition();
196         MT_Matrix3x3 matje = obj->NodeGetWorldOrientation();
197         MT_Matrix3x3 invmat = matje.inverse();
198         
199         MT_Vector3 todir;
200         m_reset = false;
201         switch (m_axis)
202         {
203         case 1: // X
204                 {
205                         todir[0] = invmat[0][0];
206                         todir[1] = invmat[0][1];
207                         todir[2] = invmat[0][2];
208                         break;
209                 }
210         case 0: // Y
211                 {
212                         todir[0] = invmat[1][0];
213                         todir[1] = invmat[1][1];
214                         todir[2] = invmat[1][2];
215                         break;
216                 }
217         case 2: // Z
218                 {
219                         todir[0] = invmat[2][0];
220                         todir[1] = invmat[2][1];
221                         todir[2] = invmat[2][2];
222                         break;
223                 }
224         case 3: // -X
225                 {
226                         todir[0] = -invmat[0][0];
227                         todir[1] = -invmat[0][1];
228                         todir[2] = -invmat[0][2];
229                         break;
230                 }
231         case 4: // -Y
232                 {
233                         todir[0] = -invmat[1][0];
234                         todir[1] = -invmat[1][1];
235                         todir[2] = -invmat[1][2];
236                         break;
237                 }
238         case 5: // -Z
239                 {
240                         todir[0] = -invmat[2][0];
241                         todir[1] = -invmat[2][1];
242                         todir[2] = -invmat[2][2];
243                         break;
244                 }
245         }
246         todir.normalize();
247         m_rayDirection[0] = todir[0];
248         m_rayDirection[1] = todir[1];
249         m_rayDirection[2] = todir[2];
250
251         MT_Point3 topoint = frompoint + (m_distance) * todir;
252         PHY_IPhysicsEnvironment* pe = m_scene->GetPhysicsEnvironment();
253
254         if (!pe)
255         {
256                 std::cout << "WARNING: Ray sensor " << GetName() << ":  There is no physics environment!" << std::endl;
257                 std::cout << "         Check universe for malfunction." << std::endl;
258                 return false;
259         } 
260
261         KX_IPhysicsController *spc = obj->GetPhysicsController();
262         KX_GameObject *parent = obj->GetParent();
263         if (!spc && parent)
264                 spc = parent->GetPhysicsController();
265         
266         if (parent)
267                 parent->Release();
268         
269
270         PHY_IPhysicsEnvironment* physics_environment = this->m_scene->GetPhysicsEnvironment();
271         
272
273         KX_RayCast::Callback<KX_RaySensor> callback(this, spc);
274         KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback);
275
276         /* now pass this result to some controller */
277
278     if (m_rayHit)
279         {
280                 if (!m_bTriggered)
281                 {
282                         // notify logicsystem that ray is now hitting
283                         result = true;
284                         m_bTriggered = true;
285                 }
286                 else
287                   {
288                         // notify logicsystem that ray is STILL hitting ...
289                         result = false;
290                     
291                   }
292         }
293     else
294       {
295                 if (m_bTriggered)
296                 {
297                         m_bTriggered = false;
298                         // notify logicsystem that ray JUST left the Object
299                         result = true;
300                 }
301                 else
302                 {
303                         result = false;
304                 }
305         
306       }
307     if (reset)
308                 // force an event
309                 result = true;
310
311         return result;
312 }
313
314
315
316 /* ------------------------------------------------------------------------- */
317 /* Python functions                                                          */
318 /* ------------------------------------------------------------------------- */
319
320 /* Integration hooks ------------------------------------------------------- */
321 PyTypeObject KX_RaySensor::Type = {
322         PyVarObject_HEAD_INIT(NULL, 0)
323         "KX_RaySensor",
324         sizeof(PyObjectPlus_Proxy),
325         0,
326         py_base_dealloc,
327         0,
328         0,
329         0,
330         0,
331         py_base_repr,
332         0,0,0,0,0,0,0,0,0,
333         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
334         0,0,0,0,0,0,0,
335         Methods,
336         0,
337         0,
338         &SCA_ISensor::Type,
339         0,0,0,0,0,0,
340         py_base_new
341
342 };
343
344 PyMethodDef KX_RaySensor::Methods[] = {
345         // Deprecated ----->
346         {"getHitObject",(PyCFunction) KX_RaySensor::sPyGetHitObject,METH_NOARGS, (const char *)GetHitObject_doc},
347         {"getHitPosition",(PyCFunction) KX_RaySensor::sPyGetHitPosition,METH_NOARGS, (const char *)GetHitPosition_doc},
348         {"getHitNormal",(PyCFunction) KX_RaySensor::sPyGetHitNormal,METH_NOARGS, (const char *)GetHitNormal_doc},
349         {"getRayDirection",(PyCFunction) KX_RaySensor::sPyGetRayDirection,METH_NOARGS, (const char *)GetRayDirection_doc},
350         // <-----
351         {NULL,NULL} //Sentinel
352 };
353
354 PyAttributeDef KX_RaySensor::Attributes[] = {
355         KX_PYATTRIBUTE_BOOL_RW("useMaterial", KX_RaySensor, m_bFindMaterial),
356         KX_PYATTRIBUTE_BOOL_RW("useXRay", KX_RaySensor, m_bXRay),
357         KX_PYATTRIBUTE_FLOAT_RW("range", 0, 10000, KX_RaySensor, m_distance),
358         KX_PYATTRIBUTE_STRING_RW("propName", 0, 100, false, KX_RaySensor, m_propertyname),
359         KX_PYATTRIBUTE_INT_RW("axis", 0, 5, true, KX_RaySensor, m_axis),
360         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("hitPosition", KX_RaySensor, m_hitPosition, 3),
361         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("rayDirection", KX_RaySensor, m_rayDirection, 3),
362         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("hitNormal", KX_RaySensor, m_hitNormal, 3),
363         KX_PYATTRIBUTE_RO_FUNCTION("hitObject", KX_RaySensor, pyattr_get_hitobject),
364         { NULL }        //Sentinel
365 };
366
367 PyObject* KX_RaySensor::pyattr_get_hitobject(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
368 {
369         KX_RaySensor* self = static_cast<KX_RaySensor*>(self_v);
370         if (self->m_hitObject)
371                 return self->m_hitObject->GetProxy();
372
373         Py_RETURN_NONE;
374 }
375
376 // Deprecated ----->
377 const char KX_RaySensor::GetHitObject_doc[] = 
378 "getHitObject()\n"
379 "\tReturns the name of the object that was hit by this ray.\n";
380 PyObject* KX_RaySensor::PyGetHitObject()
381 {
382         ShowDeprecationWarning("getHitObject()", "the hitObject property");
383         if (m_hitObject)
384         {
385                 return m_hitObject->GetProxy();
386         }
387         Py_RETURN_NONE;
388 }
389
390
391 const char KX_RaySensor::GetHitPosition_doc[] = 
392 "getHitPosition()\n"
393 "\tReturns the position (in worldcoordinates) where the object was hit by this ray.\n";
394 PyObject* KX_RaySensor::PyGetHitPosition()
395 {
396         ShowDeprecationWarning("getHitPosition()", "the hitPosition property");
397
398         PyObject *retVal = PyList_New(3);
399
400         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_hitPosition[0]));
401         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_hitPosition[1]));
402         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_hitPosition[2]));
403
404         return retVal;
405 }
406
407 const char KX_RaySensor::GetRayDirection_doc[] = 
408 "getRayDirection()\n"
409 "\tReturns the direction from the ray (in worldcoordinates) .\n";
410 PyObject* KX_RaySensor::PyGetRayDirection()
411 {
412         ShowDeprecationWarning("getRayDirection()", "the rayDirection property");
413
414         PyObject *retVal = PyList_New(3);
415         
416         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_rayDirection[0]));
417         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_rayDirection[1]));
418         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_rayDirection[2]));
419
420         return retVal;
421 }
422
423 const char KX_RaySensor::GetHitNormal_doc[] = 
424 "getHitNormal()\n"
425 "\tReturns the normal (in worldcoordinates) of the object at the location where the object was hit by this ray.\n";
426 PyObject* KX_RaySensor::PyGetHitNormal()
427 {
428         ShowDeprecationWarning("getHitNormal()", "the hitNormal property");
429
430         PyObject *retVal = PyList_New(3);
431
432         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_hitNormal[0]));
433         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_hitNormal[1]));
434         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_hitNormal[2]));
435
436         return retVal;
437 }
438
439 // <----- Deprecated