Compare commits

..
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 770f8881e0 At the wheel, the right trigger is the throttle and the left the brake
Holding a Warthog's wheel, the triggers drive as the left stick pushed up
or down would (backwards once stopped), unless the stick is pushed further;
they neither fire nor hold a grenade then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:50:41 +02:00
Stefan MaierandClaude Opus 5.5 38ab192749 Steer a Warthog by its wheel with the hands
At an open vehicle's wheel, a hand that grips near it (where the driver's
drawn hands hold it) takes hold, and the hands turn it about its column:
both by the line between them, one about the wheel's middle. Turned
vr.vehicle_wheel_degrees (90) either way it steers fully; let go, the right
stick steers as before. Holding the wheel, the right grip is no melee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:00:26 +02:00
10 changed files with 261 additions and 349 deletions

No files matched your search

+5 -6
View File
@@ -140,12 +140,6 @@ out (halo_vr_scope_pass). */
int halo_vr_scope(short local_player_index, float magnification, float position[3], float forward[3], float up[3],
float *vertical_field_of_view);
int halo_vr_scope_pass(void);
/* The menus' pointer (d3d8_gl.c's halo_ui_pointer_update): where a
controller points on the menus' screen, in their 640x480, whether that
moved since asked, and the trigger's clicks since (nonzero while it points
at the screen); and where it points this frame, to draw it (render.c) */
int halo_vr_menu_pointer(int *x, int *y, int *click_x, int *click_y, int *moved, int *clicks);
int halo_vr_menu_pointer_at(int *x, int *y);
/* Players' names over their heads (hud.c, render.c): whether they are drawn
apart this frame; the slot a name is drawn into (left, top, right, bottom
of the game's 640x480); a name's place (world units, over the head),
@@ -196,6 +190,11 @@ enum
};
void halo_vr_set_seat(short local_player_index, int seat, int armed, float vehicle_yaw);
void halo_vr_set_seat_frame(short local_player_index, float vehicle_yaw);
/* At an open vehicle's wheel (a Warthog's), each frame (render.c): the
driver's hands on it as drawn (world units, from the game's camera), or
NULL; the player's own hands turn it. And whether they hold it. */
void halo_vr_set_wheel(short local_player_index, const float left[3], const float right[3]);
int halo_vr_wheel_held(void);
/* whether a seated player's view stays in first person (director.c) */
int halo_vr_vehicle_first_person(void);
/* VR test aid (debug.vehicle_spawn): both sticks held in for a moment asks
+3 -13
View File
@@ -1365,21 +1365,11 @@ HRESULT WINAPI Direct3D_CreateDevice(UINT adapter, D3DDEVTYPE device_type, void
/* ---------- the menus' pointer */
#ifdef HALO_ANDROID
/* the controller pointing at the menus' screen in VR (halo_vr_menu_pointer) */
int halo_ui_pointer_update(int menus_active, struct halo_ui_pointer *pointer)
{
int x, y, click_x, click_y, moved, clicks;
if (!menus_active || !halo_vr_menu_pointer(&x, &y, &click_x, &click_y, &moved, &clicks))
return 0;
memset(pointer, 0, sizeof(*pointer));
pointer->x = (short)(x - (halo_screen_width() - 640) / 2);
pointer->y = (short)y;
pointer->click_x = (short)(click_x - (halo_screen_width() - 640) / 2);
pointer->click_y = (short)click_y;
pointer->moved = (unsigned char)(moved != 0);
pointer->left_clicks = (unsigned char)(clicks < 255 ? clicks : 255);
return 1;
(void)menus_active;
(void)pointer;
return 0;
}
#else
/* a point in the window, as SDL reports it, in the menus' coordinates: the
+7
View File
@@ -221,6 +221,13 @@ static const struct config_setting config_settings[] =
{ "vr.vehicle_pitch_degrees", _config_real, "45", "HALO_VR_VEHICLE_PITCH", _environment_value, _platform_android,
"Driving, the right stick pushed fully up or down aims this many degrees\n"
"above or below the horizon (a Banshee climbs or dives)." },
{ "vr.vehicle_wheel", _config_boolean, "true", "HALO_VR_VEHICLE_WHEEL", _environment_value, _platform_android,
"At an open vehicle's wheel (a Warthog's), grip it with one hand or both and\n"
"turn it to steer, the right trigger the throttle and the left the brake;\n"
"let go, and the right stick steers." },
{ "vr.vehicle_wheel_degrees", _config_real, "90", "HALO_VR_VEHICLE_WHEEL_DEGREES", _environment_value,
_platform_android,
"The wheel turned this many degrees either way steers fully." },
{ "vr.comfort_vignette", _config_real, "0.7", "HALO_VR_COMFORT_VIGNETTE", _environment_value, _platform_android,
"How far the view darkens from its edges while a vehicle turns you\n"
"(0 never, 1 to a small window)." },
+158 -134
View File
@@ -107,6 +107,16 @@ static struct
float vehicle_steer_angle; /* radians */
float vehicle_gun_steer_angle; /* radians: steering that aims guns too */
float vehicle_pitch_angle;
/* the wheel of an open vehicle (a Warthog's: wheel_update) */
int vehicle_wheel; /* vr.vehicle_wheel: the hands turn it */
float vehicle_wheel_angle; /* radians: turned this far, it steers fully */
int wheel_valid; /* where it is, as last drawn */
float wheel_hands_drawn[2][3]; /* the driver's drawn hands on it, from the camera (world units) */
int wheel_held[HALO_XR_HAND_COUNT];
int wheel_hands; /* the hands that held it last frame (bits) */
float wheel_reference; /* their turn about the column last frame */
float wheel_turn; /* the wheel's turn (radians, anticlockwise as the driver sees it) */
int wheel_logged;
float vehicle_steer_curve; /* the stick's deflection to this power */
float vehicle_steer_smoothing; /* seconds */
float steer[2]; /* the steering as smoothed: aside, up (-1..1) */
@@ -455,6 +465,10 @@ void halo_vr_initialize(void)
vr.vehicle_steer_angle = (float)config_real("vr.vehicle_steer_degrees") * PI / 180.0f;
vr.vehicle_gun_steer_angle = (float)config_real("vr.vehicle_gun_steer_degrees") * PI / 180.0f;
vr.vehicle_pitch_angle = (float)config_real("vr.vehicle_pitch_degrees") * PI / 180.0f;
vr.vehicle_wheel = config_boolean("vr.vehicle_wheel");
vr.vehicle_wheel_angle = (float)config_real("vr.vehicle_wheel_degrees") * PI / 180.0f;
if (vr.vehicle_wheel_angle < 10.0f * PI / 180.0f)
vr.vehicle_wheel_angle = 10.0f * PI / 180.0f;
vr.vehicle_steer_curve = (float)config_real("vr.vehicle_steer_curve");
vr.vehicle_steer_smoothing = (float)config_real("vr.vehicle_steer_smoothing");
vr.comfort_vignette = (float)config_real("vr.comfort_vignette");
@@ -557,7 +571,8 @@ static void grenade_update(void)
const struct halo_xr_hand *hand = &vr.frame.hands[HALO_XR_HAND_LEFT];
unsigned long long now = now_us();
int valid = hand->active && (hand->flags & HALO_XR_POSE_VALID);
int held = valid && vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN &&
/* (at a wheel the left trigger brakes: xinput_sdl.c) */
int held = valid && vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN && !vr.wheel_hands &&
hand->trigger > (vr.grenade_held ? 0.2f : 0.3f);
int count = vr.grenade_sample_count;
@@ -737,7 +752,9 @@ static void melee_update(void)
continue;
}
/* (a grenade's throw is not a blow) */
/* (nor is turning the wheel) */
if (vr.hand_last_valid[hand] && seconds > 0.0f && seconds < 0.1f && vr.melee_swing_speed > 0.0f &&
!vr.wheel_held[hand] &&
vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN && now >= vr.melee_ready_us &&
!(hand == HALO_XR_HAND_LEFT && (vr.grenade_held || now - vr.grenade_released_us < 500000)))
{
@@ -823,6 +840,138 @@ static void turn_update(void)
seat_body();
}
/* At an open vehicle's wheel (a Warthog's, vr.vehicle_wheel), a hand that
grips near it takes hold of it, and the hands turn it about its column: both
by the line between them, one about the wheel's middle. Where it is, the
driver's hands as drawn hold it (halo_vr_set_wheel); its column runs from
there back towards the driver, tipped up WHEEL_COLUMN_TILT. Held, its turn
steers (halo_vr_aim); let go, the right stick does. */
#define WHEEL_REACH 0.35f /* metres from its middle */
#define WHEEL_COLUMN_TILT (45.0f * PI / 180.0f) /* from the horizontal */
static void wheel_release(void)
{
int hand;
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
vr.wheel_held[hand] = 0;
vr.wheel_hands = 0;
}
static void wheel_update(void)
{
static const float local_up[3] = { 0.0f, 1.0f, 0.0f };
float world[3], centre[3], forward_game[3], forward[3], column[3], up[3], side[3], line[3];
float heading = seat_heading(), along, angle;
int hand, axis, hands = 0;
if (vr.seat_mode != HALO_VR_SEAT_STEER || !vr.vehicle_wheel || !vr.wheel_valid || !vr.frame.focused ||
vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN)
{
wheel_release();
return;
}
for (axis = 0; axis < 3; axis++)
{
world[axis] = vr.game_camera[axis] +
(vr.wheel_hands_drawn[0][axis] + vr.wheel_hands_drawn[1][axis]) * 0.5f;
}
world_to_local(world, centre);
if (!vr.wheel_logged)
{
float apart[3];
for (axis = 0; axis < 3; axis++)
apart[axis] = (vr.wheel_hands_drawn[0][axis] - vr.wheel_hands_drawn[1][axis]) * METRES_PER_WORLD_UNIT;
platform_log("VR: wheel at %.2f %.2f %.2f (LOCAL), the hands %.2f m apart", centre[0], centre[1], centre[2],
sqrtf(apart[0] * apart[0] + apart[1] * apart[1] + apart[2] * apart[2]));
vr.wheel_logged = 1;
}
/* its column, and the wheel's up and left about it */
forward_game[0] = cosf(heading);
forward_game[1] = sinf(heading);
forward_game[2] = 0.0f;
game_to_local(forward_game, forward);
for (axis = 0; axis < 3; axis++)
column[axis] = -forward[axis] * cosf(WHEEL_COLUMN_TILT) + local_up[axis] * sinf(WHEEL_COLUMN_TILT);
along = local_up[0] * column[0] + local_up[1] * column[1] + local_up[2] * column[2];
for (axis = 0; axis < 3; axis++)
up[axis] = local_up[axis] - column[axis] * along;
normalize3(up);
cross3(column, up, side);
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
{
const struct halo_xr_hand *state = &vr.frame.hands[hand];
float apart[3];
if (!state->active || !(state->flags & HALO_XR_POSE_VALID) || state->squeeze < 0.35f)
{
vr.wheel_held[hand] = 0;
continue;
}
if (!vr.wheel_held[hand])
{
for (axis = 0; axis < 3; axis++)
apart[axis] = state->grip.position[axis] - centre[axis];
if (state->squeeze < 0.6f ||
sqrtf(apart[0] * apart[0] + apart[1] * apart[1] + apart[2] * apart[2]) > WHEEL_REACH)
{
continue;
}
vr.wheel_held[hand] = 1;
host_xr_haptic(hand, 0.3f, 0.05f);
}
hands |= 1 << hand;
}
if (!hands)
{
vr.wheel_hands = 0;
return;
}
/* the hands' turn about the column */
for (axis = 0; axis < 3; axis++)
{
line[axis] = hands == ((1 << HALO_XR_HAND_LEFT) | (1 << HALO_XR_HAND_RIGHT)) ?
vr.frame.hands[HALO_XR_HAND_LEFT].grip.position[axis] -
vr.frame.hands[HALO_XR_HAND_RIGHT].grip.position[axis] :
vr.frame.hands[hands & (1 << HALO_XR_HAND_LEFT) ? HALO_XR_HAND_LEFT : HALO_XR_HAND_RIGHT]
.grip.position[axis] - centre[axis];
}
angle = atan2f(line[0] * side[0] + line[1] * side[1] + line[2] * side[2],
line[0] * up[0] + line[1] * up[1] + line[2] * up[2]);
if (!vr.wheel_hands)
{
/* taken hold of: turned as far as it steers now */
vr.wheel_turn = -vr.steer[0] * vr.vehicle_wheel_angle;
}
else if (hands == vr.wheel_hands)
{
vr.wheel_turn += wrap_angle(angle - vr.wheel_reference);
}
if (vr.wheel_turn > vr.vehicle_wheel_angle)
vr.wheel_turn = vr.vehicle_wheel_angle;
if (vr.wheel_turn < -vr.vehicle_wheel_angle)
vr.wheel_turn = -vr.vehicle_wheel_angle;
vr.wheel_reference = angle;
vr.wheel_hands = hands;
}
void halo_vr_set_wheel(short local_player_index, const float left[3], const float right[3])
{
if (!vr.active || local_player_index != 0)
return;
vr.wheel_valid = left && right;
if (!vr.wheel_valid)
return;
memcpy(vr.wheel_hands_drawn[0], left, sizeof(vr.wheel_hands_drawn[0]));
memcpy(vr.wheel_hands_drawn[1], right, sizeof(vr.wheel_hands_drawn[1]));
}
int halo_vr_wheel_held(void)
{
return vr.active && vr.wheel_hands != 0;
}
/* The comfort vignette: while a vehicle turns the player (from 15 degrees a
second, fully at 60), the eye images darken from their edges; it comes in
quickly and goes slowly. */
@@ -1043,6 +1192,7 @@ void halo_vr_frame_begin(void)
vr_recentre();
}
turn_update();
wheel_update();
vignette_update();
vehicle_spawn_update();
look_update();
@@ -1394,6 +1544,13 @@ int halo_vr_aim(short local_player_index, float current_yaw, float current_pitch
amount = amount > 0.0f ? powf(amount > 1.0f ? 1.0f : amount, vr.vehicle_steer_curve) : 0.0f;
if (stick[axis] < 0.0f)
amount = -amount;
if (axis == 0 && vr.wheel_hands)
{
/* (the hands on the wheel steer, as they turn it: clockwise
to the right) */
vr.steer[0] = -vr.wheel_turn / vr.vehicle_wheel_angle;
continue;
}
vr.steer[axis] += (amount - vr.steer[axis]) * blend;
}
/* (a Ghost or a Banshee aims its guns as it steers: less, finer) */
@@ -1969,133 +2126,6 @@ static int crosshair_pose(struct halo_xr_pose *pose, float *size)
return 1;
}
/* ---------- the menus' pointer
In the menus (the screen fixed in the room), a controller points at the
screen and its trigger clicks, as the desktop builds' mouse does
(halo_ui_pointer.h): where its aim's line meets the screen, in the menus'
640x480. The hand whose trigger was pressed last points; a click gives a
short tick of the hand's rumble. */
static struct
{
int valid;
float x, y;
float reported_x, reported_y;
int hand;
int trigger_down[HALO_XR_HAND_COUNT];
int clicks;
/* a click is taken where it pointed as the trigger began to move
(pulling it through tips the controller) */
int pulling[HALO_XR_HAND_COUNT];
float pull_x[HALO_XR_HAND_COUNT], pull_y[HALO_XR_HAND_COUNT];
float click_x, click_y;
} menu_pointer;
static void menu_pointer_update(void)
{
static const float forward[3] = { 0.0f, 0.0f, -1.0f };
const struct halo_xr_hand *hand;
float direction[3], t, hit_x, hit_y, width, height;
int index;
menu_pointer.valid = 0;
if (!vr.active || vr.last_overlay_mode != HALO_XR_OVERLAY_SCREEN || !vr.frame.focused)
{
menu_pointer.trigger_down[0] = menu_pointer.trigger_down[1] = 0;
menu_pointer.pulling[0] = menu_pointer.pulling[1] = 0;
return;
}
/* (the hand that clicked last points; a click counts as it goes down) */
for (index = 0; index < HALO_XR_HAND_COUNT; index++)
{
int down = vr.frame.hands[index].active && (vr.frame.hands[index].buttons & HALO_XR_BUTTON_TRIGGER);
if (down && !menu_pointer.trigger_down[index])
{
menu_pointer.hand = index;
menu_pointer.clicks++;
}
menu_pointer.trigger_down[index] = down;
}
hand = &vr.frame.hands[menu_pointer.hand];
if (!hand->active || !(hand->flags & HALO_XR_POSE_VALID))
{
hand = &vr.frame.hands[1 - menu_pointer.hand];
if (!hand->active || !(hand->flags & HALO_XR_POSE_VALID))
return;
}
quaternion_rotate(hand->aim.orientation, forward, direction);
if (direction[2] > -1e-3f)
return;
t = (-vr.screen_distance - hand->aim.position[2]) / direction[2];
if (t <= 0.0f)
return;
hit_x = hand->aim.position[0] + direction[0] * t;
hit_y = hand->aim.position[1] + direction[1] * t;
width = vr.screen_width;
height = vr.screen_width * 0.75f;
menu_pointer.x = (hit_x / width + 0.5f) * 640.0f;
menu_pointer.y = (0.5f - hit_y / height) * 480.0f;
menu_pointer.valid = menu_pointer.x >= 0.0f && menu_pointer.x < 640.0f && menu_pointer.y >= 0.0f &&
menu_pointer.y < 480.0f;
/* (the click where the trigger began to move, if it pointed then) */
{
int pulling = vr.frame.hands[menu_pointer.hand].trigger > 0.1f;
if (pulling && !menu_pointer.pulling[menu_pointer.hand] && menu_pointer.valid)
{
menu_pointer.pull_x[menu_pointer.hand] = menu_pointer.x;
menu_pointer.pull_y[menu_pointer.hand] = menu_pointer.y;
}
if (!pulling || !menu_pointer.pulling[menu_pointer.hand])
{
menu_pointer.click_x = menu_pointer.x;
menu_pointer.click_y = menu_pointer.y;
}
else
{
menu_pointer.click_x = menu_pointer.pull_x[menu_pointer.hand];
menu_pointer.click_y = menu_pointer.pull_y[menu_pointer.hand];
}
if (!pulling || menu_pointer.valid)
menu_pointer.pulling[menu_pointer.hand] = pulling;
}
}
int halo_vr_menu_pointer(int *x, int *y, int *click_x, int *click_y, int *moved, int *clicks)
{
menu_pointer_update();
*click_x = (int)menu_pointer.click_x;
*click_y = (int)menu_pointer.click_y;
*clicks = menu_pointer.clicks;
if (menu_pointer.clicks)
host_xr_haptic(menu_pointer.hand, 0.3f, 0.02f);
menu_pointer.clicks = 0;
if (!menu_pointer.valid)
return 0;
*x = (int)menu_pointer.x;
*y = (int)menu_pointer.y;
/* (moved past a hand's tremor: a few pixels) */
*moved = fabsf(menu_pointer.x - menu_pointer.reported_x) >= 3.0f ||
fabsf(menu_pointer.y - menu_pointer.reported_y) >= 3.0f;
if (*moved)
{
menu_pointer.reported_x = menu_pointer.x;
menu_pointer.reported_y = menu_pointer.y;
}
return 1;
}
int halo_vr_menu_pointer_at(int *x, int *y)
{
if (!menu_pointer.valid)
return 0;
*x = (int)menu_pointer.x;
*y = (int)menu_pointer.y;
return 1;
}
/* ---------- players' names
Names over the heads (hud.c) are drawn into slots of a region of the back
@@ -2400,12 +2430,6 @@ int halo_vr_scope(short local_player_index, float magnification, float position[
return 0;
}
int halo_vr_scope_pass(void) { return 0; }
int halo_vr_menu_pointer(int *x, int *y, int *click_x, int *click_y, int *moved, int *clicks)
{
(void)x; (void)y; (void)click_x; (void)click_y; (void)moved; *clicks = 0;
return 0;
}
int halo_vr_menu_pointer_at(int *x, int *y) { (void)x; (void)y; return 0; }
int halo_vr_names_apart(void) { return 0; }
void halo_vr_name_slot(int slot, short bounds[4]) { (void)slot; (void)bounds; }
int halo_vr_add_name(const float point[3]) { (void)point; return -1; }
+18 -4
View File
@@ -540,12 +540,17 @@ In play, laid out for VR shooters rather than by position:
right stick turns (vr.c); click zooms left stick move; click crouches
menu pause (start)
Holding a Warthog's wheel (vr.c), the right trigger is the throttle and the
left one the brake (backwards, once stopped): the left stick pushed up or
down as far, unless it is pushed further.
In the menus the buttons are the Xbox's by position: A accepts, B goes back. */
#include "guest_host.h"
#include "halo_xr_abi.h"
int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menus);
int halo_vr_wheel_held(void);
static SHORT vr_stick(float value)
{
@@ -573,11 +578,18 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
if (left->buttons & HALO_XR_BUTTON_MENU) pad->wButtons |= XINPUT_GAMEPAD_START;
if (left->buttons & HALO_XR_BUTTON_THUMBSTICK) pad->wButtons |= XINPUT_GAMEPAD_LEFT_THUMB;
if (right->buttons & HALO_XR_BUTTON_THUMBSTICK) pad->wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB;
/* (in the menus the triggers click the pointer: halo_vr_menu_pointer) */
if (!menus && (BYTE)(right->trigger * 255.0f) > pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER])
if ((BYTE)(right->trigger * 255.0f) > pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER])
pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = (BYTE)(right->trigger * 255.0f);
if (!menus && (BYTE)(left->trigger * 255.0f) > pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER])
if ((BYTE)(left->trigger * 255.0f) > pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER])
pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = (BYTE)(left->trigger * 255.0f);
if (!menus && halo_vr_wheel_held())
{
/* at the wheel: the triggers drive, and neither fire nor throw */
pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = 0;
pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = 0;
value = vr_stick(right->trigger - left->trigger);
if (abs(value) > abs(pad->sThumbLY)) pad->sThumbLY = value;
}
if (menus)
{
/* the right stick scrolls too; turning is off in the menus */
@@ -593,7 +605,9 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
}
merge_button(pad, XINPUT_GAMEPAD_A, (right->buttons & HALO_XR_BUTTON_A) != 0);
merge_button(pad, XINPUT_GAMEPAD_X, (left->buttons & HALO_XR_BUTTON_A) != 0);
merge_button(pad, XINPUT_GAMEPAD_B, (right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 || halo_vr_melee());
/* (the grips holding a Warthog's wheel are not a blow) */
merge_button(pad, XINPUT_GAMEPAD_B,
((right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 && !halo_vr_wheel_held()) || halo_vr_melee());
merge_button(pad, XINPUT_GAMEPAD_Y, (left->buttons & HALO_XR_BUTTON_B) != 0);
{
/* B: a tap is the flashlight, holding it switches grenades; the
+7 -101
View File
@@ -677,9 +677,6 @@ struct widget_instance;
#include "text/text_group.h"
#include "text/unicode.h"
#include "ui_widget.h"
#ifdef HALO_ANDROID
#include "halo_vr.h"
#endif
/* ---------- constants */
@@ -5272,8 +5269,6 @@ struct ui_mouse_target
rectangle2d bounds;
short kind;
short button_index;
/* (a button's: where its icon begins) */
short icon_x0;
};
static struct ui_mouse_target ui_mouse_targets[UI_MOUSE_MAXIMUM_TARGETS];
@@ -5408,7 +5403,6 @@ static void ui_mouse_note_target(
rectangle2d bounds = definition->bounds;
short button_index;
short kind;
short icon_x0;
if (!ui_mouse_noting_targets ||
ui_mouse_target_count >= UI_MOUSE_MAXIMUM_TARGETS ||
@@ -5421,22 +5415,13 @@ static void ui_mouse_note_target(
bounds.y0 += offset.y;
bounds.y1 += offset.y;
button_index = ui_mouse_key_button(widget);
icon_x0 = bounds.x0;
if (button_index != NONE)
{
/* the icon and its label: the text beginning just right of it */
struct widget_instance *sibling;
rectangle2d label_bounds;
short best_distance = 17;
#ifdef HALO_ANDROID
/* (VR: a controller points less steadily than a mouse: the label a
little further off is the button's too) */
short most_distance = halo_vr_active() ? 25 : 17;
#else
short most_distance = 17;
#endif
best_distance = most_distance;
kind = _ui_mouse_target_button;
for (sibling = parent ? parent->child : NULL; sibling; sibling = sibling->next)
{
@@ -5460,23 +5445,12 @@ static void ui_mouse_note_target(
label_bounds = sibling_bounds;
}
}
if (best_distance < most_distance)
if (best_distance < 17)
{
bounds.x1 = MAX(bounds.x1, label_bounds.x1);
bounds.y0 = MIN(bounds.y0, label_bounds.y0);
bounds.y1 = MAX(bounds.y1, label_bounds.y1);
}
#ifdef HALO_ANDROID
/* (and a margin about it: above and below, little to the sides,
where the next key is) */
if (halo_vr_active())
{
bounds.x0 -= 2;
bounds.x1 += 2;
bounds.y0 -= 4;
bounds.y1 += 4;
}
#endif
}
else if (parent && ui_mouse_list_shows_several(parent))
{
@@ -5508,7 +5482,6 @@ static void ui_mouse_note_target(
target->bounds = bounds;
target->kind = kind;
target->button_index = button_index;
target->icon_x0 = icon_x0;
return;
}
@@ -5519,33 +5492,16 @@ static struct ui_mouse_target *ui_mouse_target_at(
short y)
{
long index;
struct ui_mouse_target *button = NULL;
for (index = ui_mouse_target_count - 1; index >= 0; index--)
{
struct ui_mouse_target *target = &ui_mouse_targets[index];
rectangle2d const *bounds = &target->bounds;
rectangle2d const *bounds = &ui_mouse_targets[index].bounds;
if (!(x >= bounds->x0 && x < bounds->x1 && y >= bounds->y0 && y < bounds->y1))
continue;
/* port: a key's label's text box can be far wider than its text, over
the next key: of keys under the point, the one whose icon is nearest
on its left (each key from its icon to the next one's) */
if (target->kind == _ui_mouse_target_button)
{
if (!button ||
(target->icon_x0 <= x + 2 && (button->icon_x0 > x + 2 || target->icon_x0 > button->icon_x0)))
{
button = target;
}
continue;
}
if (!button)
return target;
break;
if (x >= bounds->x0 && x < bounds->x1 && y >= bounds->y0 && y < bounds->y1)
return &ui_mouse_targets[index];
}
return button;
return NULL;
}
static boolean ui_mouse_widget_has_focus(
@@ -5736,8 +5692,6 @@ enum
MAXIMUM_SHOWN_PUBLIC_GAMES = 32,
/* the online games panel's rows: "enter a code" and the games */
ONLINE_PANEL_ROWS = 11,
/* (the rows before the games: "enter a code") */
ONLINE_PANEL_FIXED_ROWS = 1,
};
static struct
@@ -5978,48 +5932,6 @@ static boolean ui_join_code_take_event(
return FALSE;
}
/* the pointer over the VR port's panels: the row under it chosen, and a
click on it as A (TRUE: a panel is open and took it) */
static boolean ui_join_code_take_pointer(
struct halo_ui_pointer const *pointer)
{
struct event_record press;
short row;
/* (a click where it was clicked, which may be a moment before) */
short x = pointer->left_clicks ? pointer->click_x : pointer->x;
short y = pointer->left_clicks ? pointer->click_y : pointer->y;
csmemset(&press, 0, sizeof(press));
press.type = _event_type_button;
press.data.button.index = _gamepad_analog_button_a;
press.data.button.value = 1;
if (ui_join_code_globals.panel_open)
{
if (x >= 48 && x < 592 && y >= 128 && y < 128 + ONLINE_PANEL_ROWS * 22)
{
row = (short)(ui_join_code_globals.scroll + (y - 128) / 22);
if (row < ui_join_code_globals.game_count + ONLINE_PANEL_FIXED_ROWS)
{
ui_join_code_globals.selection = row;
if (pointer->left_clicks)
ui_join_code_panel_event(&press);
}
}
return TRUE;
}
if (ui_join_code_globals.host_panel_open)
{
if (x >= 148 && x < 492 && y >= 187 && y < 187 + HOST_PANEL_ROWS * 30)
{
ui_join_code_globals.host_selection = (short)((y - 187) / 30);
if (pointer->left_clicks)
ui_join_code_host_panel_event(&press);
}
return TRUE;
}
return FALSE;
}
/* the keyboard closed: the code typed, looked up */
static void ui_join_code_update(
void)
@@ -6274,9 +6186,8 @@ static void ui_widgets_process_mouse(
struct ui_mouse_target *target;
short controller_index = 0;
/* (the keyboard takes the pointer itself: virtual_keyboard.c) */
if (virtual_keyboard_active() ||
!halo_ui_pointer_update(ui_mouse_menus_active(), &pointer))
if (!halo_ui_pointer_update(ui_mouse_menus_active(), &pointer) ||
virtual_keyboard_active())
{
ui_mouse_press_count = 0;
ui_mouse_hover_pending = FALSE;
@@ -6284,11 +6195,6 @@ static void ui_widgets_process_mouse(
}
else
{
#ifdef HALO_ANDROID
/* (the VR port's panels over the menus take the pointer themselves) */
if (ui_join_code_take_pointer(&pointer))
return;
#endif
if (pointer.moved)
{
ui_mouse_hover_pending = TRUE;
-69
View File
@@ -1166,25 +1166,6 @@ static boolean virtual_keyboard_select(
return TRUE;
}
#ifdef HALO_ANDROID
/* the key drawn at a point of the menus' 640x480, or NONE */
static short virtual_keyboard_key_at(
short x,
short y)
{
short key;
for (key = 0; key < NUMBER_OF_VIRTUAL_KEYS; key++)
{
rectangle2d const *bounds = &keyboard_rect[key];
if (x >= bounds->x0 && x < bounds->x1 && y >= bounds->y0 && y < bounds->y1)
return key;
}
return NONE;
}
#endif
static void virtual_keyboard_process_internal(
void)
{
@@ -1323,56 +1304,6 @@ static void virtual_keyboard_process_internal(
}
}
#ifdef HALO_ANDROID
/* the VR port's: the controller's pointer (halo_ui_pointer.h) picks the
key under it, and its click types it */
{
struct halo_ui_pointer pointer;
if (halo_ui_pointer_update(TRUE, &pointer))
{
short x = pointer.left_clicks ? pointer.click_x : pointer.x;
short y = pointer.left_clicks ? pointer.click_y : pointer.y;
short key = virtual_keyboard_key_at(x, y);
if (key != NONE && (pointer.moved || pointer.left_clicks))
{
short row, column;
boolean found = FALSE;
for (row = 0; row < VIRTUAL_KEYBOARD_ROW_COUNT && !found; row++)
{
for (column = 0; column < VIRTUAL_KEYBOARD_COLUMN_COUNT && !found; column++)
{
if (virtual_keyboard_layout_table[row][column] == key)
{
virtual_keyboard_globals.row = row;
virtual_keyboard_globals.column = column;
found = TRUE;
}
}
}
}
if (pointer.left_clicks && key != NONE)
action = _event_key_select;
/* (the keys' hints under the keyboard, in its picture: B = BACK
cancels, A = ENTER finishes, as Done does) */
if (pointer.left_clicks && key == NONE && y >= 412 && y < 448)
{
if (x >= 380 && x < 480)
{
action = _event_cancel;
}
else if (x >= 488 && x < 600)
{
virtual_keyboard_globals.row = 0;
virtual_keyboard_globals.column = 0;
action = _event_key_select;
}
}
}
}
#endif
if (action != NONE)
{
virtual_keyboard_globals.last_key = virtual_keyboard_layout_table[
+17 -22
View File
@@ -667,8 +667,25 @@ static void render_vr_player_frame(
long vehicle_index = unit_index == NONE ? NONE : object_get(unit_index)->object.parent_object_index;
real_matrix4x3 *nodes = vehicle_index == NONE ? NULL : render_interpolation_object_node_matrices(vehicle_index);
real_point3d left_hand;
real_point3d right_hand;
if (nodes)
halo_vr_set_seat_frame(window->local_player_index, (float)atan2(nodes[0].forward.j, nodes[0].forward.i));
if (nodes && render_object_vr_wheel_hands(unit_index, &left_hand, &right_hand))
{
/* (at a wheel: the hands that hold it) */
float left[3] = { left_hand.x - game_position[0], left_hand.y - game_position[1],
left_hand.z - game_position[2] };
float right[3] = { right_hand.x - game_position[0], right_hand.y - game_position[1],
right_hand.z - game_position[2] };
halo_vr_set_wheel(window->local_player_index, left, right);
}
else
{
halo_vr_set_wheel(window->local_player_index, NULL, NULL);
}
}
if (halo_vr_multiview())
{
@@ -874,28 +891,6 @@ static void render_vr_overlay(
interface_draw_screen();
halo_screen_ui_offset(TRUE);
render_ui_widgets(window->local_player_index, &window->rasterizer_camera.viewport_bounds);
if (menus_active)
{
/* the controller's pointer on the menus' screen: a dot, ringed */
int pointer_x, pointer_y;
if (halo_vr_menu_pointer_at(&pointer_x, &pointer_y))
{
rectangle2d dot;
pointer_x -= (int)(halo_screen_width() - 640) / 2;
dot.x0 = (short)(pointer_x - 5);
dot.x1 = (short)(pointer_x + 5);
dot.y0 = (short)(pointer_y - 5);
dot.y1 = (short)(pointer_y + 5);
draw_quad(&dot, 0xE0102030);
dot.x0 += 2;
dot.x1 -= 2;
dot.y0 += 2;
dot.y1 -= 2;
draw_quad(&dot, 0xFF8FD3FF);
}
}
halo_screen_ui_offset(FALSE);
rasterizer_window_end();
+5
View File
@@ -154,6 +154,11 @@ boolean render_object_vr_seated_body(
long object_index);
boolean render_object_vr_at_controls(
long object_index);
/* at an open wheel (a Warthog's), where its drawn hands hold it */
boolean render_object_vr_wheel_hands(
long object_index,
real_point3d *left,
real_point3d *right);
#endif
void render_objects(
void);
+41
View File
@@ -509,6 +509,47 @@ boolean render_object_vr_seated_body(
vehicle_vr_encloses_riders(object->object.parent_object_index));
}
/* at an open wheel (a Warthog's): where the driver's hands hold it, as the
seat's animation has them this frame (vr.c: the player's own hands turn
it) */
boolean render_object_vr_wheel_hands(
long object_index,
real_point3d *left,
real_point3d *right)
{
struct object_datum *object;
struct model *model;
real_matrix4x3 *nodes;
short node_index;
short left_index = NONE;
short right_index = NONE;
if (!render_object_vr_seated_body(object_index))
return FALSE;
object = object_get(object_index);
if (!TEST_FLAG(_object_mask_vehicle, object_get(object->object.parent_object_index)->object.type))
return FALSE;
model = model_definition_get(object_definition_get(object->definition_index)->object.model.index);
nodes = render_interpolation_object_node_matrices(object_index);
if (!nodes)
nodes = object_get_node_matrices(object_index);
for (node_index = 0; node_index < model->nodes.count; node_index++)
{
char const *name = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_index, struct model_node)->name;
size_t length = strlen(name);
if (length >= 6 && !strcmp(name + length - 6, "l hand"))
left_index = node_index;
if (length >= 6 && !strcmp(name + length - 6, "r hand"))
right_index = node_index;
}
if (left_index == NONE || right_index == NONE)
return FALSE;
*left = nodes[left_index].position;
*right = nodes[right_index].position;
return TRUE;
}
/* its node matrices with the head's shrunk to nothing, which takes the
helmet out of the eyes' way */
static real_matrix4x3 *object_vr_headless_node_matrices(