diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 88381fe..07abeed 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,6 +9,15 @@ ], "group": "build", "label": "rust: cargo build" - } + }, + { + "type": "shell", + "command": "steam steam://rungameid/365960", + "group": "build", + "label": "Run rFactor2", + "dependsOn": [ + "rust: cargo build" + ] + }, ] } \ No newline at end of file diff --git a/src/enums.rs b/src/enums.rs index 813d5ed..5f701e6 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -64,8 +64,6 @@ create_functions_enum!( [CreateDevice, create_device], [DestroyDevice, destroy_device], [CreateSwapchainKHR, create_swapchain], - [QueueSubmit, submit_queue], - [GetDeviceQueue, get_device_queue], [AcquireNextImageKHR, acquire_next_image], [QueuePresentKHR, present_queue], ); diff --git a/src/lib.rs b/src/lib.rs index 300579f..2700f58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,30 +19,55 @@ use structs::*; use vk_handles::*; use vulkan_rs::prelude::*; +static mut LOG_ENABLED: bool = true; static mut LOG_FILE: String = String::new(); - static mut OVERLAY: Overlay = Overlay::new(); -static mut QUEUE_SUBMIT: PFN_vkQueueSubmit = - unsafe { mem::transmute(vkVoidFunction as *const c_void) }; static mut ACQUIRE_NEXT_IMAGE: PFN_vkAcquireNextImageKHR = unsafe { mem::transmute(vkVoidFunction as *const c_void) }; +pub(crate) fn logging() -> bool { + unsafe { LOG_ENABLED } +} + +macro_rules! write_log { + ($msg:expr) => { + if crate::logging() { + crate::log($msg); + } + }; +} + +pub(crate) use write_log; + #[no_mangle] #[allow(non_snake_case)] pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( pVersionStruct: *mut VkNegotiateLayerInterface, ) -> VkResult { - let home = std::env::var("HOME").unwrap(); - unsafe { - LOG_FILE = format!("{}/rf2_vk_hud.log", home); + let log_enabled = match std::env::var("RFACTOR_HUD_LOG") { + Ok(var) => { + let i: u32 = var.parse().unwrap_or(0); + + i == 1 + } + Err(_) => false, + }; + + unsafe { LOG_ENABLED = log_enabled }; + + if logging() { + let home = std::env::var("HOME").unwrap(); + unsafe { + LOG_FILE = format!("{}/rf2_vk_hud.log", home); + } + + if let Err(_) = File::create(unsafe { &LOG_FILE }) {} + + write_log!(" =================================================================="); + write_log!(" ======================= New Negotiation =========================="); + write_log!(" =================================================================="); } - if let Err(_) = File::create(unsafe { &LOG_FILE }) {} - - write_log(" =================================================================="); - write_log(" ======================= New Negotiation =========================="); - write_log(" =================================================================="); - unsafe { *pVersionStruct = VkNegotiateLayerInterface { sType: enums::VkNegotiateLayerStructType::LAYER_NEGOTIATE_INTERFACE_STRUCT, @@ -57,7 +82,7 @@ pub(crate) extern "C" fn vkNegotiateLoaderLayerInterfaceVersion( set_vk_handles(match VkTypedefHandles::new() { Ok(handles) => handles, Err(err) => { - write_log(format!("failed to load typedef handles {:?}", err)); + write_log!(format!("failed to load typedef handles {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }); @@ -73,7 +98,7 @@ extern "system" fn get_device_proc_addr( let func_string = match VkString::try_from(function_name) { Ok(func) => func, Err(_) => { - write_log("Err: failed creating string"); + write_log!("Err: failed creating string"); return Functions::Null.convert(); } }; @@ -99,7 +124,7 @@ extern "system" fn get_instance_proc_addr( let func_string = match VkString::try_from(function_name) { Ok(func) => func, Err(_) => { - write_log("Err: failed creating string"); + write_log!("Err: failed creating string"); return Functions::Null.convert(); } }; @@ -122,7 +147,7 @@ pub(crate) extern "system" fn create_instance( allocator: *const VkAllocationCallbacks, instance: *mut VkInstance, ) -> VkResult { - write_log(" ================== vulkan layer create instance =================="); + write_log!(" ================== vulkan layer create instance =================="); let chain_info = match VkLayerInstanceCreateInfo::get_chain_info( unsafe { &*create_info }, @@ -130,7 +155,7 @@ pub(crate) extern "system" fn create_instance( ) { Some(info) => info, None => { - write_log("instance chain info not found."); + write_log!("instance chain info not found."); return VK_ERROR_LAYER_NOT_PRESENT; } }; @@ -151,11 +176,11 @@ pub(crate) extern "system" fn create_instance( vk_handles_mut().load_instance_functions(unsafe { *instance }, proc_addr); - write_log("-> successfully created instance."); + write_log!("-> successfully created instance."); let ext_names = unsafe { (*create_info).extension_names() }; - write_log(format!("{:?}", ext_names)); + write_log!(format!("{:?}", ext_names)); // DXVK workaround, it creates the instance twice with different properties if ext_names.contains(&VkString::new("VK_KHR_surface")) { @@ -168,7 +193,7 @@ pub(crate) extern "system" fn create_instance( ) { Ok(ins) => ins, Err(err) => { - write_log(format!("-> local instance creation failed: {:?}", err)); + write_log!(format!("-> local instance creation failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -176,7 +201,7 @@ pub(crate) extern "system" fn create_instance( OVERLAY.set_instance(ins); } - write_log("-> created local instance handle"); + write_log!("-> created local instance handle"); } VK_SUCCESS @@ -186,7 +211,7 @@ pub(crate) extern "system" fn destroy_instance( instance: VkInstance, allocator: *const VkAllocationCallbacks, ) { - write_log(" ================== vulkan layer destroy instance =================="); + write_log!(" ================== vulkan layer destroy instance =================="); unsafe { if let Some(vk_fn) = vk_handles().handle("vkDestroyInstance") { @@ -203,14 +228,14 @@ pub(crate) extern "system" fn create_device( allocator: *const VkAllocationCallbacks, device: *mut VkDevice, ) -> VkResult { - write_log(" ================== vulkan layer create device =================="); + write_log!(" ================== vulkan layer create device =================="); let chain_info = match VkLayerDeviceCreateInfo::get_chain_info(unsafe { &*create_info }, VK_LAYER_LINK_INFO) { Some(info) => info, None => { - write_log("device chain info not found."); + write_log!("device chain info not found."); return VK_ERROR_LAYER_NOT_PRESENT; } }; @@ -226,7 +251,7 @@ pub(crate) extern "system" fn create_device( create_device(physical_device, create_info, allocator, device) } None => { - write_log("failed creating device"); + write_log!("failed creating device"); return VK_ERROR_INITIALIZATION_FAILED; } } @@ -238,18 +263,10 @@ pub(crate) extern "system" fn create_device( vk_handles_mut().load_device_functions(unsafe { *device }, proc_addr); unsafe { - QUEUE_SUBMIT = match vk_handles().handle("vkQueueSubmit") { - Some(submit) => mem::transmute(submit), - None => { - write_log("failed querying vkQueueSubmit"); - return VK_ERROR_INITIALIZATION_FAILED; - } - }; - ACQUIRE_NEXT_IMAGE = match vk_handles().handle("vkAcquireNextImageKHR") { Some(acquire_next_image) => mem::transmute(acquire_next_image), None => { - write_log("failed querying vkAcquireNextImageKHR"); + write_log!("failed querying vkAcquireNextImageKHR"); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -258,7 +275,7 @@ pub(crate) extern "system" fn create_device( let pdev = match PhysicalDevice::from_raw(unsafe { OVERLAY.instance() }, physical_device) { Ok(pdev) => pdev, Err(err) => { - write_log(format!("failed creating physical device: {:?}", err)); + write_log!(format!("failed creating physical device: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -267,7 +284,7 @@ pub(crate) extern "system" fn create_device( { Ok(qi) => qi, Err(err) => { - write_log(format!("failed getting queue info: {:?}", err)); + write_log!(format!("failed getting queue info: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; @@ -279,13 +296,13 @@ pub(crate) extern "system" fn create_device( ); for info in create_queue_info { - write_log(format!( + write_log!(format!( "pCreateDeviceInfo; queue fam: {}, queue count: {}", info.queueFamilyIndex, info.queueCount )); } - write_log(format!( + write_log!(format!( "Queue: queue fam: {}, queue count: {}", queue_info.queue_create_info.queueFamilyIndex, queue_info.queue_create_info.queueCount )); @@ -293,21 +310,21 @@ pub(crate) extern "system" fn create_device( let ext_names = unsafe { (*create_info).extension_names() }; - write_log(format!("{:?}", ext_names)); + write_log!(format!("{:?}", ext_names)); let device = match Device::preinitialized(unsafe { *device }, proc_addr, pdev, &ext_names) { Ok(dev) => dev, Err(err) => { - write_log(format!("-> local device creation failed: {:?}", err)); + write_log!(format!("-> local device creation failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; - write_log("created device"); + write_log!("created device"); let queue = device.get_queue(queue_info.queue_family_index, queue_info.queue_index); - write_log("got queue from device"); + write_log!("got queue from device"); unsafe { OVERLAY.set_device(device); @@ -321,7 +338,7 @@ pub(crate) extern "system" fn destroy_device( device: VkDevice, allocator: *const VkAllocationCallbacks, ) { - write_log(" ================== vulkan layer destroy device =================="); + write_log!(" ================== vulkan layer destroy device =================="); unsafe { if let Some(vk_fn) = vk_handles().handle("vkDestroyDevice") { @@ -338,20 +355,20 @@ pub(crate) extern "system" fn create_swapchain( _allocator: *const VkAllocationCallbacks, p_swapchain: *mut VkSwapchainKHR, ) -> VkResult { - write_log(" ================== vulkan layer create swapchain =================="); + write_log!(" ================== vulkan layer create swapchain =================="); let create_swapchain: PFN_vkCreateSwapchainKHR = match vk_handles().handle("vkCreateSwapchainKHR") { Some(create_swapchain) => unsafe { mem::transmute(create_swapchain) }, None => { - write_log("failed querying vkCreateSwapchainKHR"); + write_log!("failed querying vkCreateSwapchainKHR"); return VK_ERROR_INITIALIZATION_FAILED; } }; create_swapchain(_device, create_info, _allocator, p_swapchain); - write_log(format!("-> created swapchain vk handle {:?}", unsafe { + write_log!(format!("-> created swapchain vk handle {:?}", unsafe { *p_swapchain })); @@ -359,99 +376,23 @@ pub(crate) extern "system" fn create_swapchain( match unsafe { Swapchain::from_raw(OVERLAY.device(), &*create_info, *p_swapchain) } { Ok(swapchain) => swapchain, Err(err) => { - write_log(format!("create swapchain failed: {:?}", err)); + write_log!(format!("create swapchain failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } }; - write_log("-> created Arc"); + write_log!("-> created Arc"); if let Err(err) = unsafe { OVERLAY.create_rendering(swapchain) } { - write_log(format!("create overlay rendering struct failed: {:?}", err)); + write_log!(format!("create overlay rendering struct failed: {:?}", err)); return VK_ERROR_INITIALIZATION_FAILED; } - write_log("-> created renderer"); + write_log!("-> created renderer"); VK_SUCCESS } -pub(crate) extern "system" fn submit_queue( - queue: VkQueue, - submit_count: u32, - submits: *const VkSubmitInfo, - fence: VkFence, -) -> VkResult { - write_log(" ================== vulkan layer submit queue =================="); - - unsafe { - write_log(format!( - "submit queue ({:?}), internal queue: ({:?})", - queue, - OVERLAY.queue().lock().unwrap().vk_handle() - )); - - // let command_buffers = slice::from_raw_parts( - // (*submits).pCommandBuffers, - // (*submits).commandBufferCount as usize, - // ); - - // let cb = match OVERLAY.render() { - // Ok(cb) => cb, - // Err(err) => { - // write_log(format!("overlay rendering failed: {:?}", err)); - // return VK_ERROR_DEVICE_LOST; - // } - // }; - - // let cb = [command_buffers, &[cb]].concat(); - - // let mut_ptr = submits as *mut VkSubmitInfo; - - // (*mut_ptr).commandBufferCount = cb.len() as u32; - // (*mut_ptr).pCommandBuffers = cb.as_ptr(); - - QUEUE_SUBMIT(queue, submit_count, submits, fence) - } -} - -pub(crate) extern "system" fn get_device_queue( - device: VkDevice, - queue_family_index: u32, - queue_index: u32, - p_queue: *mut VkQueue, -) { - write_log(" ================== vulkan layer get device queue =================="); - - let get_device_queue: PFN_vkGetDeviceQueue = match vk_handles().handle("vkGetDeviceQueue") { - Some(get_queue) => unsafe { mem::transmute(get_queue) }, - None => { - write_log("failed querying vkGetDeviceQueue"); - panic!("failed querying vkGetDeviceQueue"); - } - }; - - get_device_queue(device, queue_family_index, queue_index, p_queue); - write_log(format!("new queue: {:?}", unsafe { *p_queue })); - unsafe { - write_log(format!( - "internal queue: ({:?})", - OVERLAY.queue().lock().unwrap().vk_handle() - )); - } - - // unsafe { - // let arc_device = OVERLAY.device(); - - // arc_device.physical_device().OVERLAY.add_queue(Queue::new( - // arc_device, - // *p_queue, - // queue_family_index, - // queue_index, - // )); - // } -} - pub(crate) extern "system" fn acquire_next_image( device: VkDevice, swapchain: VkSwapchainKHR, @@ -472,7 +413,7 @@ pub(crate) extern "system" fn acquire_next_image( Some(fence), ); - write_log(format!( + write_log!(format!( "acquire (swapchain: {:?}) res: {:?}", sc.vk_handle(), res @@ -488,13 +429,13 @@ pub(crate) extern "system" fn acquire_next_image( OutOfDate::TimeOut => VK_TIMEOUT, }, Err(err) => { - write_log(format!("failed acquiring next image {:?}", err)); + write_log!(format!("failed acquiring next image {:?}", err)); VK_ERROR_DEVICE_LOST } } } None => { - write_log("acquired other swapchain image"); + write_log!("acquired other swapchain image"); ACQUIRE_NEXT_IMAGE(device, swapchain, timeout, semaphore, fence, image_index) } } @@ -505,23 +446,28 @@ pub(crate) extern "system" fn present_queue( queue: VkQueue, present_info: *const VkPresentInfoKHR, ) -> VkResult { - write_log(" ================== vulkan layer queue present =================="); - write_log(format!("iq: {:?}, cq: {:?}", queue, unsafe { - OVERLAY.queue().lock().unwrap().vk_handle() - })); - unsafe { - let swapchains = std::slice::from_raw_parts( - (*present_info).pSwapchains, - (*present_info).swapchainCount as usize, - ); + if logging() { + write_log!(" ================== vulkan layer queue present =================="); + write_log!(format!("iq: {:?}, cq: {:?}", queue, unsafe { + OVERLAY.queue().lock().unwrap().vk_handle() + })); + unsafe { + let swapchains = std::slice::from_raw_parts( + (*present_info).pSwapchains, + (*present_info).swapchainCount as usize, + ); - write_log(format!("present {} swapchain(s)", swapchains.len())); + write_log!(format!("present {} swapchain(s)", swapchains.len())); - for swapchain in swapchains { - write_log(format!("present swapchain: {:?}", swapchain)); + for swapchain in swapchains { + write_log!(format!("present swapchain: {:?}", swapchain)); - if let Some(swch) = OVERLAY.swapchain(*swapchain) { - write_log(" -> internal swapchain found!"); + if let Some(swch) = OVERLAY.swapchain(*swapchain) { + write_log!(format!( + " -> internal swapchain found! ({:?})", + swch.vk_handle() + )); + } } } } @@ -529,7 +475,7 @@ pub(crate) extern "system" fn present_queue( match unsafe { OVERLAY.render() } { Ok(_) => (), Err(err) => { - write_log(format!("overlay rendering failed: {:?}", err)); + write_log!(format!("overlay rendering failed: {:?}", err)); return VK_ERROR_DEVICE_LOST; } }; @@ -537,7 +483,7 @@ pub(crate) extern "system" fn present_queue( let pfn: PFN_vkQueuePresentKHR = match vk_handles().handle("vkQueuePresentKHR") { Some(pfn) => unsafe { mem::transmute(pfn) }, None => { - write_log("failed querying vkQueuePresentKHR"); + write_log!("failed querying vkQueuePresentKHR"); return VK_ERROR_DEVICE_LOST; } }; @@ -545,7 +491,9 @@ pub(crate) extern "system" fn present_queue( pfn(queue, present_info) } -pub fn write_log(msg: impl ToString) { +pub fn log(msg: impl ToString) { + assert!(logging()); + if let Ok(mut file) = OpenOptions::new() .append(true) .create(true) diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index 2050d7e..df9ae54 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -1,12 +1,16 @@ use crate::write_log; -use self::{rendering::Rendering, rfactor_data::RFactorData}; +use self::{ + rendering::Rendering, + rfactor_data::{DataConfig, RFactorData}, +}; mod pipeline; mod rendering; mod rfactor_data; use anyhow::Result; +use cgmath::vec3; use std::sync::{Arc, Mutex}; use vulkan_rs::prelude::*; @@ -66,30 +70,40 @@ impl Overlay { } pub fn create_rendering(&mut self, swapchain: Arc) -> Result<()> { - write_log("-> create rendering: start"); + write_log!("-> create rendering: start"); self.rendering = None; - write_log("-> create rendering: old cleared"); + write_log!("-> create rendering: old cleared"); self.rendering = Some(Rendering::new(self.device(), self.queue(), swapchain)?); - write_log("-> create rendering: new created"); + write_log!("-> create rendering: new created"); - write_log("-> create rendering: end"); + write_log!("-> create rendering: end"); Ok(()) } pub fn render(&mut self) -> Result<()> { + let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); + if self.rfactor_data.is_none() { self.rfactor_data = RFactorData::new( + DataConfig { + radar_scale: 1.0, + radar_car_distance: 20.0, + safe_color: vec3(0.0, 0.75, 0.0), + danger_color: vec3(0.75, 0.0, 0.0), + }, self.device(), self.rendering .as_mut() .unwrap() .single_color_pipeline() .descriptor_layout(), + swapchain.width(), + swapchain.height(), ) .ok(); } @@ -104,8 +118,6 @@ impl Overlay { None => Vec::new(), }; - let swapchain = self.rendering.as_ref().unwrap().swapchain().clone(); - self.rendering.as_mut().unwrap().render(swapchain, &objects) } } diff --git a/src/overlay/rendering.rs b/src/overlay/rendering.rs index 55d9d5c..d642723 100644 --- a/src/overlay/rendering.rs +++ b/src/overlay/rendering.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::{Vector2, Vector4}; +use cgmath::{Matrix4, Vector2, Vector4}; use vulkan_rs::prelude::*; use std::{ @@ -22,25 +22,25 @@ impl PositionOnlyVertex { /// corners[2] - top right /// corners[3] - bottom right /// - pub fn from_2d_corners(corners: [Vector2; 4]) -> [Self; 6] { + pub fn from_2d_corners(ortho: Matrix4, corners: [Vector2; 4]) -> [Self; 6] { [ Self { - position: corners[0].extend(0.0).extend(1.0), + position: ortho * corners[0].extend(0.0).extend(1.0), }, Self { - position: corners[1].extend(0.0).extend(1.0), + position: ortho * corners[1].extend(0.0).extend(1.0), }, Self { - position: corners[2].extend(0.0).extend(1.0), + position: ortho * corners[2].extend(0.0).extend(1.0), }, Self { - position: corners[2].extend(0.0).extend(1.0), + position: ortho * corners[2].extend(0.0).extend(1.0), }, Self { - position: corners[3].extend(0.0).extend(1.0), + position: ortho * corners[3].extend(0.0).extend(1.0), }, Self { - position: corners[0].extend(0.0).extend(1.0), + position: ortho * corners[0].extend(0.0).extend(1.0), }, ] } @@ -62,20 +62,20 @@ impl Rendering { queue: Arc>, swapchain: Arc, ) -> Result { - write_log("-> Rendering ctor: begin"); + crate::write_log!("-> Rendering ctor: begin"); let vk_images = swapchain.vk_images()?; - write_log(format!( + write_log!(format!( "-> Rendering ctor: vk images ({})", vk_images.len() )); let images = match swapchain.wrap_images(&vk_images, &queue, true) { Ok(images) => images, Err(err) => { - write_log(format!("-> Rendering ctor: failed wrapper: {:?}", err)); + write_log!(format!("-> Rendering ctor: failed wrapper: {:?}", err)); return Err(err); } }; - write_log("-> Rendering ctor: wrapped images"); + write_log!("-> Rendering ctor: wrapped images"); let render_target = RenderTarget::builder() .add_sub_pass( @@ -85,9 +85,9 @@ impl Rendering { ) .build(&device)?; - write_log("-> Rendering ctor: created render_target"); + write_log!("-> Rendering ctor: created render_target"); - write_log(format!( + write_log!(format!( "-> Rendering swapchain extents ({}, {})", swapchain.width(), swapchain.height(), @@ -149,7 +149,7 @@ impl Rendering { recorder.set_scissor(&scissor); recorder.set_viewport(&viewport); - write_log(format!("-> Rendering {} objects", objects.len())); + write_log!(format!("-> Rendering {} objects", objects.len())); for object in objects { let buffer = object.buffer(); diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs index 576ac00..62fbc9f 100644 --- a/src/overlay/rfactor_data.rs +++ b/src/overlay/rfactor_data.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use cgmath::vec2; +use cgmath::{ortho, vec2, vec3, InnerSpace, Matrix4, Vector2, Vector3, VectorSpace}; use rfactor_sm_reader::*; use vulkan_rs::prelude::*; @@ -7,72 +7,224 @@ use std::sync::Arc; use super::rendering::PositionOnlyVertex; +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, + 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, + car_width: f32, + car_height: f32, + + device: Arc, + descriptor_layout: Arc, } impl RFactorData { - pub fn new(device: Arc, descriptor_layout: &Arc) -> Result { - let radar_extent = 0.2; - let car_height = 0.05; - let car_width = 0.025; + pub fn new( + config: DataConfig, + device: Arc, + descriptor_layout: &Arc, + width: u32, + height: u32, + ) -> Result { + 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); Ok(Self { - // telemetry_reader: TelemetryReader::new()?, - // scoring_reader: ScoringReader::new()?, + config, + + telemetry_reader: TelemetryReader::new()?, + scoring_reader: ScoringReader::new()?, + background: RadarObject::new( device.clone(), descriptor_layout, - PositionOnlyVertex::from_2d_corners([ - vec2(-radar_extent, -radar_extent), - vec2(-radar_extent, radar_extent), - vec2(radar_extent, radar_extent), - vec2(radar_extent, -radar_extent), - ]), + 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([ - vec2(-car_width, -car_height), - vec2(-car_width, car_height), - vec2(car_width, car_height), - vec2(car_width, -car_height), - ]), + 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, + car_width, + car_height, + + device, + descriptor_layout: descriptor_layout.clone(), }) } + fn create_car_object(&self, offset: Vector2, color: [f32; 4]) -> Result { + 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, + ) + } + pub fn update(&mut self) -> Result<()> { + // get scoring info + let (scoring_info, vehicle_scorings) = self.scoring_reader.vehicle_scoring(); + + // 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 { + self.player_id = Some(vehicle_scoring.mID); + } + } + } + + // if player id is set (a map is loaded), check telemetry data + if let Some(player_id) = &self.player_id { + let telemetries = self.telemetry_reader.query_telemetry(); + + 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 offset = diff.truncate(); + let color = self.config.danger_color.lerp( + self.config.safe_color, + distance / self.config.radar_car_distance, + ); + + self.cars + .push(self.create_car_object(offset, [color.x, color.y, color.z, 0.9])?); + } + } + } + Ok(()) } pub fn objects(&self) -> Vec<&dyn RenderObject> { let mut objects: Vec<&dyn RenderObject> = Vec::new(); - objects.push(&self.background); + // 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); + for other_player_cars in &self.cars { + objects.push(other_player_cars); + } + + objects.push(&self.player_car); + } } - objects.push(&self.player_car); - objects } } @@ -134,3 +286,17 @@ impl RenderObject for RadarObject { &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), + } + } +}