VR arms 3a.6: vr.body (arms, hands, off) and vr.fingers in the VR options

- vr.body = "arms" (default): arms and hands as before; "hands": the arms'
  bones shrunk to a point 3.5 cm behind each wrist along the hand's line
  so the cuff closes behind the hand (vr_hide_arm, after HaloCE-Quest-VR's
  vr_hide_forearms, CC0), no arm IK solved; "off": the hands model is not
  drawn (one model draw less an eye) and nothing of the arms posed; the
  weapon, its aim with both hands, barrel, laser and crosshair stay.
- vr.c reads vr.body with the live settings (halo_vr_body_mode).
- The VR options panel gets "Body" (Arms and hands / Hands only / Weapon
  only) and "Fingers" (Tracked / Off) rows; its twelve rows are 22
  pixels apart (were 26) so the panel keeps its box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Stefan MaierandClaude Opus 5.5 committed 2026-10-06 11:35:07 +02:00
1 parent 98e848de39
commit 6d2dcfaaec
7 files changed
+159 -5

No files matched your search

+64
View File
@@ -749,4 +749,68 @@ void vr_straighten_finger(
vr_turn_subtree(skeleton, matrices, joints[1], rotation);
}
/* ---------- hands alone (vr.body "hands")
The arm's bones (not the hand's, its fingers' or the weapon's) shrunk to
nothing at a point 3.5 cm back from the wrist along the hand's line,
knuckles to wrist: the cuff closes just behind the hand. Shrunk onto their
own places, the forearm drew the wrist's skin to the elbow and the hand
looked cut off (after HaloCE-Quest-VR, CC0, its vr_hide_forearms and
vr_hand_back_axis). */
void vr_hide_arm(
struct vr_skeleton const *skeleton,
real_matrix4x3 *matrices,
short const bones[NUMBER_OF_VR_ARM_BONES],
short const fingers[5][3],
short gun)
{
real_point3d centre = matrices[bones[_vr_arm_hand]].position;
real_point3d knuckles;
real_vector3d back;
boolean along = FALSE;
short finger, node_index, found = 0;
knuckles.x = knuckles.y = knuckles.z = 0.0f;
/* (the four fingers' bases: the thumb's sits off the line) */
for (finger = 1; finger < 5; finger++)
{
if (fingers[finger][0] == NONE)
continue;
knuckles.x += matrices[fingers[finger][0]].position.x;
knuckles.y += matrices[fingers[finger][0]].position.y;
knuckles.z += matrices[fingers[finger][0]].position.z;
found++;
}
if (found)
{
knuckles.x /= found;
knuckles.y /= found;
knuckles.z /= found;
vr_point_minus(&centre, &knuckles, &back);
along = vr_unit_vector(&back);
}
if (!along)
{
vr_point_minus(&matrices[bones[_vr_arm_fore]].position, &centre, &back);
along = vr_unit_vector(&back);
}
if (along)
{
centre.x += back.i * 0.035f * VR_UNITS_PER_METRE;
centre.y += back.j * 0.035f * VR_UNITS_PER_METRE;
centre.z += back.k * 0.035f * VR_UNITS_PER_METRE;
}
for (node_index = 0; node_index < skeleton->node_count; node_index++)
{
if (!vr_skeleton_under(skeleton, node_index, bones[_vr_arm_upper]) ||
vr_skeleton_under(skeleton, node_index, bones[_vr_arm_hand]) ||
(gun != NONE && vr_skeleton_under(skeleton, node_index, gun)))
{
continue;
}
matrices[node_index].scale = 0.0f;
matrices[node_index].position = centre;
}
}
#endif
+5
View File
@@ -71,6 +71,11 @@ joint */
void vr_straighten_finger(struct vr_skeleton const *skeleton, real_matrix4x3 *matrices, short const joints[3],
real amount);
/* the arm's bones shrunk to nothing just behind the wrist, the hand (with
its fingers) and the weapon left: the hand alone */
void vr_hide_arm(struct vr_skeleton const *skeleton, real_matrix4x3 *matrices,
short const bones[NUMBER_OF_VR_ARM_BONES], short const fingers[5][3], short gun);
#endif
#endif
+9
View File
@@ -141,6 +141,15 @@ int halo_vr_finger_curls(short local_player_index, int hand, float curls[4]);
which the arms' bending is eased */
int halo_vr_free_arm_bends(void);
double halo_vr_pose_time(void);
/* - what of the body is drawn (vr.body): the arms and hands, the hands
alone, or neither (the weapon alone) */
enum
{
HALO_VR_BODY_ARMS = 0,
HALO_VR_BODY_HANDS,
HALO_VR_BODY_OFF,
};
int halo_vr_body_mode(void);
/* a hand has just been thrust forward: a melee attack (xinput_sdl.c) */
int halo_vr_melee(void);
/* the weight of the melee attack under way, from the swing's speed (0..1;
+4
View File
@@ -198,6 +198,10 @@ static const struct config_setting config_settings[] =
{ "vr.two_hand_reach", _config_real, "0.2", "HALO_VR_TWO_HAND_REACH", _environment_value, _platform_android,
"How near, in metres, the left hand must be to the weapon's foregrip for\n"
"its grip button to take hold of it with both hands." },
{ "vr.body", _config_string, "\"arms\"", "HALO_VR_BODY", _environment_value, _platform_android,
"What of your body is drawn with the weapon: \"arms\", the arms and hands\n"
"reaching from the shoulders to the controllers; \"hands\", the hands\n"
"alone; \"off\", neither (the weapon alone)." },
{ "vr.fingers", _config_boolean, "true", "HALO_VR_FINGERS", _environment_value, _platform_android,
"The free hand's fingers follow the controller: the index finger the\n"
"trigger, the others the grip, the thumb where it rests (on the Quest's\n"
+13
View File
@@ -235,6 +235,7 @@ static struct
middle and the last two, curled 0 (open) to 1, and how fast */
int fingers;
int arm_ik; /* vr.arm_ik: the free arm bends at the elbow */
int body_mode; /* vr.body: HALO_VR_BODY_* */
float finger_curl[HALO_XR_HAND_COUNT][4];
float finger_velocity[HALO_XR_HAND_COUNT][4];
unsigned long long fingers_last_us;
@@ -469,6 +470,12 @@ static void vr_read_live_settings(void)
vr.comfort_vignette = (float)config_real("vr.comfort_vignette");
vr.fingers = config_boolean("vr.fingers");
vr.arm_ik = config_boolean("vr.arm_ik");
{
const char *body = config_string("vr.body");
vr.body_mode = !strcmp(body, "hands") ? HALO_VR_BODY_HANDS :
!strcmp(body, "off") ? HALO_VR_BODY_OFF : HALO_VR_BODY_ARMS;
}
}
void halo_vr_initialize(void)
@@ -1053,6 +1060,11 @@ int halo_vr_free_arm_bends(void)
return vr.active && vr.arm_ik;
}
int halo_vr_body_mode(void)
{
return vr.active ? vr.body_mode : HALO_VR_BODY_ARMS;
}
double halo_vr_pose_time(void)
{
return (double)vr.frame_start_us * 1e-6;
@@ -2840,6 +2852,7 @@ int halo_vr_finger_curls(short local_player_index, int hand, float curls[4])
return 0;
}
int halo_vr_free_arm_bends(void) { return 0; }
int halo_vr_body_mode(void) { return HALO_VR_BODY_ARMS; }
double halo_vr_pose_time(void) { return 0.0; }
void halo_vr_set_seat(short local_player_index, int seat, int armed, float vehicle_yaw)
{
+27 -3
View File
@@ -668,6 +668,10 @@ void first_person_weapon_draw(
if (log_skin)
model_vr_log_skin(first_person_interface->hands.index);
}
#endif
#ifdef HALO_ANDROID
/* VR: the weapon alone (vr.body "off") */
if (halo_vr_body_mode()!=HALO_VR_BODY_OFF)
#endif
render_model(
first_person_interface->hands.index,
@@ -1571,6 +1575,7 @@ static void first_person_weapon_vr_arms(
boolean gripping;
boolean left_free;
boolean rigid = !halo_vr_free_arm_bends();
int body = halo_vr_body_mode();
float hand_position[3], hand_forward[3], hand_up[3];
if (vr_arm_nodes.graph_index != graph_index)
@@ -1692,6 +1697,9 @@ static void first_person_weapon_vr_arms(
reference[2] = dot_product3d(&between, &weapon_camera->up);
gripping = halo_vr_two_hand(local_player_index, foregrip, reference);
}
/* (the weapon alone: no hands to pose) */
if (body == HALO_VR_BODY_OFF)
return;
if (!gripping && vr_arm_nodes.wrist[0] != NONE)
{
if (halo_vr_left_hand(local_player_index, hand_position, hand_forward, hand_up))
@@ -1723,6 +1731,12 @@ static void first_person_weapon_vr_arms(
pole.k = -global_up3d->k;
vr_vector_scale_add(&pole, &left, outward * 0.6f);
vr_vector_scale_add(&pole, &forward, -0.3f);
bones[_vr_arm_upper] = vr_arm_nodes.upper[side];
bones[_vr_arm_fore] = vr_arm_nodes.fore[side];
bones[_vr_arm_hand] = vr_arm_nodes.wrist[side];
/* (the hands alone: the arms are shrunk away below) */
if (body == HALO_VR_BODY_HANDS)
continue;
if (!side && left_free && rigid)
{
/* the free left arm keeps its shape (vr_left_hand_free), but
@@ -1732,15 +1746,25 @@ static void first_person_weapon_vr_arms(
}
/* the hand stays where it is (at the controller, or on the
weapon): the arm bends to it */
bones[_vr_arm_upper] = vr_arm_nodes.upper[side];
bones[_vr_arm_fore] = vr_arm_nodes.fore[side];
bones[_vr_arm_hand] = vr_arm_nodes.wrist[side];
vr_solve_arm(&vr_arm_skeleton, node_matrices, bones, shoulder, &node_matrices[bones[_vr_arm_hand]].position,
&pole, TRUE, &vr_arm_history[side]);
vr_distribute_arm_twist(node_matrices, bones, &animated_forearm[side], &animated_wrist[side],
&vr_arm_history[side]);
}
first_person_weapon_vr_fingers(local_player_index, node_matrices, left_free, hand_forward, hand_up);
if (body == HALO_VR_BODY_HANDS)
{
for (side = 0; side < 2; side++)
{
short bones[NUMBER_OF_VR_ARM_BONES];
bones[_vr_arm_upper] = vr_arm_nodes.upper[side];
bones[_vr_arm_fore] = vr_arm_nodes.fore[side];
bones[_vr_arm_hand] = vr_arm_nodes.wrist[side];
if (bones[_vr_arm_upper] != NONE && bones[_vr_arm_fore] != NONE && bones[_vr_arm_hand] != NONE)
vr_hide_arm(&vr_arm_skeleton, node_matrices, bones, vr_arm_nodes.fingers[side], vr_arm_nodes.gun);
}
}
}
#endif
+37 -2
View File
@@ -6645,6 +6645,7 @@ they change), and from it a page of what the controllers' buttons do. */
int config_boolean(char const *name);
double config_real(char const *name);
char const *config_string(char const *name);
int config_write(char const *name, char const *value);
int config_write_boolean(char const *name, int value);
@@ -6658,12 +6659,15 @@ enum
_vr_option_laser,
_vr_option_crosshair,
_vr_option_motion_tracker,
_vr_option_body,
_vr_option_fingers,
_vr_option_controls,
_vr_option_quit,
NUMBER_OF_VR_OPTIONS,
VR_OPTIONS_TOP = 98,
VR_OPTIONS_ROW_HEIGHT = 26,
/* (twelve rows between the title and the footer, in the box) */
VR_OPTIONS_TOP = 94,
VR_OPTIONS_ROW_HEIGHT = 22,
VR_OPTIONS_FOOTER = 362,
_vr_controls_on_foot = 0,
@@ -6688,6 +6692,23 @@ static real const vr_snap_angles[] = { 30.0f, 45.0f, 60.0f, 90.0f };
static real const vr_smooth_speeds[] = { 60.0f, 90.0f, 120.0f, 150.0f, 180.0f };
static real const vr_vignettes[] = { 0.0f, 0.35f, 0.7f, 1.0f };
static char const *const vr_vignette_names[] = { "Off", "Light", "Medium", "Strong" };
/* vr.body's values, and as the panel shows them */
static char const *const vr_bodies[] = { "arms", "hands", "off" };
static char const *const vr_body_names[] = { "Arms and hands", "Hands only", "Weapon only" };
static short ui_vr_options_body(
void)
{
char const *body = config_string("vr.body");
short index;
for (index = 0; index < (short)NUMBEROF(vr_bodies); index++)
{
if (body && strcmp(body, vr_bodies[index]) == 0)
return index;
}
return 0;
}
static boolean ui_vr_options_screen(
void)
@@ -6753,6 +6774,11 @@ static void ui_vr_options_change(
case _vr_option_motion_tracker:
config_write_boolean("vr.motion_tracker_on_wrist", !config_boolean("vr.motion_tracker_on_wrist"));
break;
case _vr_option_body:
config_write("vr.body", vr_bodies[(ui_vr_options_body() + change + (short)NUMBEROF(vr_bodies)) %
(short)NUMBEROF(vr_bodies)]);
break;
case _vr_option_fingers: config_write_boolean("vr.fingers", !config_boolean("vr.fingers")); break;
case _vr_option_controls:
ui_vr_options_globals.controls = TRUE;
ui_vr_options_globals.page = _vr_controls_on_foot;
@@ -7179,6 +7205,15 @@ static void ui_vr_options_render(
label = "Motion tracker";
snprintf(value, sizeof(value), "%s", config_boolean("vr.motion_tracker_on_wrist") ? "On the wrist" : "In the HUD");
break;
case _vr_option_body:
label = "Body";
snprintf(value, sizeof(value), "%s", vr_body_names[ui_vr_options_body()]);
break;
case _vr_option_fingers:
label = "Fingers";
snprintf(value, sizeof(value), "%s", config_boolean("vr.fingers") ? "Tracked" : "Off");
unused = ui_vr_options_body() == 2;
break;
case _vr_option_controls:
label = "Controller buttons...";
break;