Add readme and few attempts at radar fixes
This commit is contained in:
parent
d1c711586b
commit
7ad2f4eab9
4 changed files with 138 additions and 45 deletions
|
@ -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" }
|
rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" }
|
||||||
|
|
||||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||||
cgmath = "0.18.0"
|
cgmath = { version = "0.18.0", features = ["swizzle"] }
|
||||||
paste = "1.0.11"
|
paste = "1.0.11"
|
||||||
|
|
26
README.md
Normal file
26
README.md
Normal file
|
@ -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)
|
|
@ -4,7 +4,7 @@
|
||||||
"name": "VK_LAYER_rFactor2_overlay",
|
"name": "VK_LAYER_rFactor2_overlay",
|
||||||
"type": "GLOBAL",
|
"type": "GLOBAL",
|
||||||
"api_version": "1.3.0",
|
"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",
|
"implementation_version": "1",
|
||||||
"description": "Vulkan Hud Overlay",
|
"description": "Vulkan Hud Overlay",
|
||||||
"functions": {
|
"functions": {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Result;
|
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 rfactor_sm_reader::*;
|
||||||
use vulkan_rs::prelude::*;
|
use vulkan_rs::prelude::*;
|
||||||
|
|
||||||
|
@ -37,14 +37,17 @@ pub struct RFactorData {
|
||||||
player_car: RadarObject,
|
player_car: RadarObject,
|
||||||
cars: Vec<RadarObject>,
|
cars: Vec<RadarObject>,
|
||||||
|
|
||||||
|
// buffer car objects, to prevent recreating them every update
|
||||||
|
car_handles: Vec<RadarObject>,
|
||||||
|
|
||||||
// game info
|
// game info
|
||||||
player_id: Option<i32>,
|
player_id: Option<i32>,
|
||||||
|
|
||||||
// math objects
|
// math objects
|
||||||
radar_center: Vector2<f32>,
|
radar_center: Vector2<f32>,
|
||||||
ortho: Matrix4<f32>,
|
ortho: Matrix4<f32>,
|
||||||
window_width: u32,
|
_window_width: u32,
|
||||||
window_height: u32,
|
_window_height: u32,
|
||||||
radar_extent: f32,
|
radar_extent: f32,
|
||||||
car_width: f32,
|
car_width: f32,
|
||||||
car_height: f32,
|
car_height: f32,
|
||||||
|
@ -108,16 +111,17 @@ impl RFactorData {
|
||||||
vec2(radar_center.x + car_width, radar_center.y - car_height),
|
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(),
|
cars: Vec::new(),
|
||||||
|
car_handles: Vec::new(),
|
||||||
|
|
||||||
player_id: None,
|
player_id: None,
|
||||||
|
|
||||||
radar_center,
|
radar_center,
|
||||||
ortho,
|
ortho,
|
||||||
window_width: width,
|
_window_width: width,
|
||||||
window_height: height,
|
_window_height: height,
|
||||||
radar_extent,
|
radar_extent,
|
||||||
car_width,
|
car_width,
|
||||||
car_height,
|
car_height,
|
||||||
|
@ -135,31 +139,47 @@ impl RFactorData {
|
||||||
RadarObject::new(
|
RadarObject::new(
|
||||||
self.device.clone(),
|
self.device.clone(),
|
||||||
&self.descriptor_layout,
|
&self.descriptor_layout,
|
||||||
PositionOnlyVertex::from_2d_corners(
|
Self::create_car_vertices(
|
||||||
self.ortho,
|
self.ortho,
|
||||||
[
|
self.radar_center,
|
||||||
vec2(
|
self.car_width,
|
||||||
self.radar_center.x - self.car_width + offset.x,
|
self.car_height,
|
||||||
self.radar_center.y - self.car_height + offset.y,
|
offset,
|
||||||
),
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
color,
|
color,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_car_vertices(
|
||||||
|
mvp: Matrix4<f32>,
|
||||||
|
radar_center: Vector2<f32>,
|
||||||
|
car_width: f32,
|
||||||
|
car_height: f32,
|
||||||
|
offset: Vector2<f32>,
|
||||||
|
) -> [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 {
|
fn now(&self) -> f32 {
|
||||||
self.start_time.elapsed().as_secs_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()) {
|
if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) {
|
||||||
write_log!("new telemetry update");
|
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 player_position = CarPosition::default();
|
||||||
let mut other_positions = Vec::new();
|
let mut other_positions = Vec::new();
|
||||||
|
|
||||||
for telemetry in telemetries {
|
for telemetry in telemetries {
|
||||||
if telemetry.id == *player_id {
|
if telemetry.id == *player_id {
|
||||||
player_position.position = convert_vec(telemetry.position);
|
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 {
|
} else {
|
||||||
other_positions.push(CarPosition {
|
other_positions.push(CarPosition {
|
||||||
position: convert_vec(telemetry.position),
|
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
|
// update radar objects
|
||||||
|
|
||||||
// naive way: clear cars and create them new if near enough
|
|
||||||
self.cars.clear();
|
self.cars.clear();
|
||||||
|
let mut buffer_car_index = 0;
|
||||||
|
|
||||||
for other_position in other_positions {
|
for other_position in other_positions {
|
||||||
let diff = player_position.position - other_position.position;
|
let diff = player_position.position - other_position.position;
|
||||||
|
@ -222,18 +259,22 @@ impl RFactorData {
|
||||||
|
|
||||||
// check if car is close enough the players car
|
// check if car is close enough the players car
|
||||||
if distance < self.config.radar_car_distance {
|
if distance < self.config.radar_car_distance {
|
||||||
let distance_ratio = distance / self.config.radar_car_distance;
|
|
||||||
|
|
||||||
let offset =
|
let offset =
|
||||||
diff.truncate() * (self.radar_extent / self.config.radar_car_distance);
|
diff.xz() * (self.radar_extent / self.config.radar_car_distance);
|
||||||
let color = self
|
|
||||||
.config
|
|
||||||
.danger_color
|
|
||||||
.lerp(self.config.safe_color, distance_ratio);
|
|
||||||
|
|
||||||
self.cars.push(
|
let buffered_car = self.car_handles[buffer_car_index].clone();
|
||||||
self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?,
|
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();
|
let mut objects: Vec<&dyn RenderObject> = Vec::new();
|
||||||
|
|
||||||
// only draw radar when player is loaded into a map
|
// 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
|
// only draw radar when any car is near enough
|
||||||
if !self.cars.is_empty() {
|
if !self.cars.is_empty() {
|
||||||
objects.push(&self.background);
|
objects.push(&self.background);
|
||||||
|
@ -263,8 +304,17 @@ impl RFactorData {
|
||||||
|
|
||||||
objects
|
objects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn car_orientation(car: &CarPosition) -> Rad<f32> {
|
||||||
|
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 {
|
struct RadarObject {
|
||||||
descriptor_set: Arc<DescriptorSet>,
|
descriptor_set: Arc<DescriptorSet>,
|
||||||
|
|
||||||
|
@ -308,7 +358,24 @@ impl RadarObject {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_color(&self, color: [f32; 4]) -> Result<()> {
|
pub fn update(
|
||||||
|
&self,
|
||||||
|
ortho: Matrix4<f32>,
|
||||||
|
offset: Vector2<f32>,
|
||||||
|
rotation: impl Into<Deg<f32>>,
|
||||||
|
radar_center: Vector2<f32>,
|
||||||
|
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)
|
self.color_buffer.fill(&color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,14 +392,14 @@ impl RenderObject for RadarObject {
|
||||||
|
|
||||||
struct CarPosition {
|
struct CarPosition {
|
||||||
pub position: Vector3<f32>,
|
pub position: Vector3<f32>,
|
||||||
pub local_rotation: Vector3<f32>,
|
pub orientation: [Vector3<f32>; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CarPosition {
|
impl Default for CarPosition {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
position: vec3(0.0, 0.0, 0.0),
|
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],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue