1. Check material names passed to the physics engine (for collision sensors.)
[blender.git] / source / gameengine / Ketsji / KX_TouchSensor.cpp
1 /**
2  * Senses touch and collision events
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL/BL DUAL 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. The Blender
12  * Foundation also sells licenses for use in proprietary software under
13  * the Blender License.  See http://www.blender.org/BL/ for information
14  * about this.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include "KX_TouchSensor.h"
36 #include "SCA_EventManager.h"
37 #include "SCA_LogicManager.h"
38 #include "KX_GameObject.h"
39 #include "KX_TouchEventManager.h"
40 #include "SM_Object.h"
41 #include "KX_SumoPhysicsController.h"
42 #include <iostream>
43
44 #ifdef HAVE_CONFIG_H
45 #include <config.h>
46 #endif
47
48 /* ------------------------------------------------------------------------- */
49 /* Native functions                                                          */
50 /* ------------------------------------------------------------------------- */
51
52 void KX_TouchSensor::SynchronizeTransform()
53 {
54
55         if (m_sumoObj)
56         {
57                 m_sumoObj->setPosition(((KX_GameObject*)GetParent())->NodeGetWorldPosition());
58                 m_sumoObj->setOrientation(
59                         ((KX_GameObject*)GetParent())->NodeGetWorldOrientation().getRotation()
60                         );
61                 m_sumoObj->calcXform();
62         }
63         
64 }
65
66
67 void KX_TouchSensor::EndFrame() {
68         m_colliders->ReleaseAndRemoveAll();
69         m_bTriggered = false;
70 }
71
72 bool KX_TouchSensor::Evaluate(CValue* event)
73 {
74         bool result = false;
75
76         if (m_bTriggered != m_bLastTriggered)
77         {
78                 m_bLastTriggered = m_bTriggered;
79                 if (!m_bTriggered)
80                         m_hitObject = NULL;
81                 result = true;
82         }
83         
84         return result;
85 }
86
87 KX_TouchSensor::KX_TouchSensor(SCA_EventManager* eventmgr,KX_GameObject* gameobj,/*SM_Object* sumoObj,*/bool bFindMaterial,const STR_String& touchedpropname,PyTypeObject* T)
88 :SCA_ISensor(gameobj,eventmgr,T),
89 m_touchedpropname(touchedpropname),
90 m_bFindMaterial(bFindMaterial),
91 m_eventmgr(eventmgr),
92 /*m_sumoObj(sumoObj),*/
93 m_bCollision(false),
94 m_bTriggered(false),
95 m_bLastTriggered(false)
96 {
97         KX_TouchEventManager* touchmgr = (KX_TouchEventManager*) eventmgr;
98 //      m_resptable = touchmgr->GetResponseTable();
99         
100 //      m_solidHandle = m_sumoObj->getObjectHandle();
101
102         m_hitObject =  NULL;
103         m_colliders = new CListValue();
104         
105         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
106         client_info->m_clientobject = gameobj;
107         client_info->m_auxilary_info = NULL;
108         
109         KX_SumoPhysicsController *sphy = dynamic_cast<KX_SumoPhysicsController *>(gameobj->GetPhysicsController());
110         if (sphy)
111                 m_sumoObj = sphy->GetSumoObject();
112
113 }
114
115
116 KX_TouchSensor::~KX_TouchSensor()
117 {
118         //DT_ClearObjectResponse(m_resptable,m_solidHandle);
119         m_colliders->Release();
120 }
121
122 CValue* KX_TouchSensor::GetReplica() 
123 {
124         KX_TouchSensor* replica = new KX_TouchSensor(*this);
125         replica->m_colliders = new CListValue();
126         replica->m_bCollision = false;
127         replica->m_bTriggered= false;
128         replica->m_hitObject = NULL;
129         replica->m_bLastTriggered = false;
130         // this will copy properties and so on...
131         CValue::AddDataToReplica(replica);
132         return replica;
133 }
134
135 void    KX_TouchSensor::ReParent(SCA_IObject* parent)
136 {
137         KX_GameObject *gameobj = static_cast<KX_GameObject *>(parent);
138         KX_SumoPhysicsController *sphy = dynamic_cast<KX_SumoPhysicsController *>(((KX_GameObject*)parent)->GetPhysicsController());
139         if (sphy)
140                 m_sumoObj = sphy->GetSumoObject();
141
142 //      m_solidHandle = m_sumoObj->getObjectHandle();
143         KX_ClientObjectInfo *client_info = gameobj->getClientInfo();
144         client_info->m_clientobject = parent;
145         client_info->m_auxilary_info = NULL;
146         SCA_ISensor::ReParent(parent);
147 }
148
149 void KX_TouchSensor::RegisterSumo(KX_TouchEventManager *touchman)
150 {
151         if (m_sumoObj)
152         {
153                 touchman->GetSumoScene()->requestCollisionCallback(*m_sumoObj);
154                 // collision
155                 // Deprecated   
156
157         }
158 }
159
160 DT_Bool    KX_TouchSensor::HandleCollision(void* obj1,void* obj2,const DT_CollData * coll_data)
161 {
162         KX_TouchEventManager* toucheventmgr = (KX_TouchEventManager*)m_eventmgr;
163         KX_GameObject* parent = (KX_GameObject*)GetParent();
164
165         // need the mapping from SM_Objects to gameobjects now
166         
167         KX_ClientObjectInfo* client_info =(KX_ClientObjectInfo*) (obj1 == m_sumoObj? 
168                                         ((SM_Object*)obj2)->getClientObject() : 
169                                         ((SM_Object*)obj1)->getClientObject());
170
171         KX_GameObject* gameobj = ( client_info ? 
172                         (KX_GameObject*)client_info->m_clientobject : 
173                         NULL);
174         
175         if (gameobj && (gameobj != parent) && client_info->isActor())
176         {
177                 if (!m_colliders->SearchValue(gameobj))
178                         m_colliders->Add(gameobj->AddRef());
179                 
180                 bool found = m_touchedpropname.IsEmpty();
181                 if (!found)
182                 {
183                         if (m_bFindMaterial)
184                         {
185                                 if (client_info->m_auxilary_info)
186                                 {
187                                         found = (m_touchedpropname == STR_String((char*)client_info->m_auxilary_info));
188                                 }
189                         } else
190                         {
191                                 found = (gameobj->GetProperty(m_touchedpropname) != NULL);
192                         }
193                 }
194                 if (found)
195                 {
196                         m_bTriggered = true;
197                         m_hitObject = gameobj;
198                         //printf("KX_TouchSensor::HandleCollision\n");
199                 }
200                 
201         } 
202         return DT_CONTINUE;
203 }
204
205
206 /* ------------------------------------------------------------------------- */
207 /* Python functions                                                          */
208 /* ------------------------------------------------------------------------- */
209 /* Integration hooks ------------------------------------------------------- */
210 PyTypeObject KX_TouchSensor::Type = {
211         PyObject_HEAD_INIT(&PyType_Type)
212         0,
213         "KX_TouchSensor",
214         sizeof(KX_TouchSensor),
215         0,
216         PyDestructor,
217         0,
218         __getattr,
219         __setattr,
220         0, //&MyPyCompare,
221         __repr,
222         0, //&cvalue_as_number,
223         0,
224         0,
225         0,
226         0
227 };
228
229 PyParentObject KX_TouchSensor::Parents[] = {
230         &KX_TouchSensor::Type,
231         &SCA_ISensor::Type,
232         &SCA_ILogicBrick::Type,
233         &CValue::Type,
234         NULL
235 };
236
237 PyMethodDef KX_TouchSensor::Methods[] = {
238         {"setProperty", 
239          (PyCFunction) KX_TouchSensor::sPySetProperty,      METH_VARARGS, SetProperty_doc},
240         {"getProperty", 
241          (PyCFunction) KX_TouchSensor::sPyGetProperty,      METH_VARARGS, GetProperty_doc},
242         {"getHitObject", 
243          (PyCFunction) KX_TouchSensor::sPyGetHitObject,     METH_VARARGS, GetHitObject_doc},
244         {"getHitObjectList", 
245          (PyCFunction) KX_TouchSensor::sPyGetHitObjectList, METH_VARARGS, GetHitObjectList_doc},
246         {NULL,NULL} //Sentinel
247 };
248
249 PyObject* KX_TouchSensor::_getattr(char* attr) {
250         _getattr_up(SCA_ISensor);
251 }
252
253 /* Python API */
254
255 /* 1. setProperty */
256 char KX_TouchSensor::SetProperty_doc[] = 
257 "setProperty(name)\n"
258 "\t- name: string\n"
259 "\tSet the property or material to collide with. Use\n"
260 "\tsetTouchMaterial() to switch between properties and\n"
261 "\tmaterials.";
262 PyObject* KX_TouchSensor::PySetProperty(PyObject* self, 
263                                                                                 PyObject* args, 
264                                                                                 PyObject* kwds) {
265         char *nameArg;
266         if (!PyArg_ParseTuple(args, "s", &nameArg)) {
267                 return NULL;
268         }
269
270         CValue* prop = GetParent()->FindIdentifier(nameArg);
271
272         if (!prop->IsError()) {
273                 m_touchedpropname = nameArg;
274                 prop->Release();
275         } else {
276                 ; /* not found ... */
277         }
278         
279         Py_Return;
280 }
281 /* 2. getProperty */
282 char KX_TouchSensor::GetProperty_doc[] = 
283 "getProperty(name)\n"
284 "\tReturns the property or material to collide with. Use\n"
285 "\tgetTouchMaterial() to find out whether this sensor\n"
286 "\tlooks for properties or materials.";
287 PyObject*  KX_TouchSensor::PyGetProperty(PyObject* self, 
288                                                                                  PyObject* args, 
289                                                                                  PyObject* kwds) {
290         return PyString_FromString(m_touchedpropname);
291 }
292
293 char KX_TouchSensor::GetHitObject_doc[] = 
294 "getHitObject()\n"
295 ;
296 PyObject* KX_TouchSensor::PyGetHitObject(PyObject* self, 
297                                                                                  PyObject* args, 
298                                                                                  PyObject* kwds)
299 {
300         /* to do: do Py_IncRef if the object is already known in Python */
301         /* otherwise, this leaks memory */
302         if (m_hitObject)
303         {
304                 return m_hitObject->AddRef();
305         }
306         Py_Return;
307 }
308
309 char KX_TouchSensor::GetHitObjectList_doc[] = 
310 "getHitObjectList()\n"
311 "\tReturn a list of the objects this object collided with,\n"
312 "\tbut only those matching the property/material condition.\n";
313 PyObject* KX_TouchSensor::PyGetHitObjectList(PyObject* self, 
314                                                                                  PyObject* args, 
315                                                                                  PyObject* kwds)
316 {
317
318         /* to do: do Py_IncRef if the object is already known in Python */
319         /* otherwise, this leaks memory */
320
321         if ( m_touchedpropname.IsEmpty() ) {
322                 return m_colliders->AddRef();
323         } else {
324                 CListValue* newList = new CListValue();
325                 int i = 0;
326                 while (i < m_colliders->GetCount()) {
327                         if (m_bFindMaterial) {
328                                 /* need to associate the CValues from the list to material
329                                  * names. The collider list _should_ contains only
330                                  * KX_GameObjects. I am loathe to cast them, though... The
331                                  * material name must be retrieved from Sumo. To a Sumo
332                                  * object, a client-info block is attached. This block
333                                  * contains the material name. 
334                                  * - this also doesn't work (obviously) for multi-materials... 
335                                  */
336                                 KX_GameObject* gameob = (KX_GameObject*) m_colliders->GetValue(i);
337                                 KX_SumoPhysicsController* spc = dynamic_cast<KX_SumoPhysicsController*>(gameob->GetPhysicsController());
338                                 SM_Object* smob = spc?spc->GetSumoObject():NULL;
339                                 
340                                 if (smob) {
341                                         KX_ClientObjectInfo* cl_inf = (KX_ClientObjectInfo*) smob->getClientObject();
342                                         
343                                         if (m_touchedpropname == ((char*)cl_inf->m_auxilary_info)) {
344                                                 newList->Add(m_colliders->GetValue(i)->AddRef());
345                                         } 
346                                 }
347                                 
348                         } else {
349                                 CValue* val = m_colliders->GetValue(i)->FindIdentifier(m_touchedpropname);
350                                 if (!val->IsError()) {
351                                         newList->Add(m_colliders->GetValue(i)->AddRef());
352                                         val->Release();
353                                 }
354                         }
355                         
356                         i++;
357                 }
358                 return newList->AddRef();
359         }
360
361 }
362
363 /* 5. getTouchMaterial */
364 char KX_TouchSensor::GetTouchMaterial_doc[] = 
365 "getTouchMaterial()\n"
366 "\tReturns KX_TRUE if this sensor looks for a specific material,\n"
367 "\tKX_FALSE if it looks for a specific property.\n" ;
368 PyObject* KX_TouchSensor::PyGetTouchMaterial(PyObject* self, 
369                                                                                          PyObject* args, 
370                                                                                          PyObject* kwds)
371 {
372         int retval = 0;
373         
374         if (m_bFindMaterial) {
375                 retval = KX_TRUE;
376         } else {
377                 retval = KX_FALSE;
378         }
379
380         return PyInt_FromLong(retval);
381 }
382
383 /* 6. setTouchMaterial */
384 char KX_TouchSensor::SetTouchMaterial_doc[] = 
385 "setTouchMaterial(flag)\n"
386 "\t- flag: KX_TRUE or KX_FALSE.\n"
387 "\tSet flag to KX_TRUE to switch on positive pulse mode,\n"
388 "\tKX_FALSE to switch off positive pulse mode.\n" ;
389 PyObject* KX_TouchSensor::PySetTouchMaterial(PyObject* self, PyObject* args, PyObject* kwds)
390 {
391         int pulseArg = 0;
392
393         if(!PyArg_ParseTuple(args, "i", &pulseArg)) {
394                 return NULL;
395         }
396         
397         if (pulseArg == KX_TRUE) {
398                 m_bFindMaterial = true;
399         } else if (pulseArg == KX_FALSE){
400                 m_bFindMaterial = false;
401         } else {
402                 ; /* internal error */
403         }
404
405         Py_Return;
406 }
407
408
409 /* eof */