svn merge -r 15392:15551 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[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                                         double distance,
55                                         int axis,
56                                         KX_Scene* ketsjiScene,
57                                         PyTypeObject* T)
58                         : SCA_ISensor(gameobj,eventmgr, T),
59                                         m_propertyname(propname),
60                                         m_bFindMaterial(bFindMaterial),
61                                         m_distance(distance),
62                                         m_scene(ketsjiScene),
63                                         m_axis(axis)
64
65                                 
66 {
67         Init();
68 }
69
70 void KX_RaySensor::Init()
71 {
72         m_bTriggered = (m_invert)?true:false;
73         m_rayHit = false;
74         m_hitObject = NULL;
75         m_reset = true;
76 }
77
78 KX_RaySensor::~KX_RaySensor() 
79 {
80     /* Nothing to be done here. */
81 }
82
83
84
85 CValue* KX_RaySensor::GetReplica()
86 {
87         KX_RaySensor* replica = new KX_RaySensor(*this);
88         // this will copy properties and so on...
89         CValue::AddDataToReplica(replica);
90         replica->Init();
91
92         return replica;
93 }
94
95
96
97 bool KX_RaySensor::IsPositiveTrigger()
98 {
99         bool result = m_rayHit;
100
101         if (m_invert)
102                 result = !result;
103         
104         return result;
105 }
106
107 bool KX_RaySensor::RayHit(KX_ClientObjectInfo* client, MT_Point3& hit_point, MT_Vector3& hit_normal, void * const data)
108 {
109
110         KX_GameObject* hitKXObj = client->m_gameobject;
111         
112         if (client->m_type > KX_ClientObjectInfo::ACTOR)
113         {
114                 // false hit
115                 return false;
116         }
117         bool bFound = false;
118
119         if (m_propertyname.Length() == 0)
120         {
121                 bFound = true;
122         }
123         else
124         {
125                 if (m_bFindMaterial)
126                 {
127                         if (client->m_auxilary_info)
128                         {
129                                 bFound = (m_propertyname== ((char*)client->m_auxilary_info));
130                         }
131                 }
132                 else
133                 {
134                         bFound = hitKXObj->GetProperty(m_propertyname) != NULL;
135                 }
136         }
137
138         if (bFound)
139         {
140                 m_rayHit = true;
141                 m_hitObject = hitKXObj;
142                 m_hitPosition = hit_point;
143                 m_hitNormal = hit_normal;
144                         
145         }
146
147         return bFound;
148         
149 }
150
151
152
153 bool KX_RaySensor::Evaluate(CValue* event)
154 {
155         bool result = false;
156         bool reset = m_reset && m_level;
157         m_rayHit = false; 
158         m_hitObject = NULL;
159         m_hitPosition = MT_Vector3(0,0,0);
160         m_hitNormal = MT_Vector3(1,0,0);
161         
162         KX_GameObject* obj = (KX_GameObject*)GetParent();
163         MT_Point3 frompoint = obj->NodeGetWorldPosition();
164         MT_Matrix3x3 matje = obj->NodeGetWorldOrientation();
165         MT_Matrix3x3 invmat = matje.inverse();
166         
167         MT_Vector3 todir;
168         m_reset = false;
169         switch (m_axis)
170         {
171         case 1: // X
172                 {
173                         todir[0] = invmat[0][0];
174                         todir[1] = invmat[0][1];
175                         todir[2] = invmat[0][2];
176                         break;
177                 }
178         case 0: // Y
179                 {
180                         todir[0] = invmat[1][0];
181                         todir[1] = invmat[1][1];
182                         todir[2] = invmat[1][2];
183                         break;
184                 }
185         case 2: // Z
186                 {
187                         todir[0] = invmat[2][0];
188                         todir[1] = invmat[2][1];
189                         todir[2] = invmat[2][2];
190                         break;
191                 }
192         case 3: // -X
193                 {
194                         todir[0] = -invmat[0][0];
195                         todir[1] = -invmat[0][1];
196                         todir[2] = -invmat[0][2];
197                         break;
198                 }
199         case 4: // -Y
200                 {
201                         todir[0] = -invmat[1][0];
202                         todir[1] = -invmat[1][1];
203                         todir[2] = -invmat[1][2];
204                         break;
205                 }
206         case 5: // -Z
207                 {
208                         todir[0] = -invmat[2][0];
209                         todir[1] = -invmat[2][1];
210                         todir[2] = -invmat[2][2];
211                         break;
212                 }
213         }
214         todir.normalize();
215         m_rayDirection = todir;
216
217         MT_Point3 topoint = frompoint + (m_distance) * todir;
218         MT_Point3 resultpoint;
219         MT_Vector3 resultnormal;
220         PHY_IPhysicsEnvironment* pe = m_scene->GetPhysicsEnvironment();
221
222         if (!pe)
223         {
224                 std::cout << "WARNING: Ray sensor " << GetName() << ":  There is no physics environment!" << std::endl;
225                 std::cout << "         Check universe for malfunction." << std::endl;
226                 return false;
227         } 
228
229         KX_IPhysicsController *spc = obj->GetPhysicsController();
230         KX_GameObject *parent = obj->GetParent();
231         if (!spc && parent)
232                 spc = parent->GetPhysicsController();
233         
234         if (parent)
235                 parent->Release();
236         
237
238         PHY_IPhysicsEnvironment* physics_environment = this->m_scene->GetPhysicsEnvironment();
239         
240
241         result = KX_RayCast::RayTest(spc, physics_environment, frompoint, topoint, resultpoint, resultnormal, KX_RayCast::Callback<KX_RaySensor>(this));
242
243         /* now pass this result to some controller */
244
245     if (m_rayHit)
246         {
247                 if (!m_bTriggered)
248                 {
249                         // notify logicsystem that ray is now hitting
250                         result = true;
251                         m_bTriggered = true;
252                 }
253                 else
254                   {
255                         // notify logicsystem that ray is STILL hitting ...
256                         result = false;
257                     
258                   }
259         }
260     else
261       {
262                 if (m_bTriggered)
263                 {
264                         m_bTriggered = false;
265                         // notify logicsystem that ray JUST left the Object
266                         result = true;
267                 }
268         
269       }
270     if (reset)
271                 // force an event
272                 result = true;
273
274         return result;
275 }
276
277
278
279 /* ------------------------------------------------------------------------- */
280 /* Python functions                                                          */
281 /* ------------------------------------------------------------------------- */
282
283 /* Integration hooks ------------------------------------------------------- */
284 PyTypeObject KX_RaySensor::Type = {
285         PyObject_HEAD_INIT(&PyType_Type)
286         0,
287         "KX_RaySensor",
288         sizeof(KX_RaySensor),
289         0,
290         PyDestructor,
291         0,
292         __getattr,
293         __setattr,
294         0, //&MyPyCompare,
295         __repr,
296         0, //&cvalue_as_number,
297         0,
298         0,
299         0,
300         0
301 };
302
303 PyParentObject KX_RaySensor::Parents[] = {
304         &KX_RaySensor::Type,
305         &SCA_ISensor::Type,
306         &SCA_ILogicBrick::Type,
307         &CValue::Type,
308         NULL
309 };
310
311 PyMethodDef KX_RaySensor::Methods[] = {
312         {"getHitObject",(PyCFunction) KX_RaySensor::sPyGetHitObject,METH_VARARGS, GetHitObject_doc},
313         {"getHitPosition",(PyCFunction) KX_RaySensor::sPyGetHitPosition,METH_VARARGS, GetHitPosition_doc},
314         {"getHitNormal",(PyCFunction) KX_RaySensor::sPyGetHitNormal,METH_VARARGS, GetHitNormal_doc},
315         {"getRayDirection",(PyCFunction) KX_RaySensor::sPyGetRayDirection,METH_VARARGS, GetRayDirection_doc},
316         {NULL,NULL} //Sentinel
317 };
318
319 char KX_RaySensor::GetHitObject_doc[] = 
320 "getHitObject()\n"
321 "\tReturns the name of the object that was hit by this ray.\n";
322 PyObject* KX_RaySensor::PyGetHitObject(PyObject* self, 
323                                                                                    PyObject* args, 
324                                                                                    PyObject* kwds)
325 {
326         if (m_hitObject)
327         {
328                 return m_hitObject->AddRef();
329         }
330         Py_Return;
331 }
332
333
334 char KX_RaySensor::GetHitPosition_doc[] = 
335 "getHitPosition()\n"
336 "\tReturns the position (in worldcoordinates) where the object was hit by this ray.\n";
337 PyObject* KX_RaySensor::PyGetHitPosition(PyObject* self, 
338                                PyObject* args, 
339                                PyObject* kwds)
340 {
341
342         MT_Point3 pos = m_hitPosition;
343
344         PyObject* resultlist = PyList_New(3);
345         int index;
346         for (index=0;index<3;index++)
347         {
348                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
349         }
350         return resultlist;
351
352 }
353
354 char KX_RaySensor::GetRayDirection_doc[] = 
355 "getRayDirection()\n"
356 "\tReturns the direction from the ray (in worldcoordinates) .\n";
357 PyObject* KX_RaySensor::PyGetRayDirection(PyObject* self, 
358                                PyObject* args, 
359                                PyObject* kwds)
360 {
361
362         MT_Vector3 dir = m_rayDirection;
363
364         PyObject* resultlist = PyList_New(3);
365         int index;
366         for (index=0;index<3;index++)
367         {
368                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(dir[index]));
369         }
370         return resultlist;
371
372 }
373
374 char KX_RaySensor::GetHitNormal_doc[] = 
375 "getHitNormal()\n"
376 "\tReturns the normal (in worldcoordinates) of the object at the location where the object was hit by this ray.\n";
377 PyObject* KX_RaySensor::PyGetHitNormal(PyObject* self, 
378                                PyObject* args, 
379                                PyObject* kwds)
380 {
381         MT_Vector3 pos = m_hitNormal;
382
383         PyObject* resultlist = PyList_New(3);
384         int index;
385         for (index=0;index<3;index++)
386         {
387                 PyList_SetItem(resultlist,index,PyFloat_FromDouble(pos[index]));
388         }
389         return resultlist;
390
391 }
392
393
394
395 PyObject* KX_RaySensor::_getattr(const STR_String& attr) {
396         _getattr_up(SCA_ISensor);
397 }