1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
// SPDX-License-Identifier: BSD-2-Clause
// Copyright (c) 2025 Kirill Dmitrievich
// File: player_look.cc
// Description: Client camera rotations
#include "client/pch.hh"
#include "client/entity/player_look.hh"
#include "core/config/boolean.hh"
#include "core/config/number.hh"
#include "core/io/config_map.hh"
#include "core/math/angles.hh"
#include "shared/entity/head.hh"
#include "shared/world/dimension.hh"
#include "client/config/gamepad_axis.hh"
#include "client/config/gamepad_button.hh"
#include "client/config/keybind.hh"
#include "client/gui/settings.hh"
#include "client/io/gamepad.hh"
#include "client/io/glfw.hh"
#include "client/const.hh"
#include "client/globals.hh"
#include "client/session.hh"
constexpr static float PITCH_MIN = -1.0f * math::radians(90.0f);
constexpr static float PITCH_MAX = +1.0f * math::radians(90.0f);
// Mouse options
static config::Boolean mouse_raw_input(true);
static config::Unsigned mouse_sensitivity(25U, 1U, 100U);
// Gamepad options
static config::Float gamepad_fastlook_factor(1.5f, 1.0f, 5.0f);
static config::Unsigned gamepad_accel_pitch(15U, 1U, 100U);
static config::Unsigned gamepad_accel_yaw(25U, 1U, 100U);
// Gamepad axes
static config::GamepadAxis axis_pitch(GLFW_GAMEPAD_AXIS_LEFT_Y, false);
static config::GamepadAxis axis_yaw(GLFW_GAMEPAD_AXIS_LEFT_X, false);
// Gamepad buttons
static config::GamepadButton button_fastlook(GLFW_GAMEPAD_BUTTON_LEFT_THUMB);
static bool fastlook_enabled;
static glm::fvec2 last_cursor;
static void add_angles(float pitch, float yaw)
{
if(session::is_ingame()) {
auto& head = globals::dimension->entities.get<Head>(globals::player);
head.angles[0] += pitch;
head.angles[1] += yaw;
head.angles[0] = glm::clamp(head.angles[0], PITCH_MIN, PITCH_MAX);
head.angles = math::wrap_180(head.angles);
// Client-side head angles are not interpolated;
// Re-assigning the previous state after the current
// state has been already modified is certainly a way
// to circumvent the interpolation applied to anything with a head
globals::dimension->entities.emplace_or_replace<client::HeadPrev>(globals::player, head);
}
}
static void on_glfw_cursor_pos(const GlfwCursorPosEvent& event)
{
if(gamepad::available && gamepad::active.get_value()) {
// The player is assumed to be using
// a gamepad instead of mouse and keyboard
last_cursor = event.pos;
return;
}
if(globals::gui_screen || !session::is_ingame()) {
// UI is visible or we're not in-game
last_cursor = event.pos;
return;
}
auto dx = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * math::radians(event.pos.x - last_cursor.x);
auto dy = -0.01f * static_cast<float>(mouse_sensitivity.get_value()) * math::radians(event.pos.y - last_cursor.y);
add_angles(dy, dx);
last_cursor = event.pos;
}
static void on_gamepad_button(const GamepadButtonEvent& event)
{
if(button_fastlook.equals(event.button)) {
fastlook_enabled = event.action == GLFW_PRESS;
}
}
void player_look::init(void)
{
globals::client_config.add_value("player_look.mouse.raw_input", mouse_raw_input);
globals::client_config.add_value("player_look.mouse.sensitivity", mouse_sensitivity);
globals::client_config.add_value("player_look.gamepad.fastlook_factor", gamepad_fastlook_factor);
globals::client_config.add_value("player_look.gamepad.accel_pitch", gamepad_accel_pitch);
globals::client_config.add_value("player_look.gamepad.accel_yaw", gamepad_accel_yaw);
globals::client_config.add_value("player_look.gp_axis.pitch", axis_pitch);
globals::client_config.add_value("player_look.gp_axis.yaw", axis_yaw);
globals::client_config.add_value("player_look.gp_button.fastlook", button_fastlook);
settings::add_slider(0, mouse_sensitivity, settings_location::MOUSE, "player_look.mouse.sensitivity", true);
settings::add_checkbox(1, mouse_raw_input, settings_location::MOUSE, "player_look.mouse.raw_input", true);
settings::add_slider(0, gamepad_accel_pitch, settings_location::GAMEPAD_GAMEPLAY, "player_look.gamepad.accel_pitch", false);
settings::add_slider(1, gamepad_accel_yaw, settings_location::GAMEPAD_GAMEPLAY, "player_look.gamepad.accel_yaw", false);
settings::add_gamepad_axis(2, axis_pitch, settings_location::GAMEPAD_GAMEPLAY, "player_look.gp_axis.pitch");
settings::add_gamepad_axis(3, axis_yaw, settings_location::GAMEPAD_GAMEPLAY, "player_look.gp_axis.yaw");
settings::add_slider(4, gamepad_fastlook_factor, settings_location::GAMEPAD_GAMEPLAY, "player_look.gamepad.fastlook_factor", true,
"%.02f");
settings::add_gamepad_button(5, button_fastlook, settings_location::GAMEPAD_GAMEPLAY, "player_look.gp_button.fastlook");
fastlook_enabled = false;
last_cursor.x = 0.5f * static_cast<float>(globals::width);
last_cursor.y = 0.5f * static_cast<float>(globals::height);
globals::dispatcher.sink<GlfwCursorPosEvent>().connect<&on_glfw_cursor_pos>();
globals::dispatcher.sink<GamepadButtonEvent>().connect<&on_gamepad_button>();
}
void player_look::update_late(void)
{
if(gamepad::available && gamepad::active.get_value() && !globals::gui_screen) {
auto pitch_value = axis_pitch.get_value(gamepad::state, gamepad::deadzone.get_value());
auto yaw_value = axis_yaw.get_value(gamepad::state, gamepad::deadzone.get_value());
if(fastlook_enabled) {
// Fastlook allows the camera to
// rotate quicker when a button is held down
pitch_value *= gamepad_fastlook_factor.get_value();
yaw_value *= gamepad_fastlook_factor.get_value();
}
pitch_value *= 0.001f * static_cast<float>(gamepad_accel_pitch.get_value());
yaw_value *= 0.001f * static_cast<float>(gamepad_accel_yaw.get_value());
add_angles(pitch_value, yaw_value);
}
if(!globals::gui_screen && session::is_ingame()) {
glfwSetInputMode(globals::window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetInputMode(globals::window, GLFW_RAW_MOUSE_MOTION, mouse_raw_input.get_value());
}
else {
glfwSetInputMode(globals::window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetInputMode(globals::window, GLFW_RAW_MOUSE_MOTION, false);
}
}
|