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