8774abe04b2bb9003eb3e762362ca288936bc717
[blender.git] / source / gameengine / Ketsji / KX_MouseActuator.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Geoffrey Gollmer, Jorge Bernal
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 #include "KX_MouseActuator.h"
24 #include "KX_KetsjiEngine.h"
25 #include "SCA_MouseManager.h"
26 #include "SCA_IInputDevice.h"
27 #include "RAS_ICanvas.h"
28 #include "KX_GameObject.h"
29 #include "MT_Vector3.h"
30 #include "MT_Scalar.h"
31 #include "MT_assert.h"
32 #include "limits.h"
33
34 #include "BLI_math.h"
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 /* ------------------------------------------------------------------------- */
41 /* Native functions                                                          */
42 /* ------------------------------------------------------------------------- */
43
44 KX_MouseActuator::KX_MouseActuator(
45         SCA_IObject* gameobj,
46
47         KX_KetsjiEngine* ketsjiEngine,
48         SCA_MouseManager* eventmgr,
49         int acttype,
50         bool visible,
51         bool* use_axis,
52         float* threshold,
53         bool* reset,
54         int* object_axis,
55         bool* local,
56         float* sensitivity,
57         float* limit_x,
58         float* limit_y
59 ):
60         SCA_IActuator(gameobj, KX_ACT_MOUSE),
61         m_ketsji(ketsjiEngine),
62         m_eventmgr(eventmgr),
63         m_type(acttype),
64         m_visible(visible),
65         m_use_axis_x(use_axis[0]),
66         m_use_axis_y(use_axis[1]),
67         m_reset_x(reset[0]),
68         m_reset_y(reset[1]),
69         m_local_x(local[0]),
70         m_local_y(local[1])
71 {
72         m_canvas = m_ketsji->GetCanvas();
73         m_oldposition[0] = m_oldposition[1] = -1.f;
74         m_limit_x[0] = limit_x[0];
75         m_limit_x[1] = limit_x[1];
76         m_limit_y[0] = limit_y[0];
77         m_limit_y[1] = limit_y[1];
78         m_threshold[0] = threshold[0];
79         m_threshold[1] = threshold[1];
80         m_object_axis[0] = object_axis[0];
81         m_object_axis[1] = object_axis[1];
82         m_sensitivity[0] = sensitivity[0];
83         m_sensitivity[1] = sensitivity[1];
84         m_angle[0] = 0.f;
85         m_angle[1] = 0.f;
86 }
87
88 KX_MouseActuator::~KX_MouseActuator()
89 {
90 }
91
92 bool KX_MouseActuator::Update()
93 {
94         bool result = false;
95
96         bool bNegativeEvent = IsNegativeEvent();
97         RemoveAllEvents();
98
99         if (bNegativeEvent)
100                 return false; // do nothing on negative events
101
102         KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
103
104         m_mouse = ((SCA_MouseManager *)m_eventmgr)->GetInputDevice();
105
106         switch (m_type) {
107                 case KX_ACT_MOUSE_VISIBILITY:
108                 {
109                         if (m_visible) {
110                                 if (m_canvas) {
111                                         m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
112                                 }
113                         }
114                         else {
115                                 if (m_canvas) {
116                                         m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
117                                 }
118                         }
119                         break;
120                 }
121                 case KX_ACT_MOUSE_LOOK:
122                 {
123                         if (m_mouse) {
124
125                                 float position[2];
126                                 float movement[2];
127                                 MT_Vector3 rotation;
128                                 float setposition[2] = {0.0};
129                                 float center_x = 0.5, center_y = 0.5;
130
131                                 getMousePosition(position);
132
133                                 movement[0] = position[0];
134                                 movement[1] = position[1];
135
136                                 //preventing undesired drifting when resolution is odd
137                                 if ((m_canvas->GetWidth() % 2) != 0) {
138                                         center_x = ((m_canvas->GetWidth() - 1.0) / 2.0) / (m_canvas->GetWidth());
139                                 }
140                                 if ((m_canvas->GetHeight() % 2) != 0) {
141                                     center_y = ((m_canvas->GetHeight() - 1.0) / 2.0) / (m_canvas->GetHeight());
142                                 }
143
144                                 //preventing initial skipping.
145                                 if ((m_oldposition[0] <= -0.9) && (m_oldposition[1] <= -0.9)) {
146
147                                         if (m_reset_x) {
148                                                 m_oldposition[0] = center_x;
149                                         }
150                                         else {
151                                                 m_oldposition[0] = position[0];
152                                         }
153
154                                         if (m_reset_y) {
155                                                 m_oldposition[1] = center_y;
156                                         }
157                                         else {
158                                                 m_oldposition[1] = position[1];
159                                         }
160                                         setMousePosition(m_oldposition[0], m_oldposition[1]);
161                                         break;
162                                 }
163
164                                 //Calculating X axis.
165                                 if (m_use_axis_x) {
166
167                                         if (m_reset_x) {
168                                                 setposition[0] = center_x;
169                                                 movement[0] -= center_x;
170                                         }
171                                         else {
172                                                 setposition[0] = position[0];
173                                                 movement[0] -= m_oldposition[0];
174                                         }
175
176                                         movement[0] *= -1.0;
177
178                                         /* Don't apply the rotation when we are under a certain threshold for mouse
179                                           movement */
180
181                                         if (((movement[0] > (m_threshold[0] / 10.0)) ||
182                                             ((movement[0] * (-1.0)) > (m_threshold[0] / 10.0)))) {
183
184                                                 movement[0] *= m_sensitivity[0];
185
186                                                 if ((m_limit_x[0] != 0.0) && ((m_angle[0] + movement[0]) <= m_limit_x[0])) {
187                                                         movement[0] = m_limit_x[0] - m_angle[0];
188                                                 }
189
190                                                 if ((m_limit_x[1] != 0.0) && ((m_angle[0] + movement[0]) >= m_limit_x[1])) {
191                                                         movement[0] = m_limit_x[1] - m_angle[0];
192                                                 }
193
194                                                 m_angle[0] += movement[0];
195
196                                                 switch (m_object_axis[0]) {
197                                                         case KX_ACT_MOUSE_OBJECT_AXIS_X:
198                                                         {
199                                                                 rotation = MT_Vector3(movement[0], 0.0, 0.0);
200                                                                 break;
201                                                         }
202                                                         case KX_ACT_MOUSE_OBJECT_AXIS_Y:
203                                                         {
204                                                                 rotation = MT_Vector3(0.0, movement[0], 0.0);
205                                                                 break;
206                                                         }
207                                                         case KX_ACT_MOUSE_OBJECT_AXIS_Z:
208                                                         {
209                                                                 rotation = MT_Vector3(0.0, 0.0, movement[0]);
210                                                                 break;
211                                                         }
212                                                         default:
213                                                                 break;
214                                                 }
215                                                 parent->ApplyRotation(rotation, m_local_x);
216                                         }
217                                 }
218                                 else {
219                                         setposition[0] = center_x;
220                                 }
221
222                                 //Calculating Y axis.
223                                 if (m_use_axis_y) {
224
225                                         if (m_reset_y) {
226                                                 setposition[1] = center_y;
227                                                 movement[1] -= center_y;
228                                         }
229                                         else {
230                                                 setposition[1] = position[1];
231                                                 movement[1] -= m_oldposition[1];
232                                         }
233
234                                         movement[1] *= -1.0;
235
236                                         /* Don't apply the rotation when we are under a certain threshold for mouse
237                                           movement */
238
239                                         if (((movement[1] > (m_threshold[1] / 10.0)) ||
240                                             ((movement[1] * (-1.0)) > (m_threshold[1] / 10.0)))) {
241
242                                                 movement[1] *= m_sensitivity[1];
243
244                                                 if ((m_limit_y[0] != 0.0) && ((m_angle[1] + movement[1]) <= m_limit_y[0])) {
245                                                         movement[1] = m_limit_y[0] - m_angle[1];
246                                                 }
247
248                                                 if ((m_limit_y[1] != 0.0) && ((m_angle[1] + movement[1]) >= m_limit_y[1])) {
249                                                         movement[1] = m_limit_y[1] - m_angle[1];
250                                                 }
251
252                                                 m_angle[1] += movement[1];
253
254                                                 switch (m_object_axis[1])
255                                                 {
256                                                         case KX_ACT_MOUSE_OBJECT_AXIS_X:
257                                                         {
258                                                                 rotation = MT_Vector3(movement[1], 0.0, 0.0);
259                                                                 break;
260                                                         }
261                                                         case KX_ACT_MOUSE_OBJECT_AXIS_Y:
262                                                         {
263                                                                 rotation = MT_Vector3(0.0, movement[1], 0.0);
264                                                                 break;
265                                                         }
266                                                         case KX_ACT_MOUSE_OBJECT_AXIS_Z:
267                                                         {
268                                                                 rotation = MT_Vector3(0.0, 0.0, movement[1]);
269                                                                 break;
270                                                         }
271                                                         default:
272                                                                 break;
273                                                 }
274                                                 parent->ApplyRotation(rotation, m_local_y);
275                                         }
276                                 }
277                                 else {
278                                         setposition[1] = center_y;
279                                 }
280
281                                 setMousePosition(setposition[0], setposition[1]);
282
283                                 m_oldposition[0] = position[0];
284                                 m_oldposition[1] = position[1];
285
286                         }
287                         else {
288                                 //printf("\nNo input device detected for mouse actuator\n");
289                         }
290                         break;
291                 }
292                 default:
293                         break;
294                 }
295         return result;
296 }
297
298 bool KX_MouseActuator::isValid(KX_MouseActuator::KX_ACT_MOUSE_MODE mode)
299 {
300         return ((mode > KX_ACT_MOUSE_NODEF) && (mode < KX_ACT_MOUSE_MAX));
301 }
302
303
304 CValue* KX_MouseActuator::GetReplica()
305 {
306         KX_MouseActuator* replica = new KX_MouseActuator(*this);
307
308         replica->ProcessReplica();
309         return replica;
310 }
311
312 void KX_MouseActuator::ProcessReplica()
313 {
314         SCA_IActuator::ProcessReplica();
315 }
316
317 void KX_MouseActuator::getMousePosition(float* pos)
318 {
319         MT_assert(!m_mouse);
320         const SCA_InputEvent & xevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEX);
321         const SCA_InputEvent & yevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEY);
322
323         pos[0] = m_canvas->GetMouseNormalizedX(xevent.m_eventval);
324         pos[1] = m_canvas->GetMouseNormalizedY(yevent.m_eventval);
325 }
326
327 void KX_MouseActuator::setMousePosition(float fx, float fy)
328 {
329         int x, y;
330
331         x = (int)(fx * m_canvas->GetWidth());
332         y = (int)(fy * m_canvas->GetHeight());
333
334         m_canvas->SetMousePosition(x, y);
335 }
336
337 #ifndef DISABLE_PYTHON
338
339 /* ------------------------------------------------------------------------- */
340 /* Python functions                                                          */
341 /* ------------------------------------------------------------------------- */
342
343 /* Integration hooks ------------------------------------------------------- */
344 PyTypeObject KX_MouseActuator::Type = {
345         PyVarObject_HEAD_INIT(NULL, 0)
346         "KX_MouseActuator",
347         sizeof(PyObjectPlus_Proxy),
348         0,
349         py_base_dealloc,
350         0,
351         0,
352         0,
353         0,
354         py_base_repr,
355         0,0,0,0,0,0,0,0,0,
356         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
357         0,0,0,0,0,0,0,
358         Methods,
359         0,
360         0,
361         &SCA_IActuator::Type,
362         0,0,0,0,0,0,
363         py_base_new
364 };
365
366 PyMethodDef KX_MouseActuator::Methods[] = {
367         {"reset", (PyCFunction) KX_MouseActuator::sPyReset, METH_NOARGS,"reset() : undo rotation caused by actuator\n"},
368         {NULL,NULL} //Sentinel
369 };
370
371
372
373 PyAttributeDef KX_MouseActuator::Attributes[] = {
374         KX_PYATTRIBUTE_BOOL_RW("visible", KX_MouseActuator, m_visible),
375         KX_PYATTRIBUTE_BOOL_RW("use_axis_x", KX_MouseActuator, m_use_axis_x),
376         KX_PYATTRIBUTE_BOOL_RW("use_axis_y", KX_MouseActuator, m_use_axis_y),
377         KX_PYATTRIBUTE_FLOAT_ARRAY_RW("threshold", 0.0, 0.5, KX_MouseActuator, m_threshold, 2),
378         KX_PYATTRIBUTE_BOOL_RW("reset_x", KX_MouseActuator, m_reset_x),
379         KX_PYATTRIBUTE_BOOL_RW("reset_y", KX_MouseActuator, m_reset_y),
380         KX_PYATTRIBUTE_INT_ARRAY_RW("object_axis", 0, 2, 1, KX_MouseActuator, m_object_axis, 2),
381         KX_PYATTRIBUTE_BOOL_RW("local_x", KX_MouseActuator, m_local_x),
382         KX_PYATTRIBUTE_BOOL_RW("local_y", KX_MouseActuator, m_local_y),
383         KX_PYATTRIBUTE_FLOAT_ARRAY_RW("sensitivity", -FLT_MAX, FLT_MAX, KX_MouseActuator, m_sensitivity, 2),
384         KX_PYATTRIBUTE_RW_FUNCTION("limit_x", KX_MouseActuator, pyattr_get_limit_x, pyattr_set_limit_x),
385         KX_PYATTRIBUTE_RW_FUNCTION("limit_y", KX_MouseActuator, pyattr_get_limit_y, pyattr_set_limit_y),
386         KX_PYATTRIBUTE_RW_FUNCTION("angle", KX_MouseActuator, pyattr_get_angle, pyattr_set_angle),
387         { NULL }        //Sentinel
388 };
389
390 PyObject* KX_MouseActuator::pyattr_get_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
391 {
392         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
393         return Py_BuildValue("[f,f]", (self->m_limit_x[0] / M_PI * 180.0), (self->m_limit_x[1] / M_PI * 180.0));
394 }
395
396 int KX_MouseActuator::pyattr_set_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
397 {
398         PyObject *item1, *item2;
399         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
400
401         if (!PyList_Check(value))
402                 return PY_SET_ATTR_FAIL;
403
404         if (PyList_Size(value) != 2)
405                 return PY_SET_ATTR_FAIL;
406
407         item1 = PyList_GET_ITEM(value, 0);
408         item2 = PyList_GET_ITEM(value, 1);
409
410         if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
411                 return PY_SET_ATTR_FAIL;
412         }
413         else {
414                 self->m_limit_x[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
415                 self->m_limit_x[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
416         }
417
418         return PY_SET_ATTR_SUCCESS;
419 }
420
421 PyObject* KX_MouseActuator::pyattr_get_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
422 {
423         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
424         return Py_BuildValue("[f,f]", (self->m_limit_y[0] / M_PI * 180.0), (self->m_limit_y[1] / M_PI * 180.0));
425 }
426
427 int KX_MouseActuator::pyattr_set_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
428 {
429         PyObject *item1, *item2;
430         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
431
432         if (!PyList_Check(value))
433                 return PY_SET_ATTR_FAIL;
434
435         if (PyList_Size(value) != 2)
436                 return PY_SET_ATTR_FAIL;
437
438         item1 = PyList_GET_ITEM(value, 0);
439         item2 = PyList_GET_ITEM(value, 1);
440
441         if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
442                 return PY_SET_ATTR_FAIL;
443         }
444         else {
445                 self->m_limit_y[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
446                 self->m_limit_y[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
447         }
448
449         return PY_SET_ATTR_SUCCESS;
450 }
451
452 PyObject* KX_MouseActuator::pyattr_get_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
453 {
454         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
455         return Py_BuildValue("[f,f]", (self->m_angle[0] / M_PI * 180.0), (self->m_angle[1] / M_PI * 180.0));
456 }
457
458 int KX_MouseActuator::pyattr_set_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
459 {
460         PyObject *item1, *item2;
461         KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
462
463         if (!PyList_Check(value))
464                 return PY_SET_ATTR_FAIL;
465
466         if (PyList_Size(value) != 2)
467                 return PY_SET_ATTR_FAIL;
468
469         item1 = PyList_GET_ITEM(value, 0);
470         item2 = PyList_GET_ITEM(value, 1);
471
472         if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
473                 return PY_SET_ATTR_FAIL;
474         }
475         else {
476                 self->m_angle[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
477                 self->m_angle[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
478         }
479
480         return PY_SET_ATTR_SUCCESS;
481 }
482
483 PyObject* KX_MouseActuator::PyReset()
484 {
485         MT_Vector3 rotation;
486         KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
487
488         switch (m_object_axis[0]) {
489                 case KX_ACT_MOUSE_OBJECT_AXIS_X:
490                 {
491                         rotation = MT_Vector3(-1.0 * m_angle[0], 0.0, 0.0);
492                         break;
493                 }
494                 case KX_ACT_MOUSE_OBJECT_AXIS_Y:
495                 {
496                         rotation = MT_Vector3(0.0, -1.0 * m_angle[0], 0.0);
497                         break;
498                 }
499                 case KX_ACT_MOUSE_OBJECT_AXIS_Z:
500                 {
501                         rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[0]);
502                         break;
503                 }
504                 default:
505                         break;
506         }
507         parent->ApplyRotation(rotation, m_local_x);
508
509         switch (m_object_axis[1]) {
510                 case KX_ACT_MOUSE_OBJECT_AXIS_X:
511                 {
512                         rotation = MT_Vector3(-1.0 * m_angle[1], 0.0, 0.0);
513                         break;
514                 }
515                 case KX_ACT_MOUSE_OBJECT_AXIS_Y:
516                 {
517                         rotation = MT_Vector3(0.0, -1.0 * m_angle[1], 0.0);
518                         break;
519                 }
520                 case KX_ACT_MOUSE_OBJECT_AXIS_Z:
521                 {
522                         rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[1]);
523                         break;
524                 }
525                 default:
526                         break;
527         }
528         parent->ApplyRotation(rotation, m_local_y);
529
530         m_angle[0] = 0.0;
531         m_angle[1] = 0.0;
532
533         Py_RETURN_NONE;
534 }
535
536 #endif