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