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