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