mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-18 09:51:40 +00:00
[console] Implement mouse trackball camera control
I've always wanted this in QF :)
This commit is contained in:
parent
bf3d57cdbf
commit
8e0bfb1a7b
1 changed files with 131 additions and 29 deletions
|
@ -54,6 +54,16 @@ static in_axis_t deb_move_roll = {
|
||||||
.name = "debug.move.roll",
|
.name = "debug.move.roll",
|
||||||
.description = "[debug] Roll axis",
|
.description = "[debug] Roll axis",
|
||||||
};
|
};
|
||||||
|
static in_axis_t deb_mouse_x = {
|
||||||
|
.mode = ina_set,
|
||||||
|
.name = "debug.mouse.x",
|
||||||
|
.description = "[debug] Mouse X",
|
||||||
|
};
|
||||||
|
static in_axis_t deb_mouse_y = {
|
||||||
|
.mode = ina_set,
|
||||||
|
.name = "debug.mouse.y",
|
||||||
|
.description = "[debug] Mouse Y",
|
||||||
|
};
|
||||||
static in_button_t deb_left = {
|
static in_button_t deb_left = {
|
||||||
.name = "debug.left",
|
.name = "debug.left",
|
||||||
.description = "[debug] When active the player is turning left"
|
.description = "[debug] When active the player is turning left"
|
||||||
|
@ -101,6 +111,8 @@ static in_axis_t *deb_in_axes[] = {
|
||||||
&deb_move_pitch,
|
&deb_move_pitch,
|
||||||
&deb_move_yaw,
|
&deb_move_yaw,
|
||||||
&deb_move_roll,
|
&deb_move_roll,
|
||||||
|
&deb_mouse_x,
|
||||||
|
&deb_mouse_y,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
static in_button_t *deb_in_buttons[] = {
|
static in_button_t *deb_in_buttons[] = {
|
||||||
|
@ -159,6 +171,7 @@ static cvar_t deb_fontsize_cvar = {
|
||||||
|
|
||||||
static int deb_xlen = -1;
|
static int deb_xlen = -1;
|
||||||
static int deb_ylen = -1;
|
static int deb_ylen = -1;
|
||||||
|
static vec4f_t mouse_start;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
con_debug_f (void *data, const cvar_t *cvar)
|
con_debug_f (void *data, const cvar_t *cvar)
|
||||||
|
@ -177,7 +190,7 @@ con_debug_f (void *data, const cvar_t *cvar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
debug_app_window (const IE_event_t *ie_event)
|
debug_app_window (const IE_event_t *ie_event)
|
||||||
{
|
{
|
||||||
if (deb_xlen != ie_event->app_window.xlen
|
if (deb_xlen != ie_event->app_window.xlen
|
||||||
|
@ -186,12 +199,39 @@ debug_app_window (const IE_event_t *ie_event)
|
||||||
deb_ylen = ie_event->app_window.ylen;
|
deb_ylen = ie_event->app_window.ylen;
|
||||||
IMUI_SetSize (debug_imui, deb_xlen, deb_ylen);
|
IMUI_SetSize (debug_imui, deb_xlen, deb_ylen);
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
capture_mouse_event (const IE_event_t *ie_event)
|
||||||
|
{
|
||||||
|
static IE_mouse_event_t prev_mouse;
|
||||||
|
static bool dragging;
|
||||||
|
|
||||||
|
if (ie_event->mouse.type == ie_mousedown
|
||||||
|
&& ((ie_event->mouse.buttons ^ prev_mouse.buttons) & 4)) {
|
||||||
|
IN_UpdateGrab (1);
|
||||||
|
dragging = true;
|
||||||
|
mouse_start = (vec4f_t) {
|
||||||
|
ie_event->mouse.x,
|
||||||
|
ie_event->mouse.y,
|
||||||
|
};
|
||||||
|
} else if (ie_event->mouse.type == ie_mouseup
|
||||||
|
&& ((ie_event->mouse.buttons ^ prev_mouse.buttons) & 4)) {
|
||||||
|
IN_UpdateGrab (in_grab);
|
||||||
|
dragging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
prev_mouse = ie_event->mouse;
|
||||||
|
return !dragging;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
debug_mouse (const IE_event_t *ie_event)
|
debug_mouse (const IE_event_t *ie_event)
|
||||||
{
|
{
|
||||||
IMUI_ProcessEvent (debug_imui, ie_event);
|
if (!IMUI_ProcessEvent (debug_imui, ie_event)) {
|
||||||
|
return capture_mouse_event (ie_event);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -201,7 +241,7 @@ close_debug (void)
|
||||||
con_debug_f (0, &con_debug_cvar);
|
con_debug_f (0, &con_debug_cvar);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
debug_key (const IE_event_t *ie_event)
|
debug_key (const IE_event_t *ie_event)
|
||||||
{
|
{
|
||||||
int shift = ie_event->key.shift & ~(ies_capslock | ies_numlock);
|
int shift = ie_event->key.shift & ~(ies_capslock | ies_numlock);
|
||||||
|
@ -209,12 +249,13 @@ debug_key (const IE_event_t *ie_event)
|
||||||
close_debug ();
|
close_debug ();
|
||||||
}
|
}
|
||||||
IMUI_ProcessEvent (debug_imui, ie_event);
|
IMUI_ProcessEvent (debug_imui, ie_event);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
debug_event_handler (const IE_event_t *ie_event, void *data)
|
debug_event_handler (const IE_event_t *ie_event, void *data)
|
||||||
{
|
{
|
||||||
static void (*handlers[ie_event_count]) (const IE_event_t *ie_event) = {
|
static int (*handlers[ie_event_count]) (const IE_event_t *ie_event) = {
|
||||||
[ie_app_window] = debug_app_window,
|
[ie_app_window] = debug_app_window,
|
||||||
[ie_mouse] = debug_mouse,
|
[ie_mouse] = debug_mouse,
|
||||||
[ie_key] = debug_key,
|
[ie_key] = debug_key,
|
||||||
|
@ -223,8 +264,7 @@ debug_event_handler (const IE_event_t *ie_event, void *data)
|
||||||
|| !handlers[ie_event->type]) {
|
|| !handlers[ie_event->type]) {
|
||||||
return IN_Binding_HandleEvent (ie_event);
|
return IN_Binding_HandleEvent (ie_event);
|
||||||
}
|
}
|
||||||
handlers[ie_event->type] (ie_event);
|
return handlers[ie_event->type] (ie_event);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -377,29 +417,60 @@ camera_mouse_first_person (void)
|
||||||
Transform_SetLocalRotation (debug_camera_pivot, r);
|
Transform_SetLocalRotation (debug_camera_pivot, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __attribute__((used))
|
static vec4f_t
|
||||||
camera_mouse_trackball (const IE_event_t *ie_event)
|
trackball_vector (vec4f_t xy)
|
||||||
{
|
{
|
||||||
static IE_mouse_event_t prev_mouse;
|
// xy is already in -1..1 order of magnitude range (ie, might be a bit
|
||||||
static bool dragging;
|
// over due to screen aspect)
|
||||||
|
// This is very similar to blender's trackball calculation (based on it,
|
||||||
if (ie_event->mouse.type == ie_mousedown
|
// really)
|
||||||
&& ((ie_event->mouse.buttons ^ prev_mouse.buttons) & 4)) {
|
float r = 1;
|
||||||
puts ("rmb pressed");
|
float t = r * r / 2;
|
||||||
IN_UpdateGrab (1);
|
float d = dotf (xy, xy)[0];
|
||||||
dragging = true;
|
vec4f_t vec = xy;
|
||||||
} else if (ie_event->mouse.type == ie_mouseup
|
if (d < t) {
|
||||||
&& ((ie_event->mouse.buttons ^ prev_mouse.buttons) & 4)) {
|
// less than 45 degrees around the sphere from the viewer facing
|
||||||
puts ("rmb released");
|
// pole, so map the mouse point to the sphere
|
||||||
IN_UpdateGrab (in_grab);
|
vec[2] = sqrt (r * r - d);
|
||||||
dragging = false;
|
} else {
|
||||||
} else if (ie_event->mouse.type == ie_mousemove && dragging) {
|
// beyond 45 degrees around the sphere from the veiwer facing
|
||||||
int dx = ie_event->mouse.x - prev_mouse.x;
|
// pole, so the slope is rapidly approaching infinity or the mouse
|
||||||
int dy = ie_event->mouse.y - prev_mouse.y;
|
// point may miss the sphere entirely, so instead map the mouse
|
||||||
printf ("mouse dragged: [%d, %d]\n", dx, dy);
|
// point to the hyperbolic cone pointed towards the viewer. The
|
||||||
|
// cone and sphere are tangential at the 45 degree latitude
|
||||||
|
vec[2] = t / sqrt (d);
|
||||||
}
|
}
|
||||||
|
return (vec4f_t) { vec[2], vec[0], vec[1] };
|
||||||
|
}
|
||||||
|
static float trackball_sensitivity = 0.1;
|
||||||
|
#define sphere_scale 1.0f
|
||||||
|
static void
|
||||||
|
camera_mouse_trackball (void)
|
||||||
|
{
|
||||||
|
vec4f_t delta = {
|
||||||
|
IN_UpdateAxis (&deb_mouse_x),
|
||||||
|
IN_UpdateAxis (&deb_mouse_y),
|
||||||
|
};
|
||||||
|
float size = min (deb_xlen, deb_ylen) / 2;
|
||||||
|
vec4f_t center = { deb_xlen, deb_ylen }; center /= 2;
|
||||||
|
vec4f_t start = mouse_start - center;
|
||||||
|
vec4f_t end = start + delta;
|
||||||
|
start = trackball_vector (start / (size * sphere_scale));
|
||||||
|
end = trackball_vector (end / (size * sphere_scale));
|
||||||
|
vec4f_t drot = crossf (end, start);
|
||||||
|
float ma = dotf (drot, drot)[0];
|
||||||
|
if (ma < 1e-7) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drot /= sqrtf (ma);
|
||||||
|
float dist = sqrtf (dotf (delta, delta)[0]) * trackball_sensitivity;
|
||||||
|
dist /= size;
|
||||||
|
drot *= sinf (dist);
|
||||||
|
drot[3] = cosf (dist);
|
||||||
|
|
||||||
prev_mouse = ie_event->mouse;
|
vec4f_t rot = Transform_GetLocalRotation (debug_camera_pivot);
|
||||||
|
vec4f_t r = normalf (qmulf (rot, drot));
|
||||||
|
Transform_SetLocalRotation (debug_camera_pivot, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -418,6 +489,10 @@ static imui_window_t cam_window = {
|
||||||
.name = "Debug Camera",
|
.name = "Debug Camera",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static imui_window_t inp_window = {
|
||||||
|
.name = "Debug Input",
|
||||||
|
};
|
||||||
|
|
||||||
static imui_window_t debug_menu = {
|
static imui_window_t debug_menu = {
|
||||||
.name = "Debug##menu",
|
.name = "Debug##menu",
|
||||||
.group_offset = 0,
|
.group_offset = 0,
|
||||||
|
@ -440,9 +515,13 @@ menu_bar (void)
|
||||||
system_info_window.is_open = true;
|
system_info_window.is_open = true;
|
||||||
}
|
}
|
||||||
hs ();
|
hs ();
|
||||||
if (UI_Button ("Camera")) {
|
if (UI_MenuItem ("Camera")) {
|
||||||
cam_window.is_open = true;
|
cam_window.is_open = true;
|
||||||
}
|
}
|
||||||
|
hs ();
|
||||||
|
if (UI_MenuItem ("Input")) {
|
||||||
|
inp_window.is_open = true;
|
||||||
|
}
|
||||||
if (r_funcs->debug_ui) {
|
if (r_funcs->debug_ui) {
|
||||||
hs ();
|
hs ();
|
||||||
// create the menu so the renderer can access it
|
// create the menu so the renderer can access it
|
||||||
|
@ -486,7 +565,11 @@ camera_window (void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (r_override_camera) {
|
if (r_override_camera) {
|
||||||
|
if (cam_mode == 0) {
|
||||||
camera_mouse_first_person();
|
camera_mouse_first_person();
|
||||||
|
} else if (cam_mode == 1) {
|
||||||
|
camera_mouse_trackball ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cam_state) {
|
if (cam_state) {
|
||||||
|
@ -525,6 +608,24 @@ camera_window (void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_window (void)
|
||||||
|
{
|
||||||
|
UI_Window (&inp_window) {
|
||||||
|
if (inp_window.is_collapsed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; deb_in_axes[i]; i++) {
|
||||||
|
auto axis = deb_in_axes[i];
|
||||||
|
UI_Horizontal {
|
||||||
|
UI_Label (axis->name);
|
||||||
|
UI_FlexibleSpace ();
|
||||||
|
UI_Labelf ("%8g", axis->rel_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool close_debug_pressed = false;
|
static bool close_debug_pressed = false;
|
||||||
static void
|
static void
|
||||||
system_info (void)
|
system_info (void)
|
||||||
|
@ -602,6 +703,7 @@ Con_Debug_Draw (void)
|
||||||
system_info ();
|
system_info ();
|
||||||
color_window ();
|
color_window ();
|
||||||
camera_window ();
|
camera_window ();
|
||||||
|
input_window ();
|
||||||
if (r_funcs->debug_ui) {
|
if (r_funcs->debug_ui) {
|
||||||
r_funcs->debug_ui (debug_imui);
|
r_funcs->debug_ui (debug_imui);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue