1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2016-21 Mandelbulber Team     §R-==%w["'~5]m%=L.=~5N
5  *                                        ,=mm=§M ]=4 yJKA"/-Nsaj  "Bw,==,,
6  * This file is part of Mandelbulber.    §R.r= jw",M  Km .mM  FW ",§=ß., ,TN
7  *                                     ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
8  * Mandelbulber is free software:     §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
9  * you can redistribute it and/or     §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
10  * modify it under the terms of the    "§M=M =D=4"N #"%==A%p M§ M6  R' #"=~.4M
11  * GNU General Public License as        §W =, ][T"]C  §  § '§ e===~ U  !§[Z ]N
12  * published by the                    4M",,Jm=,"=e~  §  §  j]]""N  BmM"py=ßM
13  * Free Software Foundation,          ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
14  * either version 3 of the License,    TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
15  * or (at your option)                   TW=,-#"%=;[  =Q:["V""  ],,M.m == ]N
16  * any later version.                      J§"mr"] ,=,," =="""J]= M"M"]==ß"
17  *                                          §= "=C=4 §"eM "=B:m|4"]#F,§~
18  * Mandelbulber is distributed in            "9w=,,]w em%wJ '"~" ,=,,ß"
19  * the hope that it will be useful,                 . "K=  ,=RMMMßM"""
20  * but WITHOUT ANY WARRANTY;                            .'''
21  * without even the implied warranty
22  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23  *
24  * See the GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
27  *
28  * ###########################################################################
29  *
30  * Authors: Krzysztof Marczak (buddhi1980@gmail.com), Robert Pancoast (RobertPancoast77@gmail.com)
31  *
32  * These objects enable control of flight animation using a gamepad controller.
33  */
34 
35 #include "dock_gamepad.h"
36 
37 #include "ui_dock_gamepad.h"
38 
39 #include "src/automated_widgets.hpp"
40 #include "src/interface.hpp"
41 #include "src/rendered_image_widget.hpp"
42 #include "src/write_log.hpp"
43 
cDockGamepad(QWidget * parent)44 cDockGamepad::cDockGamepad(QWidget *parent) : QWidget(parent), ui(new Ui::cDockGamepad)
45 {
46 	ui->setupUi(this);
47 	automatedWidgets = new cAutomatedWidgets(this);
48 	automatedWidgets->ConnectSignalsForSlidersInWindow(this);
49 	ConnectSignals();
50 
51 #ifdef USE_GAMEPAD
52 	QList<int> deviceIds = QGamepadManager::instance()->connectedGamepads();
53 	if (deviceIds.size() > 0) gamepad.setDeviceId(deviceIds[0]);
54 	populateGamepadList();
55 #endif
56 }
57 
~cDockGamepad()58 cDockGamepad::~cDockGamepad()
59 {
60 	delete ui;
61 }
62 
ConnectSignals() const63 void cDockGamepad::ConnectSignals() const
64 {
65 #ifdef USE_GAMEPAD
66 	connect(ui->comboBox_gamepad_device, SIGNAL(currentIndexChanged(int)), this,
67 		SLOT(slotChangeGamepadIndex(int)));
68 
69 	// Left Joystick controls Look Angle
70 	connect(&gamepad, SIGNAL(axisLeftYChanged(double)), this, SLOT(slotGamepadLook()));
71 	connect(&gamepad, SIGNAL(axisLeftXChanged(double)), this, SLOT(slotGamepadLook()));
72 
73 	// Right Joystick controls Movement Direction
74 	connect(&gamepad, SIGNAL(axisRightXChanged(double)), this, SLOT(slotGamepadMove()));
75 	connect(&gamepad, SIGNAL(axisRightYChanged(double)), this, SLOT(slotGamepadMove()));
76 
77 	// Left and Right Triggers control Reverse and Accelerator
78 	connect(&gamepad, SIGNAL(buttonL2Changed(double)), this, SLOT(slotGamepadMove()));
79 	connect(&gamepad, SIGNAL(buttonR2Changed(double)), this, SLOT(slotGamepadMove()));
80 
81 	// Start Button will pause the flight
82 	connect(&gamepad, SIGNAL(buttonStartChanged(bool)), this, SLOT(slotGamepadPause(bool)));
83 
84 	// Left and Right Shoulder Buttons control Roll Rotation
85 	connect(&gamepad, SIGNAL(buttonL1Changed(bool)), this, SLOT(slotGamepadRoll()));
86 	connect(&gamepad, SIGNAL(buttonR1Changed(bool)), this, SLOT(slotGamepadRoll()));
87 
88 	// A and B buttons control the Movement Speed
89 	connect(&gamepad, SIGNAL(buttonAChanged(bool)), this, SLOT(slotGamepadSpeed()));
90 	connect(&gamepad, SIGNAL(buttonBChanged(bool)), this, SLOT(slotGamepadSpeed()));
91 
92 	connect(QGamepadManager::instance(), SIGNAL(gamepadConnected(int)), this,
93 		SLOT(slotGamePadDeviceChanged()));
94 	connect(QGamepadManager::instance(), SIGNAL(gamepadDisconnected(int)), this,
95 		SLOT(slotGamePadDeviceChanged()));
96 #endif // USE_GAMEPAD
97 }
98 
99 #ifdef USE_GAMEPAD
slotChangeGamepadIndex(int index)100 void cDockGamepad::slotChangeGamepadIndex(int index)
101 {
102 	QList<int> deviceIds = QGamepadManager::instance()->connectedGamepads();
103 	if (index < deviceIds.size() && index >= 0)
104 	{
105 		gamepad.setDeviceId(deviceIds[index]);
106 		WriteLog("Gamepad - slotChangeGamepadIndex: " + QString::number(index), 2);
107 	}
108 }
109 
slotGamePadDeviceChanged() const110 void cDockGamepad::slotGamePadDeviceChanged() const
111 {
112 	populateGamepadList();
113 }
114 
populateGamepadList() const115 void cDockGamepad::populateGamepadList() const
116 {
117 	ui->comboBox_gamepad_device->clear();
118 	QList<int> deviceIds = QGamepadManager::instance()->connectedGamepads();
119 
120 	for (int i = 0; i < deviceIds.size(); i++)
121 	{
122 		QGamepad gp(deviceIds[i]);
123 		QString deviceName = gp.name();
124 		if (deviceName == "") deviceName = "Device #" + QString::number(i);
125 		WriteLog("Gamepad - device list changed", 2);
126 		ui->comboBox_gamepad_device->addItem(deviceName, i);
127 	}
128 	if (deviceIds.size() == 0)
129 	{
130 		ui->comboBox_gamepad_device->setEnabled(false);
131 		ui->label_gamepad_no_device->show();
132 	}
133 	else
134 	{
135 		ui->comboBox_gamepad_device->setEnabled(true);
136 		ui->label_gamepad_no_device->hide();
137 	}
138 }
139 
slotGamepadLook() const140 void cDockGamepad::slotGamepadLook() const
141 {
142 	if (ui->groupCheck_gamepad_enabled->isChecked())
143 	{
144 		// Joystick Axis values vary from -1 to 0 to 1
145 		const double pitch = gamepad.axisLeftX();
146 		const double yaw = gamepad.axisLeftY();
147 		WriteLog("Gamepad - slotGamepadLook-Yaw | value: " + QString::number(yaw), 3);
148 		WriteLog("Gamepad - slotGamepadLook-Pitch | value: " + QString::number(pitch), 3);
149 		ui->sl_gamepad_angle_yaw->setValue(int(100 * yaw));
150 		ui->sl_gamepad_angle_pitch->setValue(int(100 * pitch));
151 		const double sensitivity = 0.5;
152 		CVector2<double> yawPitch(pitch, yaw);
153 		yawPitch = yawPitch.Deadband() * sensitivity;
154 		emit gMainInterface->renderedImage->YawAndPitchChanged(yawPitch);
155 	}
156 }
157 
slotGamepadMove() const158 void cDockGamepad::slotGamepadMove() const
159 {
160 	if (ui->groupCheck_gamepad_enabled->isChecked())
161 	{
162 		// Joystick Axis values vary from -1 to 0 to 1
163 		// -1 for down, and 1 for up, 0 for neutral
164 		// Invert the Y Axis for right Joystick
165 		const double x = gamepad.axisRightX();
166 		const double y = gamepad.axisRightY() * -1.0;
167 		CVector2<double> strafe(x, y);
168 		double sensitivity = 5.0;
169 		strafe = strafe.Deadband() * sensitivity;
170 		const bool joystick = fabs(strafe.x) > 0 || fabs(strafe.y) > 0;
171 		// Trigger values vary from 0 to 1
172 		const double reverse = gamepad.buttonL2();
173 		const double forward = gamepad.buttonR2();
174 		double z = forward - reverse / 2.0;
175 		const bool trigger = fabs(z) > 0;
176 		WriteLog("Gamepad - slotGamepadMove-X | value: " + QString::number(x), 3);
177 		WriteLog("Gamepad - slotGamepadMove-Y | value: " + QString::number(y), 3);
178 		WriteLog("Gamepad - slotGamepadMove-Z | value: " + QString::number(z), 3);
179 		ui->sl_gamepad_movement_x->setValue(int(100 * x));
180 		ui->sl_gamepad_movement_y->setValue(int(100 * y));
181 		ui->sl_gamepad_movement_z->setValue(int(100 * z));
182 		// Maintain z-axis speed
183 		sensitivity = 1 / 10.0;
184 		const double threshold = .001;
185 		z = z * sensitivity;
186 		// Forward Accelerate [threshold to 1 / sensitivity]
187 		if (fabs(z) < threshold && z >= 0.0) z = threshold;
188 		// Reverse Backwards [-threshold to -1 / sensitivity]
189 		if (fabs(z) < threshold && z <= 0.0) z = -threshold;
190 		if (joystick) emit gMainInterface->renderedImage->StrafeChanged(strafe / 2.0);
191 		if (trigger) emit gMainInterface->renderedImage->SpeedSet(z / 2.0);
192 	}
193 }
194 
slotGamepadPause(bool value)195 void cDockGamepad::slotGamepadPause(bool value)
196 {
197 	if (ui->groupCheck_gamepad_enabled->isChecked())
198 	{
199 		if (!value)
200 		{
201 			WriteLog("Gamepad - slotGamepadPause | activated", 3);
202 			emit gMainInterface->renderedImage->Pause();
203 		}
204 	}
205 }
206 
slotGamepadRoll() const207 void cDockGamepad::slotGamepadRoll() const
208 {
209 	if (ui->groupCheck_gamepad_enabled->isChecked())
210 	{
211 		// Button values are either false to true
212 		double value = 0;
213 		if (gamepad.buttonL1()) value += 1;
214 		if (gamepad.buttonR1()) value -= 1;
215 		WriteLog("Gamepad - slotGamepadRoll | value: " + QString::number(value), 3);
216 		ui->sl_gamepad_angle_roll->setValue(int(100 * value));
217 		emit gMainInterface->renderedImage->RotationChanged(value);
218 	}
219 }
220 
slotGamepadSpeed() const221 void cDockGamepad::slotGamepadSpeed() const
222 {
223 	if (ui->groupCheck_gamepad_enabled->isChecked())
224 	{
225 		// Button values are either false to true
226 		double value = 0;
227 		if (gamepad.buttonA()) value += 1;
228 		if (gamepad.buttonB()) value -= 1;
229 		WriteLog("Gamepad - slotGamepadSpeed | value: " + QString::number(value), 3);
230 		ui->sl_gamepad_movement_z->setValue(int(100 * value));
231 		if (gamepad.buttonA() != gamepad.buttonB())
232 		{
233 			if (gamepad.buttonA())
234 			{
235 				emit gMainInterface->renderedImage->SpeedChanged(1.1);
236 			}
237 			else
238 			{
239 				emit gMainInterface->renderedImage->SpeedChanged(0.9);
240 			}
241 		}
242 	}
243 }
244 
245 #endif // USE_GAMEPAD
246