diff --git a/port/linux/game/vr_arms.c b/port/linux/game/vr_arms.c index ed5165ff..b14e49b7 100644 --- a/port/linux/game/vr_arms.c +++ b/port/linux/game/vr_arms.c @@ -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(¢re, &knuckles, &back); + along = vr_unit_vector(&back); + } + if (!along) + { + vr_point_minus(&matrices[bones[_vr_arm_fore]].position, ¢re, &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 diff --git a/port/linux/game/vr_arms.h b/port/linux/game/vr_arms.h index 00abbcf4..200581d0 100644 --- a/port/linux/game/vr_arms.h +++ b/port/linux/game/vr_arms.h @@ -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 diff --git a/port/linux/include/halo_vr.h b/port/linux/include/halo_vr.h index a7a9a5c8..0d3c019d 100644 --- a/port/linux/include/halo_vr.h +++ b/port/linux/include/halo_vr.h @@ -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; diff --git a/port/linux/src/port_config.c b/port/linux/src/port_config.c index 5eba9dd3..edd517bb 100644 --- a/port/linux/src/port_config.c +++ b/port/linux/src/port_config.c @@ -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" diff --git a/port/linux/src/vr.c b/port/linux/src/vr.c index 208cb5dd..2dd0a244 100644 --- a/port/linux/src/vr.c +++ b/port/linux/src/vr.c @@ -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) { diff --git a/source/interface/first_person_weapons.c b/source/interface/first_person_weapons.c index 62684e15..5ffab0ab 100644 --- a/source/interface/first_person_weapons.c +++ b/source/interface/first_person_weapons.c @@ -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 diff --git a/source/interface/ui_widget.c b/source/interface/ui_widget.c index 2886047f..9229374d 100644 --- a/source/interface/ui_widget.c +++ b/source/interface/ui_widget.c @@ -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;