sync
This commit is contained in:
parent
2b7a562b30
commit
e541933ae1
63
Cargo.lock
generated
63
Cargo.lock
generated
@ -233,6 +233,20 @@ name = "bytemuck"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@ -581,6 +595,18 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
@ -732,8 +758,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -758,6 +786,9 @@ name = "glam"
|
||||
version = "0.29.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
@ -808,7 +839,7 @@ dependencies = [
|
||||
"log",
|
||||
"presser",
|
||||
"thiserror",
|
||||
"windows 0.56.0",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1415,6 +1446,12 @@ dependencies = [
|
||||
name = "mp_elements"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"flume",
|
||||
"glam",
|
||||
"mp_core",
|
||||
"pollster",
|
||||
"regex",
|
||||
"wgpu",
|
||||
]
|
||||
|
||||
@ -1439,6 +1476,15 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend-ohos"
|
||||
version = "0.0.7"
|
||||
@ -1711,6 +1757,12 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.9.0"
|
||||
@ -2144,6 +2196,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
version = "0.3.0+sdk-1.3.268.0"
|
||||
|
||||
@ -4,4 +4,10 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bytemuck = { version = "1.19.0", features = ["derive"] }
|
||||
glam = { version = "0.29.2", features = ["bytemuck"] }
|
||||
regex = "1.11.1"
|
||||
wgpu = "23.0.0"
|
||||
mp_core = { path = "../mp_core", version = "*" }
|
||||
flume = "0.11.1"
|
||||
pollster = "0.4.0"
|
||||
|
||||
29
mp_elements/shaders/colormap.wgsl
Normal file
29
mp_elements/shaders/colormap.wgsl
Normal file
@ -0,0 +1,29 @@
|
||||
@group(1) @binding(0) var color_map_texture: texture_2d<f32>;
|
||||
@group(1) @binding(1) var color_map_sampler: sampler;
|
||||
@group(1) @binding(2) var<uniform> color_map_params: ColorMapParams;
|
||||
|
||||
struct ColorMapParams {
|
||||
color_count: u32,
|
||||
value_min: f32,
|
||||
value_max: f32
|
||||
invalid_value: f32
|
||||
}
|
||||
|
||||
fn find_idx(ratio: f32) -> f32 {
|
||||
var sum = 0.0;
|
||||
var i = 0.0;
|
||||
var count = (color_map_params.color_count - 1) as f32;
|
||||
while (ratio > sum) {
|
||||
sum += textureSample(color_map_texture, color_map_sampler, vec2<f32>(i / count, 0.0)).r;
|
||||
i += 1.0;
|
||||
}
|
||||
return i / count;
|
||||
}
|
||||
|
||||
|
||||
fn linear_colormap(value: f32) -> vec4f {
|
||||
var v = clamp((value - color_map_params.value_min) / (color_map_params.value_max - color_map_params.value_min), 0.0, 1.0);
|
||||
float idx = find_idx(v);
|
||||
let c0: vec3f = textureSample(color_map_texture, color_map_sampler, vec2<f32>(idx, 0.0)).rgb;
|
||||
return vec4f(c0, 1.0);
|
||||
}
|
||||
5
mp_elements/shaders/constants.wgsl
Normal file
5
mp_elements/shaders/constants.wgsl
Normal file
@ -0,0 +1,5 @@
|
||||
const PI:f32 = 3.14159265358979323846264338327950288;
|
||||
const TWO_PI:f32 = 6.28318530717958647692528676655900576;
|
||||
const HALF_PI:f32 = 1.57079632679489661923132169163975144;
|
||||
const LOG2:f32 = 0.693147180559945309417232121458176568;
|
||||
const LOG10:f32 = 2.30258509299404568401799145468436421;
|
||||
65
mp_elements/shaders/ppi.wgsl
Normal file
65
mp_elements/shaders/ppi.wgsl
Normal file
@ -0,0 +1,65 @@
|
||||
#include "constants.wgsl";
|
||||
#include "colormap.wgsl";
|
||||
|
||||
@group(2) @binding(0) var<uniform> params: UniformParams;
|
||||
@group(2) @binding(1) var data_buffer: texture_3d<f32>;
|
||||
|
||||
struct UniformParams {
|
||||
origin: vec3f
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@location(0) position: vec4f,
|
||||
@location(1) r_range: vec2f,
|
||||
@location(2) idx: vec3f
|
||||
}
|
||||
|
||||
struct UniformParams {
|
||||
// Model-View-Projection matrix
|
||||
mvp: mat4x4f,
|
||||
origin: vec3f
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vertex(
|
||||
@location(0) position: vec3f,
|
||||
@location(1) r_range: vec2f,
|
||||
@location(2) idx: vec3f
|
||||
) -> VertexOutput {
|
||||
|
||||
var out: VertexOutput;
|
||||
|
||||
// Transform position
|
||||
out.position = params.mvp * vec4f(position, 1.0);
|
||||
out.r_range = r_range;
|
||||
out.idx = idx;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
fn polar_forward(cartesian: vec3f) -> vec3f {
|
||||
let r = length(cartesian.xy - params.origin.xy);
|
||||
let theta = atan2(cartesian.y, cartesian.x);
|
||||
let z = cartesian.z;
|
||||
return vec3f(r, theta, z);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(input: VertexOutput) -> location(0) vec4f {
|
||||
// Sample data texture
|
||||
let value = textureSample(data_texture, data_sampler, input.idx).r;
|
||||
let ear = polar_forward(input.position);
|
||||
var color = linear_colormap(value);
|
||||
|
||||
let r = ear.z;
|
||||
// Valid range
|
||||
let r_range = input.r_range;
|
||||
|
||||
let outside_lower_bound = step(r, r_range.x);
|
||||
let outside_upper_bound = step(r_range.y, r);
|
||||
let is_outside = outside_lower_bound + outside_upper_bound;
|
||||
color.a *= 1.0 - is_outside;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@ -1,18 +1,31 @@
|
||||
use wgpu::{core::instance, Backends, Instance, RequestAdapterOptions};
|
||||
pub struct App {}
|
||||
use crate::elements::{ElementAttach, Elements};
|
||||
use crate::elementvec::ElementVec;
|
||||
use wgpu::{Backends, Instance};
|
||||
|
||||
const BACKENDS_DEFAULT: u32 = Backends::DX12.bits()
|
||||
| Backends::METAL.bits()
|
||||
| Backends::GL.bits()
|
||||
| Backends::BROWSER_WEBGPU.bits();
|
||||
|
||||
pub struct App {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
common_utils: CommonUtils,
|
||||
_texture: wgpu::Texture,
|
||||
texture_view: wgpu::TextureView,
|
||||
pipelines: Option<ElementVec>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub async fn inistant() -> Self {
|
||||
pub async fn instant() -> Self {
|
||||
// 修复方法名称拼写错误
|
||||
// Instance is a handle to the backend. It is used to create adapters.
|
||||
let instance = Instance::new(wgpu::InstanceDescriptor {
|
||||
backends: Backends::from_bits(BACKENDS_DEFAULT).unwrap(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Request an adapter, which is a handle to a physical device.
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
@ -22,27 +35,251 @@ impl App {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Request a device and a queue from the adapter. The device is the handle to the GPU, and the queue is used to submit commands to the GPU.
|
||||
let (device, queue) = adapter
|
||||
.request_device(&Default::default(), None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let texture_size = 256u32;
|
||||
let texture_desc = wgpu::TextureDescriptor {
|
||||
// Create a new instance of the common utils struct. This struct contains the bind group layout, bind group, and uniform buffer.
|
||||
let common_utils = CommonUtils::new(&device);
|
||||
|
||||
// Create a new texture. This texture will be used as the output texture for the render pass.
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("output texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: texture_size,
|
||||
height: texture_size,
|
||||
width: 800,
|
||||
height: 600,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
label: None,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
||||
view_formats: &[],
|
||||
};
|
||||
});
|
||||
|
||||
Self {}
|
||||
// Create a texture view from the texture. This texture view will be used as the output texture for the render pass.
|
||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
common_utils,
|
||||
pipelines: None,
|
||||
_texture: texture,
|
||||
texture_view,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new context struct. This struct contains references to the device, queue, bind group layout, and bind group.
|
||||
pub fn ctx(&self) -> Ctx {
|
||||
Ctx::new(self)
|
||||
}
|
||||
|
||||
// Initialize the app. This method creates the pipelines and initializes the elements.
|
||||
pub async fn init(&mut self) {
|
||||
self.pipelines = Some(ElementVec::init(&Ctx::new(self)));
|
||||
}
|
||||
|
||||
// Get a reference to the pipelines.
|
||||
pub fn pipelines(&self) -> &ElementVec {
|
||||
self.pipelines.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn common_utils_mut(&mut self) -> &mut CommonUtils {
|
||||
&mut self.common_utils
|
||||
}
|
||||
|
||||
// Draw the elements in the draw list.
|
||||
pub fn draw(&self, draw_list: DrawList) {
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("draw_command_encoder"),
|
||||
});
|
||||
|
||||
{
|
||||
let render_pass_desc = wgpu::RenderPassDescriptor {
|
||||
label: Some("Some Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &self.texture_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
..Default::default()
|
||||
};
|
||||
let mut render_pass = encoder.begin_render_pass(&render_pass_desc);
|
||||
|
||||
// Set the common utils bind group.
|
||||
let common_utils = &self.common_utils.bind_group;
|
||||
|
||||
// Draw each element in the draw list.
|
||||
for (attach, element) in draw_list.elements {
|
||||
let pipeline = element.pipeline();
|
||||
// Set the pipeline for the render pass.
|
||||
render_pass.set_pipeline(pipeline);
|
||||
|
||||
// Set the common utils bind group for the render pass.
|
||||
render_pass.set_bind_group(0, common_utils, &[]);
|
||||
|
||||
// Set the bind group for the render pass.
|
||||
for (index, bind_group) in attach.bind_group.iter() {
|
||||
render_pass.set_bind_group(*index, bind_group, &[]);
|
||||
}
|
||||
|
||||
// Set the bind group for the render pass.
|
||||
attach.bind(&mut render_pass);
|
||||
|
||||
// Draw the element.
|
||||
element.draw(attach, &mut render_pass);
|
||||
}
|
||||
}
|
||||
self.queue.submit(Some(encoder.finish()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ctx<'a> {
|
||||
pub device: &'a wgpu::Device,
|
||||
pub queue: &'a wgpu::Queue,
|
||||
pub common_tools_bind_group_layout: &'a wgpu::BindGroupLayout,
|
||||
pub common_tools_bind_group: &'a wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl<'a> Ctx<'a> {
|
||||
pub fn bind_group_layout(&self) -> Vec<wgpu::BindGroupLayout> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
pub fn new(app: &'a App) -> Self {
|
||||
Self {
|
||||
device: &app.device,
|
||||
queue: &app.queue,
|
||||
common_tools_bind_group_layout: &app.common_utils.bind_group_layout,
|
||||
common_tools_bind_group: &app.common_utils.bind_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawList<'a> {
|
||||
pub elements: Vec<(&'a ElementAttach, &'a Elements)>, // 修复字段访问权限
|
||||
}
|
||||
|
||||
impl<'a> DrawList<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { elements: vec![] }
|
||||
}
|
||||
|
||||
pub fn push<Ele: Into<&'a Elements>>(&mut self, element: Ele, attach: &'a ElementAttach) {
|
||||
self.elements.push((attach, element.into()));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct CommonUniform {
|
||||
pub model_matrix: glam::Mat4,
|
||||
pub view_matrix: glam::Mat4,
|
||||
pub proj_matrix: glam::Mat4,
|
||||
pub camera_position: glam::Vec3,
|
||||
pub camera_front: glam::Vec3,
|
||||
pub camera_up: glam::Vec3,
|
||||
pub light_position: glam::Vec3,
|
||||
pub light_color: glam::Vec3,
|
||||
pub light_intensity: f32,
|
||||
}
|
||||
|
||||
pub struct CommonUtils {
|
||||
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub uniform_buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl CommonUtils {
|
||||
fn new(device: &wgpu::Device) -> Self {
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("common_utils_bind_group_layout"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::all(),
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
});
|
||||
|
||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("common_utils_buffer"),
|
||||
size: std::mem::size_of::<CommonUniform>() as u64,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("common_utils_bind_group"),
|
||||
layout: &bind_group_layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
});
|
||||
|
||||
Self {
|
||||
bind_group_layout,
|
||||
bind_group,
|
||||
uniform_buffer: buffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Elementi {}
|
||||
|
||||
mod test {
|
||||
use mp_core::{PluginManager, RadarGridData};
|
||||
|
||||
use crate::elements::{Element, PPI};
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_app() {
|
||||
let plugin_manager = PluginManager::new("/Users/tsuki/projects/mp/loaders").unwrap();
|
||||
|
||||
let data = plugin_manager.try_load_data(
|
||||
"/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip",
|
||||
);
|
||||
|
||||
pollster::block_on(async {
|
||||
let mut app = App::instant().await;
|
||||
|
||||
if let Ok(data) = data {
|
||||
let first_block = data.first().unwrap();
|
||||
|
||||
if let Ok(data) = <&mp_core::Data as TryInto<&RadarGridData>>::try_into(first_block)
|
||||
{
|
||||
}
|
||||
}
|
||||
// app.init().await;
|
||||
|
||||
// let pipelines = app.pipelines();
|
||||
// let ppi = pipelines.ppi();
|
||||
|
||||
// let ctx = app.ctx();
|
||||
|
||||
// let attachment = ppi.new_attachment(&ctx);
|
||||
|
||||
// ppi.bake()
|
||||
|
||||
// attachment.update_data(&ctx, data);
|
||||
|
||||
// let mut draw_list = DrawList::new();
|
||||
// draw_list.push(pipelines.ppi());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
85
mp_elements/src/elements/mod.rs
Normal file
85
mp_elements/src/elements/mod.rs
Normal file
@ -0,0 +1,85 @@
|
||||
pub mod ppi;
|
||||
use crate::app::Ctx;
|
||||
pub use ppi::PPI;
|
||||
use std::any::Any;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
macro_rules! elements {
|
||||
($(($element_name:ident,$element: ty),)+) => {
|
||||
pub enum Elements {
|
||||
$($element_name($element),)+
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$element> for Elements {
|
||||
fn from(element: $element) -> Self {
|
||||
Elements::$element_name(element)
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
impl Elements {
|
||||
pub fn pipeline(&self) -> &wgpu::RenderPipeline {
|
||||
match self {
|
||||
$(Elements::$element_name(element) => element.pipeline(),)+
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, attach: &ElementAttach, render_pass: &mut wgpu::RenderPass) {
|
||||
match self {
|
||||
$(Elements::$element_name(element) => element.draw(attach, render_pass),)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Element {
|
||||
type Vertex;
|
||||
type Uniform;
|
||||
type Data;
|
||||
|
||||
fn new(ctx: &Ctx) -> Self;
|
||||
|
||||
fn new_attachment(&self, ctx: &Ctx) -> ElementAttach;
|
||||
|
||||
// Bake the data into vertices and indices
|
||||
fn bake(&self, data: &Self::Data) -> (Vec<Self::Vertex>, Option<Vec<u32>>);
|
||||
|
||||
fn supports(&self, _data: &Self::Data) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn pipeline(&self) -> &wgpu::RenderPipeline;
|
||||
|
||||
fn draw(&self, attach: &ElementAttach, render_pass: &mut wgpu::RenderPass);
|
||||
}
|
||||
|
||||
pub struct ElementAttach {
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub index_buffer: Option<wgpu::Buffer>,
|
||||
pub num_indices: u32,
|
||||
pub uniform_buffer: Option<wgpu::Buffer>,
|
||||
pub bind_group: Vec<(u32, wgpu::BindGroup)>,
|
||||
}
|
||||
|
||||
impl ElementAttach {
|
||||
pub fn update_data<T>(&self, ctx: &Ctx, data: (Vec<T>, Option<Vec<u32>>))
|
||||
where
|
||||
T: bytemuck::Zeroable + bytemuck::Pod,
|
||||
{
|
||||
let device = ctx.device;
|
||||
}
|
||||
|
||||
pub fn bind(&self, render_pass: &mut wgpu::RenderPass) {
|
||||
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
if let Some(index_buffer) = &self.index_buffer {
|
||||
render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements! {
|
||||
(PPI, PPI),
|
||||
}
|
||||
374
mp_elements/src/elements/ppi.rs
Normal file
374
mp_elements/src/elements/ppi.rs
Normal file
@ -0,0 +1,374 @@
|
||||
use std::ops::Sub;
|
||||
|
||||
use crate::{app::Ctx, utils::merge_shader};
|
||||
use bytemuck;
|
||||
use glam::Vec3;
|
||||
use mp_core::{data::CoordType, RadarGridData};
|
||||
|
||||
use super::{Element, ElementAttach};
|
||||
|
||||
const EMAXNUM: u64 = 50;
|
||||
const AMAXNUM: u64 = 360;
|
||||
const RMAXNUM: u64 = 50;
|
||||
|
||||
pub struct PPI {
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct PPIUniform {
|
||||
origin: Vec3,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct PPIVertex {
|
||||
position: Vec3,
|
||||
r_ranges: [f32; 2],
|
||||
idx: [u32; 3],
|
||||
}
|
||||
|
||||
impl PPIVertex {
|
||||
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::size_of::<Vec3>() as wgpu::BufferAddress,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Uint32x3,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for PPI {
|
||||
type Vertex = PPIVertex;
|
||||
type Uniform = PPIUniform;
|
||||
type Data = RadarGridData;
|
||||
|
||||
fn new(ctx: &Ctx) -> Self {
|
||||
let device = ctx.device;
|
||||
// Group Layout
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("PPI Bind Group Layout"),
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Shader Code
|
||||
let shader = Self::init_shader(device);
|
||||
|
||||
// Render Pipeline Layout
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("PPI Render Pipeline Layout"),
|
||||
bind_group_layouts: &[
|
||||
// common_tools
|
||||
&ctx.common_tools_bind_group_layout,
|
||||
// ppi
|
||||
&bind_group_layout,
|
||||
],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
// Render Pipeline
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("PPI Render Pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
compilation_options: Default::default(),
|
||||
entry_point: Some("vertex"),
|
||||
buffers: &[PPIVertex::desc()],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
compilation_options: Default::default(),
|
||||
entry_point: Some("fragment"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
blend: None,
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
PPI {
|
||||
bind_group_layout: bind_group_layout,
|
||||
pipeline: render_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_attachment(&self, ctx: &Ctx) -> ElementAttach {
|
||||
let device = ctx.device;
|
||||
|
||||
// Buffers
|
||||
let data_buffer = Self::create_data_buffer(device);
|
||||
let uniform_buffer = Self::create_uniform_buffer(device);
|
||||
|
||||
// Bind Group
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniform_buffer.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: data_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: Some("PPI Bind Group"),
|
||||
});
|
||||
|
||||
// Vertex Buffer
|
||||
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("PPI Vertex Buffer"),
|
||||
mapped_at_creation: false,
|
||||
size: std::mem::size_of::<PPIVertex>() as wgpu::BufferAddress * EMAXNUM * AMAXNUM,
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
|
||||
// Index Buffer
|
||||
let index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("PPI Index Buffer"),
|
||||
mapped_at_creation: false,
|
||||
size: EMAXNUM
|
||||
* AMAXNUM
|
||||
* RMAXNUM
|
||||
* 6
|
||||
* std::mem::size_of::<u32>() as wgpu::BufferAddress,
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
});
|
||||
|
||||
ElementAttach {
|
||||
vertex_buffer,
|
||||
index_buffer: Some(index_buffer),
|
||||
num_indices: 0,
|
||||
uniform_buffer: Some(uniform_buffer),
|
||||
bind_group: vec![(1, bind_group)],
|
||||
}
|
||||
}
|
||||
|
||||
fn bake(&self, data: &Self::Data) -> (Vec<Self::Vertex>, Option<Vec<u32>>) {
|
||||
let coord_typ = data.coord_type().unwrap();
|
||||
|
||||
if let CoordType::Polar {
|
||||
range,
|
||||
azimuth,
|
||||
elevation,
|
||||
} = coord_typ
|
||||
{
|
||||
let e = elevation.map(|v| *v.first().unwrap_or(&0.0)).unwrap_or(0.0);
|
||||
let (vertices, indices) = Self::bake_vi(e, azimuth, &range);
|
||||
|
||||
(vertices, Some(indices))
|
||||
} else {
|
||||
panic!("PPI only supports polar coordinates");
|
||||
}
|
||||
}
|
||||
|
||||
fn pipeline(&self) -> &wgpu::RenderPipeline {
|
||||
&self.pipeline
|
||||
}
|
||||
|
||||
fn draw(&self, attach: &ElementAttach, render_pass: &mut wgpu::RenderPass) {
|
||||
render_pass.draw_indexed(0..attach.num_indices, 0, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
impl PPI {
|
||||
fn init_shader(device: &wgpu::Device) -> wgpu::ShaderModule {
|
||||
let shader_str = merge_shader("../../shaders/ppi.wgsl");
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("PPI Shader Module"),
|
||||
source: wgpu::ShaderSource::Wgsl(shader_str.into()),
|
||||
});
|
||||
shader
|
||||
}
|
||||
|
||||
fn create_uniform_buffer(device: &wgpu::Device) -> wgpu::Buffer {
|
||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("PPI Uniform Buffer"),
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::STORAGE,
|
||||
size: 0,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
fn create_data_buffer(device: &wgpu::Device) -> wgpu::Buffer {
|
||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("PPI Texture Buffer"),
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::STORAGE,
|
||||
size: 0,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
fn bake_vi(e: f64, a: &Vec<f64>, r: &Vec<f64>) -> (Vec<PPIVertex>, Vec<u32>) {
|
||||
// Sort azimuth
|
||||
let mut sorted_azimuth = a.clone();
|
||||
sorted_azimuth.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater));
|
||||
|
||||
// Calculate step
|
||||
let azi_step = max_step(&sorted_azimuth, f64::MIN) / 2.0;
|
||||
let r_step = min_step(&r, f64::MAX) / (r[r.len() - 1]) / 2.0;
|
||||
let r_step_f32 = r_step as f32;
|
||||
|
||||
// One cell has 4 vertices
|
||||
let mut vertexs = Vec::with_capacity(a.len() * r.len() * 4);
|
||||
for (a_idx, _a) in sorted_azimuth.into_iter().enumerate() {
|
||||
for (r_idx, _r) in r.iter().enumerate() {
|
||||
let r_ranges = [*_r as f32 - r_step_f32, *_r as f32 + r_step_f32];
|
||||
let idx = [0, a_idx as u32, r_idx as u32];
|
||||
// Left Top
|
||||
let lt = polar_to_cartesian(*_r + r_step, _a - azi_step, e);
|
||||
vertexs.push(PPIVertex {
|
||||
position: Vec3::new(lt.0, lt.1, lt.2),
|
||||
r_ranges,
|
||||
idx,
|
||||
});
|
||||
// Left Bot
|
||||
let lb = polar_to_cartesian(*_r - r_step, _a - azi_step, e);
|
||||
vertexs.push(PPIVertex {
|
||||
position: Vec3::new(lb.0, lb.1, lb.2),
|
||||
r_ranges,
|
||||
idx,
|
||||
});
|
||||
// Right Top
|
||||
let rt = polar_to_cartesian(*_r + r_step, _a + azi_step, e);
|
||||
vertexs.push(PPIVertex {
|
||||
position: Vec3::new(rt.0, rt.1, rt.2),
|
||||
r_ranges,
|
||||
idx,
|
||||
});
|
||||
// Right Bot
|
||||
let rb = polar_to_cartesian(*_r - r_step, _a + azi_step, e);
|
||||
vertexs.push(PPIVertex {
|
||||
position: Vec3::new(rb.0, rb.1, rb.2),
|
||||
r_ranges,
|
||||
idx,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// index buffer
|
||||
let mut indice_buffer = Vec::with_capacity(a.len() * r.len() * 6);
|
||||
for a_idx in 0..a.len() - 1 {
|
||||
for r_idx in 0..r.len() - 1 {
|
||||
let lt = (a_idx * r.len() + r_idx) as u32;
|
||||
let lb = (a_idx * r.len() + r_idx + 1) as u32;
|
||||
let rt = ((a_idx + 1) * r.len() + r_idx) as u32;
|
||||
let rb = ((a_idx + 1) * r.len() + r_idx + 1) as u32;
|
||||
|
||||
indice_buffer.push(lt);
|
||||
indice_buffer.push(lb);
|
||||
indice_buffer.push(rt);
|
||||
|
||||
indice_buffer.push(lb);
|
||||
indice_buffer.push(rb);
|
||||
indice_buffer.push(rt);
|
||||
}
|
||||
}
|
||||
return (vertexs, indice_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn min_step<T: Sub<T, Output = T> + Copy>(data: &Vec<T>, gap: T) -> T
|
||||
where
|
||||
<T as std::ops::Sub>::Output: PartialOrd<T>,
|
||||
{
|
||||
// 计算相邻元素之间的间距
|
||||
let mut min_gap = gap;
|
||||
for window in data.windows(2) {
|
||||
if let [a, b] = window {
|
||||
let gap = *b - *a;
|
||||
if gap < min_gap {
|
||||
min_gap = gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return min_gap;
|
||||
}
|
||||
|
||||
fn max_step<T: Copy + Sub<T, Output = T>>(data: &Vec<T>, gap: T) -> T
|
||||
where
|
||||
<T as std::ops::Sub>::Output: PartialOrd<T>,
|
||||
{
|
||||
// 计算相邻元素之间的间距
|
||||
let mut max_gap = gap;
|
||||
for window in data.windows(2) {
|
||||
if let [a, b] = window {
|
||||
let gap = *b - *a;
|
||||
if gap > max_gap {
|
||||
max_gap = gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return max_gap;
|
||||
}
|
||||
|
||||
fn polar_to_cartesian(r: f64, azimuth: f64, elevation: f64) -> (f32, f32, f32) {
|
||||
let x = r * azimuth.cos() * elevation.cos();
|
||||
let y = r * azimuth.sin() * elevation.cos();
|
||||
let z = r * elevation.sin();
|
||||
(x as f32, y as f32, z as f32)
|
||||
}
|
||||
27
mp_elements/src/elementvec.rs
Normal file
27
mp_elements/src/elementvec.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::app::Ctx;
|
||||
use crate::elements::*;
|
||||
macro_rules! elementvec {
|
||||
($({$element: ident, $element_ty: ty})+) => {
|
||||
pub struct ElementVec {
|
||||
$($element: $element_ty,)+
|
||||
}
|
||||
impl ElementVec {
|
||||
pub fn init(ctx: &Ctx) -> Self {
|
||||
// Compile the shaders, create the pipelines, etc.
|
||||
Self {
|
||||
$($element: <$element_ty>::new(ctx),)+
|
||||
}
|
||||
}
|
||||
|
||||
$(pub fn $element(&self) -> &$element_ty {
|
||||
&self.$element
|
||||
})+
|
||||
|
||||
}
|
||||
};
|
||||
() => {};
|
||||
}
|
||||
|
||||
elementvec!({
|
||||
ppi, PPI
|
||||
});
|
||||
@ -1 +1,5 @@
|
||||
pub mod app;
|
||||
pub mod elements;
|
||||
pub mod elementvec;
|
||||
pub mod renderer;
|
||||
mod utils;
|
||||
|
||||
30
mp_elements/src/renderer/camera.rs
Normal file
30
mp_elements/src/renderer/camera.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use glam::*;
|
||||
pub struct Camera {
|
||||
pub position: Vec3,
|
||||
pub center: Vec3,
|
||||
pub up: Vec3,
|
||||
}
|
||||
|
||||
impl Default for Camera {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
position: Vec3::new(0.0, 0.0, 0.0),
|
||||
center: Vec3::new(0.0, 0.0, 0.0),
|
||||
up: Vec3::new(0.0, 0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new(position: Vec3, center: Vec3, up: Vec3) -> Self {
|
||||
Self {
|
||||
position,
|
||||
center,
|
||||
up,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Mat4 {
|
||||
Mat4::look_at_rh(self.position, self.center, self.up)
|
||||
}
|
||||
}
|
||||
2
mp_elements/src/renderer/mod.rs
Normal file
2
mp_elements/src/renderer/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod camera;
|
||||
pub mod projection;
|
||||
26
mp_elements/src/renderer/projection.rs
Normal file
26
mp_elements/src/renderer/projection.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use glam::*;
|
||||
pub struct Projection {
|
||||
aspect: f32,
|
||||
fovy: f32,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
}
|
||||
|
||||
impl Projection {
|
||||
pub fn new(width: u32, height: u32, fovy: f32, z_near: f32, z_far: f32) -> Self {
|
||||
Self {
|
||||
aspect: width as f32 / height as f32,
|
||||
fovy,
|
||||
znear: z_near,
|
||||
zfar: z_far,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.aspect = width as f32 / height as f32;
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Mat4 {
|
||||
Mat4::perspective_rh(self.fovy, self.aspect, self.znear, self.zfar)
|
||||
}
|
||||
}
|
||||
96
mp_elements/src/utils.rs
Normal file
96
mp_elements/src/utils.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
pub(crate) fn merge_shader<'a>(shader: &'a str) -> String {
|
||||
const TOOLS: &'static str = r#"// This is a tool that merges the shader code with the shader code from the shader module.
|
||||
|
||||
struct UniformCommonTools {
|
||||
model_matrix: mat4,
|
||||
view_matrix: mat4,
|
||||
proj_matrix: mat4,
|
||||
camera_position: vec3,
|
||||
camera_front: vec3,
|
||||
camera_up: vec3,
|
||||
light_position: vec3,
|
||||
light_color: vec3,
|
||||
light_intensity: float,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> common_tools: UniformCommonTools;
|
||||
"#;
|
||||
|
||||
let mut visited_files = std::collections::HashSet::new();
|
||||
let path: PathBuf = PathBuf::from(shader);
|
||||
|
||||
let base = match path.canonicalize() {
|
||||
Ok(path) => path.parent().unwrap().to_owned(),
|
||||
Err(e) => {
|
||||
panic!("Failed to canonicalize path: {}", e);
|
||||
}
|
||||
};
|
||||
|
||||
let mut result = process_file(&path, &mut visited_files, &base);
|
||||
|
||||
// 将工具代码插入到 shader 代码的开头
|
||||
result.insert_str(0, TOOLS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn process_file<'a, P: AsRef<std::path::Path> + 'a, B: AsRef<std::path::Path>>(
|
||||
file_path: P,
|
||||
visited_files: &mut std::collections::HashSet<std::path::PathBuf>,
|
||||
base_path: B,
|
||||
) -> String {
|
||||
let base_path = base_path.as_ref();
|
||||
let file_path = file_path.as_ref();
|
||||
|
||||
// 如果该文件已经处理过,则避免死循环
|
||||
if visited_files.contains(file_path) {
|
||||
return String::new(); // 返回空字符串表示已经处理过
|
||||
}
|
||||
|
||||
visited_files.insert(file_path.to_path_buf());
|
||||
|
||||
let content = std::fs::read_to_string(file_path).unwrap_or_else(|_| {
|
||||
panic!("Failed to read file: {}", file_path.display());
|
||||
});
|
||||
|
||||
let re = Regex::new(r#"#include\s+\"([^\"]+\.wgsl)\";"#).unwrap();
|
||||
|
||||
let result = re.replace_all(&content, |caps: ®ex::Captures| {
|
||||
let included_file = &caps[1]; // 获取文件名
|
||||
|
||||
let included_path = resolve_included_file(included_file, base_path.as_ref());
|
||||
|
||||
let included_content = process_file(
|
||||
&included_path,
|
||||
visited_files,
|
||||
base_path.parent().unwrap_or(base_path),
|
||||
);
|
||||
|
||||
included_content
|
||||
});
|
||||
|
||||
result.to_string()
|
||||
}
|
||||
|
||||
fn resolve_included_file(included_file: &str, base_path: &std::path::Path) -> std::path::PathBuf {
|
||||
let included_path = std::path::Path::new(included_file);
|
||||
|
||||
if included_path.is_relative() {
|
||||
base_path.join(included_path)
|
||||
} else {
|
||||
included_path.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
mod test {
|
||||
use super::merge_shader;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let merged = merge_shader("/Users/tsuki/projects/mp/mp_elements/shaders/ppi.wgsl");
|
||||
println!("{}", merged);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user