Merge initial implementation of Pedal and Leader Board widgets #3
11 changed files with 191 additions and 75 deletions
5
.vscode/tasks.json
vendored
5
.vscode/tasks.json
vendored
|
@ -4,11 +4,14 @@
|
|||
{
|
||||
"type": "cargo",
|
||||
"command": "build",
|
||||
"args": [
|
||||
"--release"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$rustc"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "rust: cargo build --release"
|
||||
"label": "rust: cargo build"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
|
|
BIN
font.png
Normal file
BIN
font.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
|
@ -1,6 +1,8 @@
|
|||
mod pedals;
|
||||
mod pipeline;
|
||||
mod radar;
|
||||
mod watermark;
|
||||
|
||||
pub use pedals::*;
|
||||
pub use radar::*;
|
||||
pub use watermark::*;
|
||||
|
|
|
@ -11,6 +11,8 @@ pub struct Pedals {
|
|||
|
||||
brake: Arc<ProgressBar>,
|
||||
throttle: Arc<ProgressBar>,
|
||||
fuel: Arc<Label>,
|
||||
gear: Arc<Label>,
|
||||
}
|
||||
|
||||
impl Pedals {
|
||||
|
@ -21,11 +23,15 @@ impl Pedals {
|
|||
|
||||
let brake = gui.element("brake")?;
|
||||
let throttle = gui.element("throttle")?;
|
||||
let fuel = gui.element("fuel")?;
|
||||
let gear = gui.element("gear")?;
|
||||
|
||||
Ok(Self {
|
||||
gui,
|
||||
brake,
|
||||
throttle,
|
||||
fuel,
|
||||
gear,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -36,11 +42,7 @@ impl Drop for Pedals {
|
|||
}
|
||||
}
|
||||
|
||||
impl UiOverlay for Pedals {
|
||||
fn enable_ui(&mut self) -> Result<()> {
|
||||
self.gui.enable()
|
||||
}
|
||||
}
|
||||
impl UiOverlay for Pedals {}
|
||||
|
||||
impl DataReceiver for Pedals {
|
||||
fn scoring_update(&mut self, _vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()> {
|
||||
|
@ -52,12 +54,22 @@ impl DataReceiver for Pedals {
|
|||
player_id: Option<i32>,
|
||||
telemetries: &[rF2VehicleTelemetry],
|
||||
) -> Result<()> {
|
||||
if let Some(id) = player_id {
|
||||
if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) {
|
||||
self.brake
|
||||
.set_progress(telemetry.unfiltered_throttle as f32)?;
|
||||
self.throttle
|
||||
.set_progress(telemetry.unfiltered_throttle as f32)?;
|
||||
match player_id {
|
||||
Some(id) => {
|
||||
self.gui.enable()?;
|
||||
|
||||
if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) {
|
||||
self.brake
|
||||
.set_progress(telemetry.unfiltered_throttle as f32)?;
|
||||
self.throttle
|
||||
.set_progress(telemetry.unfiltered_throttle as f32)?;
|
||||
self.gear.set_text(telemetry.gear)?;
|
||||
let fuel = telemetry.fuel;
|
||||
self.fuel.set_text(format!("{:.2}", fuel))?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.gui.disable()?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,9 +92,6 @@ pub struct Radar {
|
|||
// buffer car objects, to prevent recreating them every update
|
||||
car_handles: Vec<RadarObject>,
|
||||
|
||||
// game info
|
||||
player_id: Option<i32>,
|
||||
|
||||
// math objects
|
||||
radar_center: Vector2<f32>,
|
||||
ortho: Matrix4<f32>,
|
||||
|
@ -109,8 +106,6 @@ pub struct Radar {
|
|||
|
||||
pipeline: SingleColorPipeline,
|
||||
render_target: RenderTarget,
|
||||
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl Radar {
|
||||
|
@ -120,8 +115,6 @@ impl Radar {
|
|||
queue: Arc<Mutex<Queue>>,
|
||||
rendering: &Rendering,
|
||||
) -> Result<Self> {
|
||||
write_log!(" =================== create RFactorData ===================");
|
||||
|
||||
let radar_extent = rendering.swapchain().width() as f32 * 0.075 * config.radar_scale;
|
||||
let car_height = radar_extent * config.height_scale;
|
||||
let car_width = car_height * config.width_scale;
|
||||
|
@ -198,8 +191,6 @@ impl Radar {
|
|||
cars: Vec::new(),
|
||||
car_handles: Vec::new(),
|
||||
|
||||
player_id: None,
|
||||
|
||||
radar_center,
|
||||
ortho,
|
||||
_window_width: rendering.swapchain().width(),
|
||||
|
@ -213,8 +204,6 @@ impl Radar {
|
|||
|
||||
render_target,
|
||||
pipeline,
|
||||
|
||||
enabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -285,33 +274,26 @@ impl Radar {
|
|||
|
||||
let mut objects = Vec::new();
|
||||
|
||||
// only draw radar when player is loaded into a map
|
||||
if let Some(_player_id) = &self.player_id {
|
||||
// only draw radar when any car is near enough
|
||||
if !self.cars.is_empty() {
|
||||
if let Some(background) = &self.background {
|
||||
objects.push(background);
|
||||
}
|
||||
|
||||
for other_player_cars in &self.cars {
|
||||
objects.push(other_player_cars);
|
||||
}
|
||||
|
||||
objects.push(&self.player_car);
|
||||
// only draw radar when any car is near enough
|
||||
if !self.cars.is_empty() {
|
||||
if let Some(background) = &self.background {
|
||||
objects.push(background);
|
||||
}
|
||||
|
||||
for other_player_cars in &self.cars {
|
||||
objects.push(other_player_cars);
|
||||
}
|
||||
|
||||
objects.push(&self.player_car);
|
||||
}
|
||||
|
||||
write_log!(format!("obj count {}", objects.len()));
|
||||
|
||||
objects
|
||||
}
|
||||
}
|
||||
|
||||
impl UiOverlay for Radar {
|
||||
fn enable_ui(&mut self) -> Result<()> {
|
||||
self.enabled = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl UiOverlay for Radar {}
|
||||
|
||||
impl DataReceiver for Radar {
|
||||
fn scoring_update(&mut self, _vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()> {
|
||||
|
@ -323,11 +305,10 @@ impl DataReceiver for Radar {
|
|||
player_id: Option<i32>,
|
||||
telemetries: &[rF2VehicleTelemetry],
|
||||
) -> Result<()> {
|
||||
self.cars.clear();
|
||||
write_log!(" ============================ Radar telemetry udpate ======================");
|
||||
write_log!(format!("player id {:?}", player_id));
|
||||
|
||||
if !self.enabled {
|
||||
return Ok(());
|
||||
}
|
||||
self.cars.clear();
|
||||
|
||||
if let Some(player_id) = player_id {
|
||||
// make sure there are enough cars in buffer
|
||||
|
@ -389,6 +370,8 @@ impl DataReceiver for Radar {
|
|||
}
|
||||
}
|
||||
|
||||
write_log!(format!("other cars: {:?}", self.cars.len()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,14 @@
|
|||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="direction">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="left_to_right"></xs:enumeration>
|
||||
<xs:enumeration value="bottom_to_top"></xs:enumeration>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="background" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="foreground" type="xs:string"></xs:attribute>
|
||||
<xs:attribute name="button_normal" type="xs:string"></xs:attribute>
|
||||
|
@ -200,6 +208,7 @@
|
|||
<xs:attribute ref="text_alignment"></xs:attribute>
|
||||
<xs:attribute ref="background"></xs:attribute>
|
||||
<xs:attribute ref="foreground"></xs:attribute>
|
||||
<xs:attribute ref="direction"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="2560" reference_height="1440">
|
||||
<grid x_dim="5" y_dim="2" x_offset="-600" y_offset="-350" width="250" height="300"
|
||||
vert_align="bottom" hori_align="right" margin="10" padding="10" background="#686868">
|
||||
<grid x_dim="7" y_dim="2" x_offset="-750" y_offset="-250" width="150" height="200"
|
||||
vert_align="bottom" hori_align="right" margin="3" padding="3" background="#686868">
|
||||
<progressbar id="brake" x_slot="0" y_slot="0" y_size="2" background="#f44444"
|
||||
foreground="#e30000"></progressbar>
|
||||
<progressbar id="throttle" x_slot="1" y_slot="0"
|
||||
y_size="2" background="#51fd51"
|
||||
direction="bottom_to_top" foreground="#e30000"></progressbar>
|
||||
<progressbar id="throttle"
|
||||
x_slot="1" y_slot="0" y_size="2" background="#51fd51" direction="bottom_to_top"
|
||||
foreground="#00b900"></progressbar>
|
||||
<label id="fuel" x_slot="2"
|
||||
x_size="5" y_slot="0"
|
||||
text_color="black"></label>
|
||||
<label id="gear" x_slot="2" x_size="5" y_slot="1"
|
||||
text_color="black"></label>
|
||||
</grid>
|
||||
</root>
|
8
src/overlay/elements/ui_files/watermark.xml
Normal file
8
src/overlay/elements/ui_files/watermark.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml-model href="gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<root reference_width="2560" reference_height="1440">
|
||||
<grid x_dim="1" y_dim="1" x_offset="10" y_offset="10" width="300" height="50"
|
||||
vert_align="top" hori_align="left" margin="2" padding="2" background="#c9c9c9">
|
||||
<label x_slot="0" y_slot="0" text_color="#c9c9c9" text_ratio="1.0"
|
||||
background="black">Vulkan Overlay Enabled</label>
|
||||
</grid>
|
||||
</root>
|
45
src/overlay/elements/watermark.rs
Normal file
45
src/overlay/elements/watermark.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01};
|
||||
use ui::prelude::*;
|
||||
|
||||
use crate::overlay::{rfactor_data::DataReceiver, UiOverlay};
|
||||
|
||||
pub struct Watermark {
|
||||
gui: Arc<GuiBuilder>,
|
||||
}
|
||||
|
||||
impl Watermark {
|
||||
pub fn new(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
|
||||
const DESC: &str = include_str!("ui_files/watermark.xml");
|
||||
|
||||
let gui = GuiBuilder::from_str(gui_handler, DESC)?;
|
||||
|
||||
gui.enable()?;
|
||||
|
||||
Ok(Self { gui })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Watermark {
|
||||
fn drop(&mut self) {
|
||||
self.gui.disable().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl UiOverlay for Watermark {}
|
||||
|
||||
impl DataReceiver for Watermark {
|
||||
fn scoring_update(&mut self, _vehicle_scoring: &[VehicleScoringInfoV01]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn telemetry_update(
|
||||
&mut self,
|
||||
_player_id: Option<i32>,
|
||||
_telemetries: &[rF2VehicleTelemetry],
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -23,12 +23,30 @@ use elements::*;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub trait UiOverlay: DataReceiver {
|
||||
fn enable_ui(&mut self) -> Result<()>;
|
||||
pub trait UiOverlay: DataReceiver {}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct UiSelectorConfig {
|
||||
pub enable_watermark: bool,
|
||||
|
||||
pub enable_radar: bool,
|
||||
pub enable_pedals: bool,
|
||||
}
|
||||
|
||||
impl UiSelectorConfig {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
enable_watermark: true,
|
||||
enable_radar: true,
|
||||
enable_pedals: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct OverlayConfig {
|
||||
pub ui_config: UiSelectorConfig,
|
||||
|
||||
pub radar_config: RadarConfig,
|
||||
pub font_path: String,
|
||||
}
|
||||
|
@ -36,6 +54,7 @@ pub struct OverlayConfig {
|
|||
impl OverlayConfig {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ui_config: UiSelectorConfig::new(),
|
||||
radar_config: RadarConfig::new(),
|
||||
font_path: String::new(),
|
||||
}
|
||||
|
@ -74,6 +93,8 @@ impl Overlay {
|
|||
|
||||
pub fn set_config(&mut self, config: OverlayConfig) {
|
||||
self.config = config;
|
||||
|
||||
self.config.font_path = "/opt/sata_ssd/Workspace/vk_layer_rs/font.png".to_string();
|
||||
}
|
||||
|
||||
pub fn set_instance(&mut self, instance: Arc<Instance>) {
|
||||
|
@ -123,8 +144,12 @@ impl Overlay {
|
|||
|
||||
// only font is used
|
||||
let mut create_info = GuiHandlerCreateInfo::default();
|
||||
create_info.font_path = AssetPath::from(self.config.font_path.clone());
|
||||
// create_info.font_path = AssetPath::from(self.config.font_path.clone());
|
||||
create_info.font_path = AssetPath::from("/opt/sata_ssd/Workspace/vk_layer_rs/font.png");
|
||||
create_info.font_path.assume_prefix_free();
|
||||
// required to not crash
|
||||
create_info.resource_directory = AssetPath::from("");
|
||||
create_info.resource_directory.assume_prefix_free();
|
||||
|
||||
// provide trait required by GuiHandler
|
||||
let ctx = Arc::new(ContextImpl::new(
|
||||
|
@ -135,28 +160,51 @@ impl Overlay {
|
|||
));
|
||||
|
||||
// create GuiHandler
|
||||
write_log!(format!("Create Info: \n{:#?}", create_info));
|
||||
|
||||
let gui_handler = GuiHandler::new(create_info, &(ctx as Arc<dyn ContextInterface>))?;
|
||||
write_log!("GuiHandler successfully created");
|
||||
|
||||
// create ui elements
|
||||
|
||||
// create watermark
|
||||
if self.config.ui_config.enable_watermark {
|
||||
let watermark = Rc::new(RefCell::new(Watermark::new(&gui_handler)?));
|
||||
|
||||
self.ui_elements.push(watermark);
|
||||
|
||||
write_log!("Watermark successfully created");
|
||||
}
|
||||
|
||||
// create radar
|
||||
let radar = Rc::new(RefCell::new(Radar::new(
|
||||
self.config.radar_config,
|
||||
self.device(),
|
||||
self.queue(),
|
||||
&rendering,
|
||||
)?));
|
||||
if self.config.ui_config.enable_radar {
|
||||
let radar = Rc::new(RefCell::new(Radar::new(
|
||||
self.config.radar_config,
|
||||
self.device(),
|
||||
self.queue(),
|
||||
&rendering,
|
||||
)?));
|
||||
|
||||
rendering.add_render_callback({
|
||||
let radar = radar.clone();
|
||||
|
||||
move |index| radar.borrow().render(index)
|
||||
});
|
||||
|
||||
self.ui_elements.push(radar);
|
||||
|
||||
write_log!("Radar successfully created");
|
||||
}
|
||||
|
||||
// create pedals
|
||||
let pedals = Rc::new(RefCell::new(Pedals::new(&gui_handler)?));
|
||||
if self.config.ui_config.enable_pedals {
|
||||
let pedals = Rc::new(RefCell::new(Pedals::new(&gui_handler)?));
|
||||
self.ui_elements.push(pedals);
|
||||
|
||||
write_log!("Pedals successfully created");
|
||||
}
|
||||
|
||||
// add rendering callbacks
|
||||
rendering.add_render_callback({
|
||||
let radar = radar.clone();
|
||||
|
||||
move |index| radar.borrow().render(index)
|
||||
});
|
||||
|
||||
rendering.add_render_callback({
|
||||
let gui_handler = gui_handler.clone();
|
||||
let device = self.device();
|
||||
|
@ -179,15 +227,13 @@ impl Overlay {
|
|||
}
|
||||
});
|
||||
|
||||
// add ui elements to local list
|
||||
self.ui_elements.push(radar);
|
||||
self.ui_elements.push(pedals);
|
||||
|
||||
write_log!("-> create rendering: end");
|
||||
write_log!("render callbacks added");
|
||||
|
||||
self.rendering = Some(rendering);
|
||||
self.gui_handler = Some(gui_handler);
|
||||
|
||||
write_log!("-> create rendering: end");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -199,13 +245,11 @@ impl Overlay {
|
|||
write_log!("created RFactorData");
|
||||
|
||||
for receiver in self.ui_elements.iter() {
|
||||
receiver.borrow_mut().enable_ui()?;
|
||||
data.add_receiver(receiver.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check twice for rfactor data, because of borrowing rules
|
||||
if let Some(rfactor) = &mut self.rfactor_data {
|
||||
rfactor.update()?;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,11 @@ impl Rendering {
|
|||
.map(|c| c(image_index))
|
||||
.collect::<Result<Vec<Arc<CommandBuffer>>>>()?;
|
||||
|
||||
write_log!(format!(
|
||||
"submitting {} commandbuffer(s)",
|
||||
command_buffers.len()
|
||||
));
|
||||
|
||||
let queue = self.queue.lock().unwrap();
|
||||
queue.minimal_submit(Duration::from_secs(10), &command_buffers)?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue