Skip to content

Commit b7f6022

Browse files
authored
Merge pull request #50 from tgfrerer/feature-gamepad
merge Feature gamepad
2 parents 6d49abf + 34d0c56 commit b7f6022

File tree

9 files changed

+431
-59
lines changed

9 files changed

+431
-59
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ can build a single, statically linked and optimised binary.
8282
reverse-ssh or similar to forward localhost::3535 and you can
8383
remotely connect to a running app from all over the world.
8484

85+
* **Gamepad** support: the default camera can be steered with
86+
a gamepad-just connect your gamepad and you are set; application
87+
windows can decide whether they want to subscribe to gamepad events
88+
- and to which gamepads to subscribe to.
89+
8590
* **Multi-Window** Island allows you to hook up multiple swapchains to
8691
a single application. You can dynamically add and remove swapchains
8792
while your Island application is running. This is particularly useful

modules/le_backend_vk/le_backend_vk.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,7 @@ static bool backend_poll_frame_fence( le_backend_o* self, size_t frameIndex ) {
20002000
logger.error( "Poll Frame Fence returned: %s", to_str_vk_result( result ) );
20012001
exit( 1 );
20022002
} else if ( result != VK_SUCCESS ) {
2003-
logger.error( "Poll Frame Fence returned: %s", to_str_vk_result( result ) );
2003+
logger.warn( "Poll Frame Fence returned: %s", to_str_vk_result( result ) );
20042004
return false;
20052005
} else {
20062006
return true;

modules/le_camera/le_camera.cpp

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "le_camera.h"
22
#include "le_core.h"
33

4+
#include "modules/le_log/le_log.h"
45
#include "private/le_renderer/le_renderer_types.h" // for le::Viewport
56
#include "le_ui_event.h"
67

@@ -92,7 +93,7 @@ static void update_frustum_planes( le_camera_o* self ) {
9293
float fPL[ 6 ]{};
9394
for ( size_t i = 0; i != 6; i++ ) {
9495
// get the length (= magnitude of the .xyz part of the row), so that we can normalize later
95-
fPL[ i ] = glm::vec3( fP[ i ].x, fP[ i ].y, fP[ i ].z ).length();
96+
fPL[ i ] = glm::distance(glm::vec3( fP[ i ].x, fP[ i ].y, fP[ i ].z ), glm::vec3(0));
9697
}
9798

9899
for ( size_t i = 0; i < 6; i++ ) {
@@ -289,6 +290,19 @@ void camera_translate_xy( le_camera_o* camera, glm::mat4 const& world_to_cam_sta
289290
camera->view_matrix = glm::inverse( world_to_cam );
290291
}
291292

293+
// ----------------------------------------------------------------------
294+
void camera_translate_xyz( le_camera_o* camera, glm::mat4 const& world_to_cam_start, glm::vec3 const& signedNorm, float movement_speed, float pivotDistance ) {
295+
296+
float distance_to_origin = glm::distance( glm::vec4{ 0, 0, 0, 1 }, world_to_cam_start * glm::vec4( 0, 0, 0, 1 ) );
297+
movement_speed *= distance_to_origin;
298+
299+
auto pivot = glm::translate( world_to_cam_start, glm::vec3{ 0, 0, -pivotDistance } );
300+
pivot = glm::translate( pivot, movement_speed * glm::vec3{ signedNorm.x, signedNorm.y, signedNorm.z } );
301+
auto world_to_cam = glm::translate( pivot, glm::vec3{ 0, 0, pivotDistance } );
302+
303+
camera->view_matrix = glm::inverse( world_to_cam );
304+
}
305+
292306
// ----------------------------------------------------------------------
293307

294308
void camera_translate_z( le_camera_o* camera, glm::mat4 const& world_to_cam_start, glm::vec3 const& signedNorm, float movement_speed, float pivotDistance ) {
@@ -326,6 +340,8 @@ static void camera_controller_update_camera( le_camera_controller_o* controller,
326340
}
327341

328342
for ( auto const& event : events ) {
343+
glm::vec3 translationDelta{};
344+
glm::vec3 rotationDelta{};
329345

330346
// -- accumulate mouse state
331347

@@ -355,21 +371,56 @@ static void camera_controller_update_camera( le_camera_controller_o* controller,
355371
if ( e.action == LeUiEvent::ButtonAction::ePress ) {
356372
// set appropriate button flag
357373
mouse_state.buttonState |= ( 1 << e.button );
358-
359374
} else if ( e.action == LeUiEvent::ButtonAction::eRelease ) {
360375
// null appropriate button flag
361376
mouse_state.buttonState &= ~( 1 << e.button );
362377
// set camera controller into neutral state if any button was released.
363378
controller->mode = le_camera_controller_o::eNeutral;
364379
}
365380
} break;
381+
case ( LeUiEvent::Type::eGamepad ): {
382+
auto& e = event->gamepad;
383+
384+
// We must make sure that axes are not drifting ... if any of the values are below axes_epsilon, they shall be zero.
385+
// Therefore, we ignore any values that are within the +- drift range; we then remap the range so that we still cover -1..1
386+
auto remove_drift = []( glm::vec3 const& vec_input, glm::vec3 const& drift_tolerance = glm::vec3( 0.1f ) ) -> glm::vec3 {
387+
glm::vec3 abs_val = glm::abs( vec_input );
388+
glm::vec3 sign_val = glm::sign( vec_input );
389+
390+
glm::vec3 test_val = abs_val - drift_tolerance;
391+
test_val = max( glm::vec3( 0.f ), test_val );
392+
test_val = test_val * sign_val;
393+
// todo: map back to original range.
394+
return test_val / ( glm::vec3( 1.f ) - drift_tolerance );
395+
};
396+
397+
glm::vec3 gamepad_x_y_z = {
398+
e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eLeftX ) ],
399+
e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eLeftY ) ],
400+
( ( e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eLeftTrigger ) ] + 1 ) -
401+
( e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eRightTrigger ) ] + 1 ) ),
402+
};
403+
404+
translationDelta += 0.01f * remove_drift( gamepad_x_y_z, glm::vec3( 0.1 ) );
405+
;
406+
407+
glm::vec3 gamepad_rot = {
408+
e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eRightX ) ],
409+
e.axes[ uint32_t( le::UiEvent::NamedGamepadAxis::eRightY ) ],
410+
0.f };
411+
412+
gamepad_rot = remove_drift( gamepad_rot, glm::vec3( 0.1 ) );
413+
414+
rotationDelta.x = glm::two_pi<float>() * -0.0025f * gamepad_rot.x;
415+
rotationDelta.y = glm::two_pi<float>() * 0.0025f * gamepad_rot.y;
416+
417+
break;
418+
}
366419
default:
367420
break;
368421
}
369422

370-
glm::vec3 rotationDelta;
371-
glm::vec3 translationDelta;
372-
{
423+
if ( mouse_state.buttonState ) {
373424
auto mouseInitial = controller->mouse_pos_initial - controlRectCentre;
374425
float mouseInitialAngle = glm::two_pi<float>() - fmodf( glm::two_pi<float>() + atan2f( mouseInitial.y, mouseInitial.x ), glm::two_pi<float>() ); // Range is expected to be 0..2pi, ccw
375426

@@ -389,6 +440,18 @@ static void camera_controller_update_camera( le_camera_controller_o* controller,
389440
switch ( controller->mode ) {
390441
case le_camera_controller_o::eNeutral: {
391442

443+
if ( event->event == le::UiEvent::Type::eGamepad ) {
444+
445+
float movement_speed = 0.5;
446+
static auto logger = le::Log( "le_camera" );
447+
controller->world_to_cam = glm::inverse( camera->view_matrix );
448+
camera_translate_xyz( camera, controller->world_to_cam, translationDelta, movement_speed, controller->pivotDistance );
449+
controller->world_to_cam = glm::inverse( camera->view_matrix );
450+
camera_orbit_xy( camera, controller->world_to_cam, rotationDelta, controller->pivotDistance );
451+
controller->world_to_cam = glm::inverse( camera->view_matrix );
452+
continue;
453+
}
454+
392455
if ( false == is_inside_rect( mouse_state.cursor_pos, controller->controlRect ) ) {
393456
// if camera is outside the control rect, we don't care.
394457
continue;
@@ -458,7 +521,10 @@ static void camera_controller_process_events( le_camera_controller_o* controller
458521
filtered_events.reserve( numEvents );
459522

460523
for ( auto event = events; event != events_end; event++ ) {
461-
if ( event->event == LeUiEvent::Type::eCursorPosition || event->event == LeUiEvent::Type::eMouseButton || event->event == LeUiEvent::Type::eKey ) {
524+
if ( event->event == LeUiEvent::Type::eCursorPosition ||
525+
event->event == LeUiEvent::Type::eMouseButton ||
526+
event->event == LeUiEvent::Type::eKey ||
527+
event->event == LeUiEvent::Type::eGamepad ) {
462528
filtered_events.emplace_back( event );
463529
}
464530
}
@@ -510,7 +576,8 @@ static void camera_controller_set_pivot_distance( le_camera_controller_o* self,
510576
// ----------------------------------------------------------------------
511577

512578
LE_MODULE_REGISTER_IMPL( le_camera, api ) {
513-
auto& le_camera_i = static_cast<le_camera_api*>( api )->le_camera_i;
579+
auto api_i = static_cast<le_camera_api*>( api );
580+
auto& le_camera_i = api_i->le_camera_i;
514581

515582
le_camera_i.create = le_camera_create;
516583
le_camera_i.destroy = le_camera_destroy;
@@ -529,7 +596,7 @@ LE_MODULE_REGISTER_IMPL( le_camera, api ) {
529596
le_camera_i.get_sphere_in_frustum = camera_get_sphere_in_frustum;
530597
le_camera_i.set_is_orthographic = camera_set_is_orthographic;
531598

532-
auto& le_camera_controller_i = static_cast<le_camera_api*>( api )->le_camera_controller_i;
599+
auto& le_camera_controller_i = api_i->le_camera_controller_i;
533600

534601
le_camera_controller_i.create = camera_controller_create;
535602
le_camera_controller_i.destroy = camera_controller_destroy;

modules/le_file_watcher/le_tweakable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
# include "le_core.h"
3535

3636
# include "le_file_watcher.h"
37-
# include "le_log.h"
37+
# include "le_log.h" ///< if you get an error message saying that this header can't be found, make sure that the module that uses le_tweakable has a line saying `depends_on_island_module(le_log)` in its CMake file.
3838

3939
# include <fstream>
4040
# include <iostream>

modules/le_mesh/le_mesh.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ struct le_mesh_api {
1515

1616
void (*clear)(le_mesh_o* self);
1717

18-
void (*get_vertices )( le_mesh_o *self, size_t& count, float const ** vertices);
19-
void (*get_normals )( le_mesh_o *self, size_t& count, float const ** normals );
20-
void (*get_colours )( le_mesh_o *self, size_t& count, float const ** colours );
21-
void (*get_uvs )( le_mesh_o *self, size_t& count, float const ** uvs );
22-
void (*get_tangents )( le_mesh_o *self, size_t& count, float const ** tangents);
23-
void (*get_indices )( le_mesh_o *self, size_t& count, uint16_t const ** indices );
18+
void (*get_vertices )( le_mesh_o *self, size_t& count, float const ** vertices); // 3 floats per vertex
19+
void (*get_normals )( le_mesh_o *self, size_t& count, float const ** normals ); // 3 floats per vertex
20+
void (*get_colours )( le_mesh_o *self, size_t& count, float const ** colours ); // 4 floats per vertex
21+
void (*get_uvs )( le_mesh_o *self, size_t& count, float const ** uvs ); // 3 floats per vertex
22+
void (*get_tangents )( le_mesh_o *self, size_t& count, float const ** tangents); // 3 floats per vertex
23+
void (*get_indices )( le_mesh_o *self, size_t& count, uint16_t const ** indices ); // 1 uint16_t per index
2424

2525
void (*get_data )( le_mesh_o *self, size_t& numVertices, size_t& numIndices, float const** vertices, float const **normals, float const **uvs, float const ** colours, uint16_t const **indices);
2626

modules/le_ui_event/le_ui_event.h

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
#pragma once
22
#include <stdint.h>
33

4-
// Todo: move this to a frame-work wide internal header file.
5-
64
struct LeUiEvent {
5+
enum class NamedGamepadButton : uint32_t {
6+
eA = 0,
7+
eB = 1,
8+
eX = 2,
9+
eY = 3,
10+
eLeftBumper = 4,
11+
eRightBumper = 5,
12+
eBack = 6,
13+
eStart = 7,
14+
eGuide = 8,
15+
eLeftThumb = 9,
16+
eRightThumb = 10,
17+
eDpadUp = 11,
18+
eDpadRight = 12,
19+
eDpadDown = 13,
20+
eDpadLeft = 14,
21+
//
22+
eLast = eDpadLeft,
23+
//
24+
eCross = eA,
25+
eCircle = eB,
26+
eSquare = eX,
27+
eTriangle = eY,
28+
};
29+
30+
enum class NamedGamepadAxis : uint32_t {
31+
eLeftX = 0,
32+
eLeftY = 1,
33+
eRightX = 2,
34+
eRightY = 3,
35+
eLeftTrigger = 4,
36+
eRightTrigger = 5,
37+
eLast = eRightTrigger,
38+
};
739

840
enum class NamedKey : int32_t {
941
eUnknown = -1,
@@ -138,13 +170,15 @@ struct LeUiEvent {
138170
};
139171

140172
enum class Type : uint32_t {
141-
eKey = 1 << 0,
142-
eCharacter = 1 << 1,
143-
eCursorPosition = 1 << 2,
144-
eCursorEnter = 1 << 3,
145-
eMouseButton = 1 << 4,
146-
eScroll = 1 << 5,
147-
eDrop = 1 << 6,
173+
eUnknown = 0,
174+
eKey,
175+
eCharacter,
176+
eCursorPosition,
177+
eCursorEnter,
178+
eMouseButton,
179+
eScroll,
180+
eDrop,
181+
eGamepad,
148182
};
149183

150184
struct KeyEvent {
@@ -183,7 +217,29 @@ struct LeUiEvent {
183217
uint64_t paths_count;
184218
};
185219

186-
Type event;
220+
struct GamepadEvent {
221+
float axes[ 6 ]; // -1 to 1 (inclusive for each axis)
222+
uint16_t buttons; // [0] : 0..14 bitset, 0 is least significant bit
223+
uint16_t gamepad_id; // 0..15
224+
225+
bool get_button_at( uint8_t index ) const noexcept {
226+
return index < 15 ? ( buttons & ( uint16_t( 1 ) << index ) ) : false;
227+
}
228+
229+
bool operator==( GamepadEvent const& rhs ) {
230+
return axes[ 0 ] == rhs.axes[ 0 ] &&
231+
axes[ 1 ] == rhs.axes[ 1 ] &&
232+
axes[ 2 ] == rhs.axes[ 2 ] &&
233+
axes[ 3 ] == rhs.axes[ 3 ] &&
234+
axes[ 4 ] == rhs.axes[ 4 ] &&
235+
axes[ 5 ] == rhs.axes[ 5 ] &&
236+
buttons == rhs.buttons;
237+
}
238+
239+
bool operator!=( GamepadEvent const& rhs ) {
240+
return !( *this == rhs );
241+
}
242+
};
187243

188244
union {
189245
KeyEvent key;
@@ -193,7 +249,10 @@ struct LeUiEvent {
193249
MouseButtonEvent mouseButton;
194250
ScrollEvent scroll;
195251
DropEvent drop;
252+
GamepadEvent gamepad;
196253
};
254+
255+
Type event;
197256
};
198257

199258
namespace le {

0 commit comments

Comments
 (0)