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