From 3e2ab4b42cad1ab7d9a49def1447d9cead700ba6 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Sun, 15 Jan 2023 10:46:22 +0100 Subject: [PATCH] Add readme and few attempts at radar fixes --- Cargo.toml | 2 +- README.md | 26 ++++++ rFactorOverlay.json | 2 +- src/overlay/rfactor_data.rs | 153 ++++++++++++++++++++++++++---------- 4 files changed, 138 insertions(+), 45 deletions(-) create mode 100644 README.md diff --git a/Cargo.toml b/Cargo.toml index 0a7c9ad..557798d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,5 +13,5 @@ vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } anyhow = { version = "1.0.68", features = ["backtrace"] } -cgmath = "0.18.0" +cgmath = { version = "0.18.0", features = ["swizzle"] } paste = "1.0.11" diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f8f1d2 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Vulkan rFactor2 HUD +This project is an attempt to render custom HUD elements for rFactor2 by being a Vulkan layer. + +# Current state +I just started doing it. That means it isn't very usable right now. I'm working on a radar right now as the first element. + +# How to enable +* Build this repository `cargo build --release` +* Change the path where the `libvk_layer_rs.so` is located +* Put the rFactorOverlay.json into a layer directory ([layer directories](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-linux-layer-discovery)) +* Add `RFACTOR_HUD=1` to steam launch command (example: `RFACTOR_HUD=1 %command%`) + +# Resources + +## Vulkan Layer +* [Sample Layer](https://github.com/baldurk/sample_layer) +* [Brief guide to Vulkan layers (Renderdoc)](https://renderdoc.org/vulkan-layer-guide.html) +* [Mangohud](https://github.com/flightlessmango/MangoHud) +* [Lunarg Guide](https://vulkan.lunarg.com/doc/view/1.3.216.0/mac/loader_and_layer_interface.html#user-content-layer-manifest-file-format) + +## rFactor2 Shared Memory +* [rFactor 2 Modding Resources](https://www.studio-397.com/modding-resources/) +* [rF2SharedMemoryMapPlugin](https://github.com/TheIronWolfModding/rF2SharedMemoryMapPlugin) +* [rF2SharedMemoryMapPlugin_Wine](https://github.com/schlegp/rF2SharedMemoryMapPlugin_Wine) +* [CrewChief](https://github.com/mrbelowski/CrewChiefV4) +* [OpenSimHud](https://github.com/schlegp/OpenSimHud) diff --git a/rFactorOverlay.json b/rFactorOverlay.json index 10ea072..f49349b 100644 --- a/rFactorOverlay.json +++ b/rFactorOverlay.json @@ -4,7 +4,7 @@ "name": "VK_LAYER_rFactor2_overlay", "type": "GLOBAL", "api_version": "1.3.0", - "library_path": "/home/michael/Dokumente/Workspace/vk_layer_rs/target/debug/libvk_layer_rs.so", + "library_path": "$HOME/Dokumente/Workspace/vk_layer_rs/target/debug/libvk_layer_rs.so", "implementation_version": "1", "description": "Vulkan Hud Overlay", "functions": { diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index fba4836..d747228 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpace}; +use cgmath::{ortho, vec2, vec3, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; @@ -37,14 +37,17 @@ pub struct RFactorData { player_car: RadarObject, cars: Vec, + // buffer car objects, to prevent recreating them every update + car_handles: Vec, + // game info player_id: Option, // math objects radar_center: Vector2, ortho: Matrix4, - window_width: u32, - window_height: u32, + _window_width: u32, + _window_height: u32, radar_extent: f32, car_width: f32, car_height: f32, @@ -108,16 +111,17 @@ impl RFactorData { vec2(radar_center.x + car_width, radar_center.y - car_height), ], ), - [0.9, 0.9, 0.0, 0.9], + [0.0, 0.9, 0.0, 0.9], )?, cars: Vec::new(), + car_handles: Vec::new(), player_id: None, radar_center, ortho, - window_width: width, - window_height: height, + _window_width: width, + _window_height: height, radar_extent, car_width, car_height, @@ -135,31 +139,47 @@ impl RFactorData { RadarObject::new( self.device.clone(), &self.descriptor_layout, - PositionOnlyVertex::from_2d_corners( + Self::create_car_vertices( self.ortho, - [ - vec2( - self.radar_center.x - self.car_width + offset.x, - self.radar_center.y - self.car_height + offset.y, - ), - vec2( - self.radar_center.x - self.car_width + offset.x, - self.radar_center.y + self.car_height + offset.y, - ), - vec2( - self.radar_center.x + self.car_width + offset.x, - self.radar_center.y + self.car_height + offset.y, - ), - vec2( - self.radar_center.x + self.car_width + offset.x, - self.radar_center.y - self.car_height + offset.y, - ), - ], + self.radar_center, + self.car_width, + self.car_height, + offset, ), color, ) } + fn create_car_vertices( + mvp: Matrix4, + radar_center: Vector2, + car_width: f32, + car_height: f32, + offset: Vector2, + ) -> [PositionOnlyVertex; 6] { + PositionOnlyVertex::from_2d_corners( + mvp, + [ + vec2( + radar_center.x - car_width + offset.x, + radar_center.y - car_height + offset.y, + ), + vec2( + radar_center.x - car_width + offset.x, + radar_center.y + car_height + offset.y, + ), + vec2( + radar_center.x + car_width + offset.x, + radar_center.y + car_height + offset.y, + ), + vec2( + radar_center.x + car_width + offset.x, + radar_center.y - car_height + offset.y, + ), + ], + ) + } + fn now(&self) -> f32 { self.start_time.elapsed().as_secs_f32() } @@ -196,25 +216,42 @@ impl RFactorData { if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { write_log!("new telemetry update"); + // make sure there are enough cars in buffer + if self.car_handles.len() < telemetries.len() { + let size_diff = telemetries.len() - self.car_handles.len(); + + for _ in 0..size_diff { + self.car_handles + .push(self.create_car_object(vec2(0.0, 0.0), [0.0, 0.0, 0.0, 0.0])?); + } + } + let mut player_position = CarPosition::default(); let mut other_positions = Vec::new(); for telemetry in telemetries { if telemetry.id == *player_id { player_position.position = convert_vec(telemetry.position); - player_position.local_rotation = convert_vec(telemetry.local_rotation); + player_position.orientation = [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ]; } else { other_positions.push(CarPosition { position: convert_vec(telemetry.position), - local_rotation: convert_vec(telemetry.local_rotation), + orientation: [ + convert_vec(telemetry.orientation[0]), + convert_vec(telemetry.orientation[1]), + convert_vec(telemetry.orientation[2]), + ], }); } } // update radar objects - - // naive way: clear cars and create them new if near enough self.cars.clear(); + let mut buffer_car_index = 0; for other_position in other_positions { let diff = player_position.position - other_position.position; @@ -222,18 +259,22 @@ impl RFactorData { // check if car is close enough the players car if distance < self.config.radar_car_distance { - let distance_ratio = distance / self.config.radar_car_distance; - let offset = - diff.truncate() * (self.radar_extent / self.config.radar_car_distance); - let color = self - .config - .danger_color - .lerp(self.config.safe_color, distance_ratio); + diff.xz() * (self.radar_extent / self.config.radar_car_distance); - self.cars.push( - self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?, - ); + let buffered_car = self.car_handles[buffer_car_index].clone(); + buffer_car_index += 1; + buffered_car.update( + self.ortho, + offset, + Self::car_orientation(&other_position), + self.radar_center, + self.car_width, + self.car_height, + [0.9, 0.9, 0.0, 0.9], + )?; + + self.cars.push(buffered_car); } } } @@ -248,7 +289,7 @@ impl RFactorData { let mut objects: Vec<&dyn RenderObject> = Vec::new(); // only draw radar when player is loaded into a map - if let Some(player_id) = &self.player_id { + if let Some(_player_id) = &self.player_id { // only draw radar when any car is near enough if !self.cars.is_empty() { objects.push(&self.background); @@ -263,8 +304,17 @@ impl RFactorData { objects } + + fn car_orientation(car: &CarPosition) -> Rad { + const DEGREES_IN_RADIAN: f32 = 57.2957795; + + let xz_val = car.orientation[2].xz(); + + Rad(xz_val.x.atan2(xz_val.y) * DEGREES_IN_RADIAN) + } } +#[derive(Clone)] struct RadarObject { descriptor_set: Arc, @@ -308,7 +358,24 @@ impl RadarObject { }) } - pub fn update_color(&self, color: [f32; 4]) -> Result<()> { + pub fn update( + &self, + ortho: Matrix4, + offset: Vector2, + rotation: impl Into>, + radar_center: Vector2, + car_width: f32, + car_height: f32, + color: [f32; 4], + ) -> Result<()> { + self.position_buffer + .fill(&RFactorData::create_car_vertices( + ortho * Matrix4::from_angle_z(rotation.into()), + radar_center, + car_width, + car_height, + offset, + ))?; self.color_buffer.fill(&color) } } @@ -325,14 +392,14 @@ impl RenderObject for RadarObject { struct CarPosition { pub position: Vector3, - pub local_rotation: Vector3, + pub orientation: [Vector3; 3], } impl Default for CarPosition { fn default() -> Self { Self { position: vec3(0.0, 0.0, 0.0), - local_rotation: vec3(0.0, 0.0, 0.0), + orientation: [vec3(0.0, 0.0, 0.0); 3], } } }