Merge from trunk 16122-16307
[blender.git] / source / gameengine / Ketsji / KX_TouchSensor.cpp
1 /**
2  * Senses touch and collision events
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_TouchSensor.h"
33 #include "SCA_EventManager.h"
34 #include "SCA_LogicManager.h"
35 #include "KX_GameObject.h"
36 #include "KX_TouchEventManager.h"
37 #include "KX_SumoPhysicsController.h"
38 #include <iostream>
39 #include "PHY_IPhysicsEnvironment.h"
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44
45 /* ------------------------------------------------------------------------- */
46 /* Native functions                                                          */
47 /* ------------------------------------------------------------------------- */
48
49 void KX_TouchSensor::SynchronizeTransform()
50 {
51         // the touch sensor does not require any synchronization: it uses
52         // the same physical object which is already synchronized by Blender
53 }
54
55
56 void KX_TouchSensor::EndFrame() {
57         m_colliders->ReleaseAndRemoveAll();
58         m_bTriggered = false;
59 }
60
61 bool KX_TouchSensor::Evaluate(CValue* event)
62 {
63         bool result = false;
64         bool reset = m_reset && m_level;
65
66         m_reset = false;
67         if (m_bTriggered != m_bLastTriggered)
68         {
69                 m_bLastTriggered = m_bTriggered;
70                 if (!m_bTriggered)
71                         m_hitObject = NULL;
72                 result = true;
73         }
74         if (reset)
75                 // force an event
76                 result = true;
77         return result;
78 }
79
80 KX_TouchSensor::KX_TouchSensor(SCA_EventManager* eventmgr,KX_GameObject* gameobj,bool bFindMaterial,const STR_String& touchedpropname,PyTypeObject* T)
81 :SCA_ISensor(gameobj,eventmgr,T),
82 m_touchedpropname(touchedpropname),
83 m_bFindMaterial(bFindMaterial),
84 m_eventmgr(eventmgr)
85 /*m_sumoObj(sumoObj),*/
86 {
87 //      KX_TouchEventManager* touchmgr = (KX_TouchEventManager*) eventmgr;
88 //      m_resptable = touchmgr->GetResponseTable();
89         
90 //      m_solidHandle = m_sumoObj->getObjectHandle();
91
92         m_colliders = new CListValue();
93         
94         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
95         client_info->m_gameobject = gameobj;
96         client_info->m_auxilary_info = NULL;
97         client_info->m_sensors.push_back(this);
98         
99         m_physCtrl = dynamic_cast<PHY_IPhysicsController*>(gameobj->GetPhysicsController());
100         MT_assert( !gameobj->GetPhysicsController() || m_physCtrl );
101         Init();
102 }
103
104 void KX_TouchSensor::Init()
105 {
106         m_bCollision = false;
107         m_bTriggered = false;
108         m_bLastTriggered = (m_invert)?true:false;
109         m_hitObject =  NULL;
110         m_reset = true;
111 }
112
113 KX_TouchSensor::~KX_TouchSensor()
114 {
115         //DT_ClearObjectResponse(m_resptable,m_solidHandle);
116         m_colliders->Release();
117 }
118
119 CValue* KX_TouchSensor::GetReplica() 
120 {
121         KX_TouchSensor* replica = new KX_TouchSensor(*this);
122         replica->m_colliders = new CListValue();
123         replica->Init();
124         // this will copy properties and so on...
125         CValue::AddDataToReplica(replica);
126         return replica;
127 }
128
129 void    KX_TouchSensor::ReParent(SCA_IObject* parent)
130 {
131         KX_GameObject *gameobj = static_cast<KX_GameObject *>(parent);
132         PHY_IPhysicsController *sphy = dynamic_cast<PHY_IPhysicsController*>(((KX_GameObject*)parent)->GetPhysicsController());
133         if (sphy)
134                 m_physCtrl = sphy;
135         
136 //      m_solidHandle = m_sumoObj->getObjectHandle();
137         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
138         client_info->m_gameobject = gameobj;
139         client_info->m_auxilary_info = NULL;
140         
141         client_info->m_sensors.push_back(this);
142         SCA_ISensor::ReParent(parent);
143 }
144
145 void KX_TouchSensor::RegisterSumo(KX_TouchEventManager *touchman)
146 {
147         if (m_physCtrl)
148         {
149                 touchman->GetPhysicsEnvironment()->requestCollisionCallback(m_physCtrl);
150                 // collision
151                 // Deprecated   
152
153         }
154 }
155
156 void KX_TouchSensor::UnregisterSumo(KX_TouchEventManager* touchman)
157 {
158         if (m_physCtrl)
159         {
160                 touchman->GetPhysicsEnvironment()->removeCollisionCallback(m_physCtrl);
161         }
162 }
163
164 bool    KX_TouchSensor::NewHandleCollision(void*object1,void*object2,const PHY_CollData* colldata)
165 {
166 //      KX_TouchEventManager* toucheventmgr = (KX_TouchEventManager*)m_eventmgr;
167         KX_GameObject* parent = (KX_GameObject*)GetParent();
168
169         // need the mapping from PHY_IPhysicsController to gameobjects now
170         
171         KX_ClientObjectInfo* client_info = static_cast<KX_ClientObjectInfo*> (object1 == m_physCtrl? 
172                                         ((PHY_IPhysicsController*)object2)->getNewClientInfo(): 
173                                         ((PHY_IPhysicsController*)object1)->getNewClientInfo());
174
175         KX_GameObject* gameobj = ( client_info ? 
176                         client_info->m_gameobject : 
177                         NULL);
178         
179         if (gameobj && (gameobj != parent) && client_info->isActor())
180         {
181                 if (!m_colliders->SearchValue(gameobj))
182                         m_colliders->Add(gameobj->AddRef());
183                 
184                 bool found = m_touchedpropname.IsEmpty();
185                 if (!found)
186                 {
187                         if (m_bFindMaterial)
188                         {
189                                 if (client_info->m_auxilary_info)
190                                 {
191                                         found = (m_touchedpropname == STR_String((char*)client_info->m_auxilary_info));
192                                 }
193                         } else
194                         {
195                                 found = (gameobj->GetProperty(m_touchedpropname) != NULL);
196                         }
197                 }
198                 if (found)
199                 {
200                         m_bTriggered = true;
201                         m_hitObject = gameobj;
202                         //printf("KX_TouchSensor::HandleCollision\n");
203                 }
204                 
205         } 
206         return DT_CONTINUE;
207 }
208
209
210 /* ------------------------------------------------------------------------- */
211 /* Python functions                                                          */
212 /* ------------------------------------------------------------------------- */
213 /* Integration hooks ------------------------------------------------------- */
214 PyTypeObject KX_TouchSensor::Type = {
215         PyObject_HEAD_INIT(&PyType_Type)
216         0,
217         "KX_TouchSensor",
218         sizeof(KX_TouchSensor),
219         0,
220         PyDestructor,
221         0,
222         __getattr,
223         __setattr,
224         0, //&MyPyCompare,
225         __repr,
226         0, //&cvalue_as_number,
227         0,
228         0,
229         0,
230         0
231 };
232
233 PyParentObject KX_TouchSensor::Parents[] = {
234         &KX_TouchSensor::Type,
235         &SCA_ISensor::Type,
236         &SCA_ILogicBrick::Type,
237         &CValue::Type,
238         NULL
239 };
240
241 PyMethodDef KX_TouchSensor::Methods[] = {
242         {"setProperty", 
243          (PyCFunction) KX_TouchSensor::sPySetProperty,      METH_VARARGS, SetProperty_doc},
244         {"getProperty", 
245          (PyCFunction) KX_TouchSensor::sPyGetProperty,      METH_VARARGS, GetProperty_doc},
246         {"getHitObject", 
247          (PyCFunction) KX_TouchSensor::sPyGetHitObject,     METH_VARARGS, GetHitObject_doc},
248         {"getHitObjectList", 
249          (PyCFunction) KX_TouchSensor::sPyGetHitObjectList, METH_VARARGS, GetHitObjectList_doc},
250         {NULL,NULL} //Sentinel
251 };
252
253 PyObject* KX_TouchSensor::_getattr(const STR_String& attr) {
254         _getattr_up(SCA_ISensor);
255 }
256
257 /* Python API */
258
259 /* 1. setProperty */
260 char KX_TouchSensor::SetProperty_doc[] = 
261 "setProperty(name)\n"
262 "\t- name: string\n"
263 "\tSet the property or material to collide with. Use\n"
264 "\tsetTouchMaterial() to switch between properties and\n"
265 "\tmaterials.";
266 PyObject* KX_TouchSensor::PySetProperty(PyObject* self, 
267                                                                                 PyObject* args, 
268                                                                                 PyObject* kwds) {
269         char *nameArg;
270         if (!PyArg_ParseTuple(args, "s", &nameArg)) {
271                 return NULL;
272         }
273
274         CValue* prop = GetParent()->FindIdentifier(nameArg);
275
276         if (!prop->IsError()) {
277                 m_touchedpropname = nameArg;
278         } else {
279                 ; /* not found ... */
280         }
281         prop->Release();
282         
283         Py_Return;
284 }
285 /* 2. getProperty */
286 char KX_TouchSensor::GetProperty_doc[] = 
287 "getProperty(name)\n"
288 "\tReturns the property or material to collide with. Use\n"
289 "\tgetTouchMaterial() to find out whether this sensor\n"
290 "\tlooks for properties or materials.";
291 PyObject*  KX_TouchSensor::PyGetProperty(PyObject* self, 
292                                                                                  PyObject* args, 
293                                                                                  PyObject* kwds) {
294         return PyString_FromString(m_touchedpropname);
295 }
296
297 char KX_TouchSensor::GetHitObject_doc[] = 
298 "getHitObject()\n"
299 ;
300 PyObject* KX_TouchSensor::PyGetHitObject(PyObject* self, 
301                                                                                  PyObject* args, 
302                                                                                  PyObject* kwds)
303 {
304         /* to do: do Py_IncRef if the object is already known in Python */
305         /* otherwise, this leaks memory */
306         if (m_hitObject)
307         {
308                 return m_hitObject->AddRef();
309         }
310         Py_Return;
311 }
312
313 char KX_TouchSensor::GetHitObjectList_doc[] = 
314 "getHitObjectList()\n"
315 "\tReturn a list of the objects this object collided with,\n"
316 "\tbut only those matching the property/material condition.\n";
317 PyObject* KX_TouchSensor::PyGetHitObjectList(PyObject* self, 
318                                                                                  PyObject* args, 
319                                                                                  PyObject* kwds)
320 {
321
322         /* to do: do Py_IncRef if the object is already known in Python */
323         /* otherwise, this leaks memory */
324
325         if ( m_touchedpropname.IsEmpty() ) {
326                 return m_colliders->AddRef();
327         } else {
328                 CListValue* newList = new CListValue();
329                 int i = 0;
330                 while (i < m_colliders->GetCount()) {
331                         if (m_bFindMaterial) {
332                                 /* need to associate the CValues from the list to material
333                                  * names. The collider list _should_ contains only
334                                  * KX_GameObjects. I am loathe to cast them, though... The
335                                  * material name must be retrieved from Sumo. To a Sumo
336                                  * object, a client-info block is attached. This block
337                                  * contains the material name. 
338                                  * - this also doesn't work (obviously) for multi-materials... 
339                                  */
340                                 KX_GameObject* gameob = (KX_GameObject*) m_colliders->GetValue(i);
341                                 PHY_IPhysicsController* spc = dynamic_cast<PHY_IPhysicsController*>(gameob->GetPhysicsController());
342                                 
343                                 if (spc) {
344                                         KX_ClientObjectInfo* cl_inf = static_cast<KX_ClientObjectInfo*>(spc->getNewClientInfo());
345                                         
346                                         if (m_touchedpropname == ((char*)cl_inf->m_auxilary_info)) {
347                                                 newList->Add(m_colliders->GetValue(i)->AddRef());
348                                         } 
349                                 }
350                                 
351                         } else {
352                                 CValue* val = m_colliders->GetValue(i)->FindIdentifier(m_touchedpropname);
353                                 if (!val->IsError()) {
354                                         newList->Add(m_colliders->GetValue(i)->AddRef());
355                                 }
356                                 val->Release();
357                         }
358                         
359                         i++;
360                 }
361                 return newList->AddRef();
362         }
363
364 }
365
366 /* 5. getTouchMaterial */
367 char KX_TouchSensor::GetTouchMaterial_doc[] = 
368 "getTouchMaterial()\n"
369 "\tReturns KX_TRUE if this sensor looks for a specific material,\n"
370 "\tKX_FALSE if it looks for a specific property.\n" ;
371 PyObject* KX_TouchSensor::PyGetTouchMaterial(PyObject* self, 
372                                                                                          PyObject* args, 
373                                                                                          PyObject* kwds)
374 {
375         return PyInt_FromLong(m_bFindMaterial);
376 }
377
378 /* 6. setTouchMaterial */
379 char KX_TouchSensor::SetTouchMaterial_doc[] = 
380 "setTouchMaterial(flag)\n"
381 "\t- flag: KX_TRUE or KX_FALSE.\n"
382 "\tSet flag to KX_TRUE to switch on positive pulse mode,\n"
383 "\tKX_FALSE to switch off positive pulse mode.\n" ;
384 PyObject* KX_TouchSensor::PySetTouchMaterial(PyObject* self, PyObject* args, PyObject* kwds)
385 {
386         int pulseArg = 0;
387
388         if(!PyArg_ParseTuple(args, "i", &pulseArg)) {
389                 return NULL;
390         }
391         
392         m_bFindMaterial = pulseArg != 0;
393
394         Py_Return;
395 }
396
397
398 /* eof */