1 /** Example 019 Mouse and Joystick
2 
3 This tutorial builds on example 04.Movement which showed how to
4 handle keyboard events in Irrlicht.  Here we'll handle mouse events
5 and joystick events, if you have a joystick connected and a device
6 that supports joysticks.  These are currently Windows, Linux and SDL
7 devices.
8 */
9 
10 #ifdef _MSC_VER
11 // We'll define this to stop MSVC complaining about sprintf().
12 #define _CRT_SECURE_NO_WARNINGS
13 #pragma comment(lib, "Irrlicht.lib")
14 #endif
15 
16 #include <irrlicht.h>
17 #include "driverChoice.h"
18 
19 using namespace irr;
20 
21 /*
22 Just as we did in example 04.Movement, we'll store the latest state of the
23 mouse and the first joystick, updating them as we receive events.
24 */
25 class MyEventReceiver : public IEventReceiver
26 {
27 public:
28 	// We'll create a struct to record info on the mouse state
29 	struct SMouseState
30 	{
31 		core::position2di Position;
32 		bool LeftButtonDown;
SMouseStateMyEventReceiver::SMouseState33 		SMouseState() : LeftButtonDown(false) { }
34 	} MouseState;
35 
36 	// This is the one method that we have to implement
OnEvent(const SEvent & event)37 	virtual bool OnEvent(const SEvent& event)
38 	{
39 		// Remember the mouse state
40 		if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
41 		{
42 			switch(event.MouseInput.Event)
43 			{
44 			case EMIE_LMOUSE_PRESSED_DOWN:
45 				MouseState.LeftButtonDown = true;
46 				break;
47 
48 			case EMIE_LMOUSE_LEFT_UP:
49 				MouseState.LeftButtonDown = false;
50 				break;
51 
52 			case EMIE_MOUSE_MOVED:
53 				MouseState.Position.X = event.MouseInput.X;
54 				MouseState.Position.Y = event.MouseInput.Y;
55 				break;
56 
57 			default:
58 				// We won't use the wheel
59 				break;
60 			}
61 		}
62 
63 		// The state of each connected joystick is sent to us
64 		// once every run() of the Irrlicht device.  Store the
65 		// state of the first joystick, ignoring other joysticks.
66 		// This is currently only supported on Windows and Linux.
67 		if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT
68 			&& event.JoystickEvent.Joystick == 0)
69 		{
70 			JoystickState = event.JoystickEvent;
71 		}
72 
73 		return false;
74 	}
75 
GetJoystickState(void) const76 	const SEvent::SJoystickEvent & GetJoystickState(void) const
77 	{
78 		return JoystickState;
79 	}
80 
GetMouseState(void) const81 	const SMouseState & GetMouseState(void) const
82 	{
83 		return MouseState;
84 	}
85 
86 
MyEventReceiver()87 	MyEventReceiver()
88 	{
89 	}
90 
91 private:
92 	SEvent::SJoystickEvent JoystickState;
93 };
94 
95 
96 /*
97 The event receiver for keeping the pressed keys is ready, the actual responses
98 will be made inside the render loop, right before drawing the scene. So lets
99 just create an irr::IrrlichtDevice and the scene node we want to move. We also
100 create some other additional scene nodes, to show that there are also some
101 different possibilities to move and animate scene nodes.
102 */
main()103 int main()
104 {
105 	// ask user for driver
106 	video::E_DRIVER_TYPE driverType=driverChoiceConsole();
107 	if (driverType==video::EDT_COUNT)
108 		return 1;
109 
110 	// create device
111 	MyEventReceiver receiver;
112 
113 	IrrlichtDevice* device = createDevice(driverType,
114 			core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
115 
116 	if (device == 0)
117 		return 1; // could not create selected driver.
118 
119 
120 	core::array<SJoystickInfo> joystickInfo;
121 	if(device->activateJoysticks(joystickInfo))
122 	{
123 		std::cout << "Joystick support is enabled and " << joystickInfo.size() << " joystick(s) are present." << std::endl;
124 
125 		for(u32 joystick = 0; joystick < joystickInfo.size(); ++joystick)
126 		{
127 			std::cout << "Joystick " << joystick << ":" << std::endl;
128 			std::cout << "\tName: '" << joystickInfo[joystick].Name.c_str() << "'" << std::endl;
129 			std::cout << "\tAxes: " << joystickInfo[joystick].Axes << std::endl;
130 			std::cout << "\tButtons: " << joystickInfo[joystick].Buttons << std::endl;
131 
132 			std::cout << "\tHat is: ";
133 
134 			switch(joystickInfo[joystick].PovHat)
135 			{
136 			case SJoystickInfo::POV_HAT_PRESENT:
137 				std::cout << "present" << std::endl;
138 				break;
139 
140 			case SJoystickInfo::POV_HAT_ABSENT:
141 				std::cout << "absent" << std::endl;
142 				break;
143 
144 			case SJoystickInfo::POV_HAT_UNKNOWN:
145 			default:
146 				std::cout << "unknown" << std::endl;
147 				break;
148 			}
149 		}
150 	}
151 	else
152 	{
153 		std::cout << "Joystick support is not enabled." << std::endl;
154 	}
155 
156 	core::stringw tmp = L"Irrlicht Joystick Example (";
157 	tmp += joystickInfo.size();
158 	tmp += " joysticks)";
159 	device->setWindowCaption(tmp.c_str());
160 
161 	video::IVideoDriver* driver = device->getVideoDriver();
162 	scene::ISceneManager* smgr = device->getSceneManager();
163 
164 	/*
165 	We'll create an arrow mesh and move it around either with the joystick axis/hat,
166 	or make it follow the mouse pointer. */
167 	scene::ISceneNode * node = smgr->addMeshSceneNode(
168 		smgr->addArrowMesh( "Arrow",
169 				video::SColor(255, 255, 0, 0),
170 				video::SColor(255, 0, 255, 0),
171 				16,16,
172 				2.f, 1.3f,
173 				0.1f, 0.6f
174 				)
175 		);
176 	node->setMaterialFlag(video::EMF_LIGHTING, false);
177 
178 	scene::ICameraSceneNode * camera = smgr->addCameraSceneNode();
179 	camera->setPosition(core::vector3df(0, 0, -10));
180 
181 	// As in example 04, we'll use framerate independent movement.
182 	u32 then = device->getTimer()->getTime();
183 	const f32 MOVEMENT_SPEED = 5.f;
184 
185 	while(device->run())
186 	{
187 		// Work out a frame delta time.
188 		const u32 now = device->getTimer()->getTime();
189 		const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
190 		then = now;
191 
192 		bool movedWithJoystick = false;
193 		core::vector3df nodePosition = node->getPosition();
194 
195 		if(joystickInfo.size() > 0)
196 		{
197 			f32 moveHorizontal = 0.f; // Range is -1.f for full left to +1.f for full right
198 			f32 moveVertical = 0.f; // -1.f for full down to +1.f for full up.
199 
200 			const SEvent::SJoystickEvent & joystickData = receiver.GetJoystickState();
201 
202 			// We receive the full analog range of the axes, and so have to implement our
203 			// own dead zone.  This is an empirical value, since some joysticks have more
204 			// jitter or creep around the center point than others.  We'll use 5% of the
205 			// range as the dead zone, but generally you would want to give the user the
206 			// option to change this.
207 			const f32 DEAD_ZONE = 0.05f;
208 
209 			moveHorizontal =
210 				(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_X] / 32767.f;
211 			if(fabs(moveHorizontal) < DEAD_ZONE)
212 				moveHorizontal = 0.f;
213 
214 			moveVertical =
215 				(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_Y] / -32767.f;
216 			if(fabs(moveVertical) < DEAD_ZONE)
217 				moveVertical = 0.f;
218 
219 			// POV hat info is only currently supported on Windows, but the value is
220 			// guaranteed to be 65535 if it's not supported, so we can check its range.
221 			const u16 povDegrees = joystickData.POV / 100;
222 			if(povDegrees < 360)
223 			{
224 				if(povDegrees > 0 && povDegrees < 180)
225 					moveHorizontal = 1.f;
226 				else if(povDegrees > 180)
227 					moveHorizontal = -1.f;
228 
229 				if(povDegrees > 90 && povDegrees < 270)
230 					moveVertical = -1.f;
231 				else if(povDegrees > 270 || povDegrees < 90)
232 					moveVertical = +1.f;
233 			}
234 
235 			if(!core::equals(moveHorizontal, 0.f) || !core::equals(moveVertical, 0.f))
236 			{
237 				nodePosition.X += MOVEMENT_SPEED * frameDeltaTime * moveHorizontal;
238 				nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime * moveVertical;
239 				movedWithJoystick = true;
240 			}
241 		}
242 
243 		// If the arrow node isn't being moved with the joystick, then have it follow the mouse cursor.
244 		if(!movedWithJoystick)
245 		{
246 			// Create a ray through the mouse cursor.
247 			core::line3df ray = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(
248 				receiver.GetMouseState().Position, camera);
249 
250 			// And intersect the ray with a plane around the node facing towards the camera.
251 			core::plane3df plane(nodePosition, core::vector3df(0, 0, -1));
252 			core::vector3df mousePosition;
253 			if(plane.getIntersectionWithLine(ray.start, ray.getVector(), mousePosition))
254 			{
255 				// We now have a mouse position in 3d space; move towards it.
256 				core::vector3df toMousePosition(mousePosition - nodePosition);
257 				const f32 availableMovement = MOVEMENT_SPEED * frameDeltaTime;
258 
259 				if(toMousePosition.getLength() <= availableMovement)
260 					nodePosition = mousePosition; // Jump to the final position
261 				else
262 					nodePosition += toMousePosition.normalize() * availableMovement; // Move towards it
263 			}
264 		}
265 
266 		node->setPosition(nodePosition);
267 
268 		// Turn lighting on and off depending on whether the left mouse button is down.
269 		node->setMaterialFlag(video::EMF_LIGHTING, receiver.GetMouseState().LeftButtonDown);
270 
271 		driver->beginScene(true, true, video::SColor(255,113,113,133));
272 		smgr->drawAll(); // draw the 3d scene
273 		driver->endScene();
274 	}
275 
276 	/*
277 	In the end, delete the Irrlicht device.
278 	*/
279 	device->drop();
280 
281 	return 0;
282 }
283 
284 /*
285 **/
286