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