blender/source/gameengine/Ketsji/KX_MouseActuator.cpp
2015-06-14 04:53:10 +10:00

540 lines
14 KiB
C++

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Geoffrey Gollmer, Jorge Bernal
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "KX_MouseActuator.h"
#include "KX_KetsjiEngine.h"
#include "SCA_MouseManager.h"
#include "SCA_IInputDevice.h"
#include "RAS_ICanvas.h"
#include "KX_GameObject.h"
#include "MT_Vector3.h"
#include "MT_Scalar.h"
#include "MT_assert.h"
#include "limits.h"
#include "BLI_math.h"
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/* ------------------------------------------------------------------------- */
/* Native functions */
/* ------------------------------------------------------------------------- */
KX_MouseActuator::KX_MouseActuator(
SCA_IObject* gameobj,
KX_KetsjiEngine* ketsjiEngine,
SCA_MouseManager* eventmgr,
int acttype,
bool visible,
bool* use_axis,
float* threshold,
bool* reset,
int* object_axis,
bool* local,
float* sensitivity,
float* limit_x,
float* limit_y
):
SCA_IActuator(gameobj, KX_ACT_MOUSE),
m_ketsji(ketsjiEngine),
m_eventmgr(eventmgr),
m_type(acttype),
m_visible(visible),
m_use_axis_x(use_axis[0]),
m_use_axis_y(use_axis[1]),
m_reset_x(reset[0]),
m_reset_y(reset[1]),
m_local_x(local[0]),
m_local_y(local[1])
{
m_canvas = m_ketsji->GetCanvas();
m_oldposition[0] = m_oldposition[1] = -1.f;
m_limit_x[0] = limit_x[0];
m_limit_x[1] = limit_x[1];
m_limit_y[0] = limit_y[0];
m_limit_y[1] = limit_y[1];
m_threshold[0] = threshold[0];
m_threshold[1] = threshold[1];
m_object_axis[0] = object_axis[0];
m_object_axis[1] = object_axis[1];
m_sensitivity[0] = sensitivity[0];
m_sensitivity[1] = sensitivity[1];
m_angle[0] = 0.f;
m_angle[1] = 0.f;
}
KX_MouseActuator::~KX_MouseActuator()
{
}
bool KX_MouseActuator::Update()
{
bool result = false;
bool bNegativeEvent = IsNegativeEvent();
RemoveAllEvents();
if (bNegativeEvent)
return false; // do nothing on negative events
KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
m_mouse = ((SCA_MouseManager *)m_eventmgr)->GetInputDevice();
switch (m_type) {
case KX_ACT_MOUSE_VISIBILITY:
{
if (m_visible) {
if (m_canvas) {
m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
}
}
else {
if (m_canvas) {
m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
}
}
break;
}
case KX_ACT_MOUSE_LOOK:
{
if (m_mouse) {
float position[2];
float movement[2];
MT_Vector3 rotation;
float setposition[2] = {0.0};
float center_x = 0.5, center_y = 0.5;
getMousePosition(position);
movement[0] = position[0];
movement[1] = position[1];
//preventing undesired drifting when resolution is odd
if ((m_canvas->GetWidth() % 2) != 0) {
center_x = ((m_canvas->GetWidth() - 1.0) / 2.0) / (m_canvas->GetWidth());
}
if ((m_canvas->GetHeight() % 2) != 0) {
center_y = ((m_canvas->GetHeight() - 1.0) / 2.0) / (m_canvas->GetHeight());
}
//preventing initial skipping.
if ((m_oldposition[0] <= -0.9) && (m_oldposition[1] <= -0.9)) {
if (m_reset_x) {
m_oldposition[0] = center_x;
}
else {
m_oldposition[0] = position[0];
}
if (m_reset_y) {
m_oldposition[1] = center_y;
}
else {
m_oldposition[1] = position[1];
}
setMousePosition(m_oldposition[0], m_oldposition[1]);
break;
}
//Calculating X axis.
if (m_use_axis_x) {
if (m_reset_x) {
setposition[0] = center_x;
movement[0] -= center_x;
}
else {
setposition[0] = position[0];
movement[0] -= m_oldposition[0];
}
movement[0] *= -1.0;
/* Don't apply the rotation when we are under a certain threshold for mouse
movement */
if (((movement[0] > (m_threshold[0] / 10.0)) ||
((movement[0] * (-1.0)) > (m_threshold[0] / 10.0)))) {
movement[0] *= m_sensitivity[0];
if ((m_limit_x[0] != 0.0) && ((m_angle[0] + movement[0]) <= m_limit_x[0])) {
movement[0] = m_limit_x[0] - m_angle[0];
}
if ((m_limit_x[1] != 0.0) && ((m_angle[0] + movement[0]) >= m_limit_x[1])) {
movement[0] = m_limit_x[1] - m_angle[0];
}
m_angle[0] += movement[0];
switch (m_object_axis[0]) {
case KX_ACT_MOUSE_OBJECT_AXIS_X:
{
rotation = MT_Vector3(movement[0], 0.0, 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Y:
{
rotation = MT_Vector3(0.0, movement[0], 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Z:
{
rotation = MT_Vector3(0.0, 0.0, movement[0]);
break;
}
default:
break;
}
parent->ApplyRotation(rotation, m_local_x);
}
}
else {
setposition[0] = center_x;
}
//Calculating Y axis.
if (m_use_axis_y) {
if (m_reset_y) {
setposition[1] = center_y;
movement[1] -= center_y;
}
else {
setposition[1] = position[1];
movement[1] -= m_oldposition[1];
}
movement[1] *= -1.0;
/* Don't apply the rotation when we are under a certain threshold for mouse
movement */
if (((movement[1] > (m_threshold[1] / 10.0)) ||
((movement[1] * (-1.0)) > (m_threshold[1] / 10.0)))) {
movement[1] *= m_sensitivity[1];
if ((m_limit_y[0] != 0.0) && ((m_angle[1] + movement[1]) <= m_limit_y[0])) {
movement[1] = m_limit_y[0] - m_angle[1];
}
if ((m_limit_y[1] != 0.0) && ((m_angle[1] + movement[1]) >= m_limit_y[1])) {
movement[1] = m_limit_y[1] - m_angle[1];
}
m_angle[1] += movement[1];
switch (m_object_axis[1])
{
case KX_ACT_MOUSE_OBJECT_AXIS_X:
{
rotation = MT_Vector3(movement[1], 0.0, 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Y:
{
rotation = MT_Vector3(0.0, movement[1], 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Z:
{
rotation = MT_Vector3(0.0, 0.0, movement[1]);
break;
}
default:
break;
}
parent->ApplyRotation(rotation, m_local_y);
}
}
else {
setposition[1] = center_y;
}
// only trigger mouse event when it is necessary
if (m_oldposition[0] != position[0] || m_oldposition[1] != position[1]) {
setMousePosition(setposition[0], setposition[1]);
}
m_oldposition[0] = position[0];
m_oldposition[1] = position[1];
}
else {
//printf("\nNo input device detected for mouse actuator\n");
}
break;
}
default:
break;
}
return result;
}
bool KX_MouseActuator::isValid(KX_MouseActuator::KX_ACT_MOUSE_MODE mode)
{
return ((mode > KX_ACT_MOUSE_NODEF) && (mode < KX_ACT_MOUSE_MAX));
}
CValue* KX_MouseActuator::GetReplica()
{
KX_MouseActuator* replica = new KX_MouseActuator(*this);
replica->ProcessReplica();
return replica;
}
void KX_MouseActuator::ProcessReplica()
{
SCA_IActuator::ProcessReplica();
}
void KX_MouseActuator::getMousePosition(float* pos)
{
MT_assert(m_mouse);
const SCA_InputEvent & xevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEX);
const SCA_InputEvent & yevent = m_mouse->GetEventValue(SCA_IInputDevice::KX_MOUSEY);
pos[0] = m_canvas->GetMouseNormalizedX(xevent.m_eventval);
pos[1] = m_canvas->GetMouseNormalizedY(yevent.m_eventval);
}
void KX_MouseActuator::setMousePosition(float fx, float fy)
{
int x, y;
x = (int)(fx * m_canvas->GetWidth());
y = (int)(fy * m_canvas->GetHeight());
m_canvas->SetMousePosition(x, y);
}
#ifdef WITH_PYTHON
/* ------------------------------------------------------------------------- */
/* Python functions */
/* ------------------------------------------------------------------------- */
/* Integration hooks ------------------------------------------------------- */
PyTypeObject KX_MouseActuator::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"KX_MouseActuator",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,0,0,0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&SCA_IActuator::Type,
0,0,0,0,0,0,
py_base_new
};
PyMethodDef KX_MouseActuator::Methods[] = {
{"reset", (PyCFunction) KX_MouseActuator::sPyReset, METH_NOARGS,"reset() : undo rotation caused by actuator\n"},
{NULL,NULL} //Sentinel
};
PyAttributeDef KX_MouseActuator::Attributes[] = {
KX_PYATTRIBUTE_BOOL_RW("visible", KX_MouseActuator, m_visible),
KX_PYATTRIBUTE_BOOL_RW("use_axis_x", KX_MouseActuator, m_use_axis_x),
KX_PYATTRIBUTE_BOOL_RW("use_axis_y", KX_MouseActuator, m_use_axis_y),
KX_PYATTRIBUTE_FLOAT_ARRAY_RW("threshold", 0.0, 0.5, KX_MouseActuator, m_threshold, 2),
KX_PYATTRIBUTE_BOOL_RW("reset_x", KX_MouseActuator, m_reset_x),
KX_PYATTRIBUTE_BOOL_RW("reset_y", KX_MouseActuator, m_reset_y),
KX_PYATTRIBUTE_INT_ARRAY_RW("object_axis", 0, 2, 1, KX_MouseActuator, m_object_axis, 2),
KX_PYATTRIBUTE_BOOL_RW("local_x", KX_MouseActuator, m_local_x),
KX_PYATTRIBUTE_BOOL_RW("local_y", KX_MouseActuator, m_local_y),
KX_PYATTRIBUTE_FLOAT_ARRAY_RW("sensitivity", -FLT_MAX, FLT_MAX, KX_MouseActuator, m_sensitivity, 2),
KX_PYATTRIBUTE_RW_FUNCTION("limit_x", KX_MouseActuator, pyattr_get_limit_x, pyattr_set_limit_x),
KX_PYATTRIBUTE_RW_FUNCTION("limit_y", KX_MouseActuator, pyattr_get_limit_y, pyattr_set_limit_y),
KX_PYATTRIBUTE_RW_FUNCTION("angle", KX_MouseActuator, pyattr_get_angle, pyattr_set_angle),
{ NULL } //Sentinel
};
PyObject* KX_MouseActuator::pyattr_get_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
return Py_BuildValue("[f,f]", (self->m_limit_x[0] / M_PI * 180.0), (self->m_limit_x[1] / M_PI * 180.0));
}
int KX_MouseActuator::pyattr_set_limit_x(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
PyObject *item1, *item2;
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
if (!PyList_Check(value))
return PY_SET_ATTR_FAIL;
if (PyList_Size(value) != 2)
return PY_SET_ATTR_FAIL;
item1 = PyList_GET_ITEM(value, 0);
item2 = PyList_GET_ITEM(value, 1);
if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
return PY_SET_ATTR_FAIL;
}
else {
self->m_limit_x[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
self->m_limit_x[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
}
return PY_SET_ATTR_SUCCESS;
}
PyObject* KX_MouseActuator::pyattr_get_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
return Py_BuildValue("[f,f]", (self->m_limit_y[0] / M_PI * 180.0), (self->m_limit_y[1] / M_PI * 180.0));
}
int KX_MouseActuator::pyattr_set_limit_y(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
PyObject *item1, *item2;
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
if (!PyList_Check(value))
return PY_SET_ATTR_FAIL;
if (PyList_Size(value) != 2)
return PY_SET_ATTR_FAIL;
item1 = PyList_GET_ITEM(value, 0);
item2 = PyList_GET_ITEM(value, 1);
if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
return PY_SET_ATTR_FAIL;
}
else {
self->m_limit_y[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
self->m_limit_y[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
}
return PY_SET_ATTR_SUCCESS;
}
PyObject* KX_MouseActuator::pyattr_get_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
return Py_BuildValue("[f,f]", (self->m_angle[0] / M_PI * 180.0), (self->m_angle[1] / M_PI * 180.0));
}
int KX_MouseActuator::pyattr_set_angle(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
PyObject *item1, *item2;
KX_MouseActuator* self= static_cast<KX_MouseActuator*>(self_v);
if (!PyList_Check(value))
return PY_SET_ATTR_FAIL;
if (PyList_Size(value) != 2)
return PY_SET_ATTR_FAIL;
item1 = PyList_GET_ITEM(value, 0);
item2 = PyList_GET_ITEM(value, 1);
if (!(PyFloat_Check(item1)) || !(PyFloat_Check(item2))) {
return PY_SET_ATTR_FAIL;
}
else {
self->m_angle[0] = (PyFloat_AsDouble(item1) * M_PI / 180.0);
self->m_angle[1] = (PyFloat_AsDouble(item2) * M_PI / 180.0);
}
return PY_SET_ATTR_SUCCESS;
}
PyObject* KX_MouseActuator::PyReset()
{
MT_Vector3 rotation;
KX_GameObject *parent = static_cast<KX_GameObject *>(GetParent());
switch (m_object_axis[0]) {
case KX_ACT_MOUSE_OBJECT_AXIS_X:
{
rotation = MT_Vector3(-1.0 * m_angle[0], 0.0, 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Y:
{
rotation = MT_Vector3(0.0, -1.0 * m_angle[0], 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Z:
{
rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[0]);
break;
}
default:
break;
}
parent->ApplyRotation(rotation, m_local_x);
switch (m_object_axis[1]) {
case KX_ACT_MOUSE_OBJECT_AXIS_X:
{
rotation = MT_Vector3(-1.0 * m_angle[1], 0.0, 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Y:
{
rotation = MT_Vector3(0.0, -1.0 * m_angle[1], 0.0);
break;
}
case KX_ACT_MOUSE_OBJECT_AXIS_Z:
{
rotation = MT_Vector3(0.0, 0.0, -1.0 * m_angle[1]);
break;
}
default:
break;
}
parent->ApplyRotation(rotation, m_local_y);
m_angle[0] = 0.0;
m_angle[1] = 0.0;
Py_RETURN_NONE;
}
#endif /* WITH_PYTHON */