use anyhow::Result; use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpace}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; use std::{sync::Arc, time::Instant}; use super::rendering::PositionOnlyVertex; use crate::write_log; fn convert_vec(v: rF2Vec3) -> Vector3 { vec3(v.x as f32, v.y as f32, v.z as f32) } pub trait RenderObject { fn descriptor(&self) -> &Arc; fn buffer(&self) -> &Arc>; } pub struct DataConfig { pub radar_scale: f32, pub radar_car_distance: f32, pub safe_color: Vector3, pub danger_color: Vector3, } pub struct RFactorData { // config config: DataConfig, // rf2 memory mapped data telemetry_reader: TelemetryReader, scoring_reader: ScoringReader, // radar objects background: RadarObject, player_car: RadarObject, cars: Vec, // game info player_id: Option, // math objects radar_center: Vector2, ortho: Matrix4, window_width: u32, window_height: u32, radar_extent: f32, car_width: f32, car_height: f32, start_time: Instant, device: Arc, descriptor_layout: Arc, } impl RFactorData { pub fn new( config: DataConfig, device: Arc, descriptor_layout: &Arc, width: u32, height: u32, ) -> Result { write_log!(" =================== create RFactorData ==================="); let radar_extent = width as f32 * 0.075 * config.radar_scale; let car_height = radar_extent * 0.2; let car_width = car_height * 0.5; let radar_center = vec2( width as f32 / 2.0, height as f32 / 2.0 + height as f32 * 0.25, ); let ortho = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0); let start_time = Instant::now(); Ok(Self { config, telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, background: RadarObject::new( device.clone(), descriptor_layout, PositionOnlyVertex::from_2d_corners( ortho, [ vec2(radar_center.x - radar_extent, radar_center.y - radar_extent), vec2(radar_center.x - radar_extent, radar_center.y + radar_extent), vec2(radar_center.x + radar_extent, radar_center.y + radar_extent), vec2(radar_center.x + radar_extent, radar_center.y - radar_extent), ], ), [0.5, 0.5, 0.5, 0.5], )?, player_car: RadarObject::new( device.clone(), descriptor_layout, PositionOnlyVertex::from_2d_corners( ortho, [ vec2(radar_center.x - car_width, radar_center.y - car_height), vec2(radar_center.x - car_width, radar_center.y + car_height), 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], )?, cars: Vec::new(), player_id: None, radar_center, ortho, window_width: width, window_height: height, radar_extent, car_width, car_height, start_time, device, descriptor_layout: descriptor_layout.clone(), }) } fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { write_log!(" =================== create car object ==================="); RadarObject::new( self.device.clone(), &self.descriptor_layout, PositionOnlyVertex::from_2d_corners( 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, ), ], ), color, ) } fn now(&self) -> f32 { self.start_time.elapsed().as_secs_f32() } pub fn update(&mut self) -> Result<()> { write_log!(" =================== update RFactorData ==================="); // get scoring info if let Some((scoring_info, vehicle_scorings)) = self.scoring_reader.vehicle_scoring(self.now()) { write_log!(format!( "new scoring info: vehicles: {}", scoring_info.mNumVehicles )); // check for player id if scoring_info.mNumVehicles == 0 { self.player_id = None; } else if self.player_id.is_none() { for vehicle_scoring in vehicle_scorings.iter() { if vehicle_scoring.mIsPlayer != 0 { write_log!(format!("player found: {}", vehicle_scoring.mID)); self.player_id = Some(vehicle_scoring.mID); break; } } } } // if player id is set (a map is loaded), check telemetry data if let Some(player_id) = &self.player_id { write_log!("before telemetry update"); if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { write_log!("new telemetry update"); 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); } else { other_positions.push(CarPosition { position: convert_vec(telemetry.position), local_rotation: convert_vec(telemetry.local_rotation), }); } } // update radar objects // naive way: clear cars and create them new if near enough self.cars.clear(); for other_position in other_positions { let diff = player_position.position - other_position.position; let distance = diff.magnitude(); // 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); self.cars.push( self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?, ); } } } } Ok(()) } pub fn objects(&self) -> Vec<&dyn RenderObject> { write_log!(" =================== get objects of 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 { // only draw radar when any car is near enough if !self.cars.is_empty() { objects.push(&self.background); for other_player_cars in &self.cars { objects.push(other_player_cars); } objects.push(&self.player_car); } } objects } } struct RadarObject { descriptor_set: Arc, // uniform buffer color_buffer: Arc>, // vertex buffer position_buffer: Arc>, } impl RadarObject { fn new( device: Arc, descriptor_layout: &Arc, positions: [PositionOnlyVertex; 6], color: [f32; 4], ) -> Result { let color_buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&color) .build(device.clone())?; let position_buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .set_data(&positions) .build(device.clone())?; let descriptor_pool = DescriptorPool::builder() .set_layout(descriptor_layout.clone()) .build(device.clone())?; let descriptor_set = descriptor_pool.prepare_set().allocate()?; descriptor_set.update(&[DescriptorWrite::uniform_buffers(0, &[&color_buffer])])?; Ok(Self { descriptor_set, color_buffer, position_buffer, }) } pub fn update_color(&self, color: [f32; 4]) -> Result<()> { self.color_buffer.fill(&color) } } impl RenderObject for RadarObject { fn descriptor(&self) -> &Arc { &self.descriptor_set } fn buffer(&self) -> &Arc> { &self.position_buffer } } struct CarPosition { pub position: Vector3, pub local_rotation: Vector3, } 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), } } }