3f27496df71217e7b1153c5e7a9fc98b19e7480c
[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 #if (PY_VERSION_HEX >= 0x02060000)
323         PyVarObject_HEAD_INIT(NULL, 0)
324 #else
325         /* python 2.5 and below */
326         PyObject_HEAD_INIT( NULL )  /* required py macro */
327         0,                          /* ob_size */
328 #endif
329         "KX_RaySensor",
330         sizeof(PyObjectPlus_Proxy),
331         0,
332         py_base_dealloc,
333         0,
334         0,
335         0,
336         0,
337         py_base_repr,
338         0,0,0,0,0,0,0,0,0,
339         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
340         0,0,0,0,0,0,0,
341         Methods,
342         0,
343         0,
344         &SCA_ISensor::Type,
345         0,0,0,0,0,0,
346         py_base_new
347
348 };
349
350 PyMethodDef KX_RaySensor::Methods[] = {
351         // Deprecated ----->
352         {"getHitObject",(PyCFunction) KX_RaySensor::sPyGetHitObject,METH_NOARGS, (PY_METHODCHAR)GetHitObject_doc},
353         {"getHitPosition",(PyCFunction) KX_RaySensor::sPyGetHitPosition,METH_NOARGS, (PY_METHODCHAR)GetHitPosition_doc},
354         {"getHitNormal",(PyCFunction) KX_RaySensor::sPyGetHitNormal,METH_NOARGS, (PY_METHODCHAR)GetHitNormal_doc},
355         {"getRayDirection",(PyCFunction) KX_RaySensor::sPyGetRayDirection,METH_NOARGS, (PY_METHODCHAR)GetRayDirection_doc},
356         // <-----
357         {NULL,NULL} //Sentinel
358 };
359
360 PyAttributeDef KX_RaySensor::Attributes[] = {
361         KX_PYATTRIBUTE_BOOL_RW("useMaterial", KX_RaySensor, m_bFindMaterial),
362         KX_PYATTRIBUTE_BOOL_RW("useXRay", KX_RaySensor, m_bXRay),
363         KX_PYATTRIBUTE_FLOAT_RW("range", 0, 10000, KX_RaySensor, m_distance),
364         KX_PYATTRIBUTE_STRING_RW("propName", 0, 100, false, KX_RaySensor, m_propertyname),
365         KX_PYATTRIBUTE_INT_RW("axis", 0, 5, true, KX_RaySensor, m_axis),
366         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("hitPosition", KX_RaySensor, m_hitPosition, 3),
367         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("rayDirection", KX_RaySensor, m_rayDirection, 3),
368         KX_PYATTRIBUTE_FLOAT_ARRAY_RO("hitNormal", KX_RaySensor, m_hitNormal, 3),
369         KX_PYATTRIBUTE_RO_FUNCTION("hitObject", KX_RaySensor, pyattr_get_hitobject),
370         { NULL }        //Sentinel
371 };
372
373 PyObject* KX_RaySensor::pyattr_get_hitobject(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
374 {
375         KX_RaySensor* self = static_cast<KX_RaySensor*>(self_v);
376         if (self->m_hitObject)
377                 return self->m_hitObject->GetProxy();
378
379         Py_RETURN_NONE;
380 }
381
382 // Deprecated ----->
383 const char KX_RaySensor::GetHitObject_doc[] = 
384 "getHitObject()\n"
385 "\tReturns the name of the object that was hit by this ray.\n";
386 PyObject* KX_RaySensor::PyGetHitObject()
387 {
388         ShowDeprecationWarning("getHitObject()", "the hitObject property");
389         if (m_hitObject)
390         {
391                 return m_hitObject->GetProxy();
392         }
393         Py_RETURN_NONE;
394 }
395
396
397 const char KX_RaySensor::GetHitPosition_doc[] = 
398 "getHitPosition()\n"
399 "\tReturns the position (in worldcoordinates) where the object was hit by this ray.\n";
400 PyObject* KX_RaySensor::PyGetHitPosition()
401 {
402         ShowDeprecationWarning("getHitPosition()", "the hitPosition property");
403
404         PyObject *retVal = PyList_New(3);
405
406         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_hitPosition[0]));
407         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_hitPosition[1]));
408         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_hitPosition[2]));
409
410         return retVal;
411 }
412
413 const char KX_RaySensor::GetRayDirection_doc[] = 
414 "getRayDirection()\n"
415 "\tReturns the direction from the ray (in worldcoordinates) .\n";
416 PyObject* KX_RaySensor::PyGetRayDirection()
417 {
418         ShowDeprecationWarning("getRayDirection()", "the rayDirection property");
419
420         PyObject *retVal = PyList_New(3);
421         
422         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_rayDirection[0]));
423         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_rayDirection[1]));
424         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_rayDirection[2]));
425
426         return retVal;
427 }
428
429 const char KX_RaySensor::GetHitNormal_doc[] = 
430 "getHitNormal()\n"
431 "\tReturns the normal (in worldcoordinates) of the object at the location where the object was hit by this ray.\n";
432 PyObject* KX_RaySensor::PyGetHitNormal()
433 {
434         ShowDeprecationWarning("getHitNormal()", "the hitNormal property");
435
436         PyObject *retVal = PyList_New(3);
437
438         PyList_SET_ITEM(retVal, 0, PyFloat_FromDouble(m_hitNormal[0]));
439         PyList_SET_ITEM(retVal, 1, PyFloat_FromDouble(m_hitNormal[1]));
440         PyList_SET_ITEM(retVal, 2, PyFloat_FromDouble(m_hitNormal[2]));
441
442         return retVal;
443 }
444
445 // <----- Deprecated