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