Compare commits

..

No commits in common. "4caf3485875faa1bdf3140cb73b85e0b270d86d6" and "2b7a562b308eda25f02c4bff45811341ddbdfdac" have entirely different histories.

14 changed files with 12 additions and 1151 deletions

63
Cargo.lock generated
View File

@ -233,20 +233,6 @@ 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"
@ -595,18 +581,6 @@ 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"
@ -758,10 +732,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -786,9 +758,6 @@ name = "glam"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
dependencies = [
"bytemuck",
]
[[package]]
name = "glow"
@ -839,7 +808,7 @@ dependencies = [
"log",
"presser",
"thiserror",
"windows 0.58.0",
"windows 0.56.0",
]
[[package]]
@ -1446,12 +1415,6 @@ dependencies = [
name = "mp_elements"
version = "0.1.0"
dependencies = [
"bytemuck",
"flume",
"glam",
"mp_core",
"pollster",
"regex",
"wgpu",
]
@ -1476,15 +1439,6 @@ 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"
@ -1757,12 +1711,6 @@ 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"
@ -2196,15 +2144,6 @@ 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"

View File

@ -4,10 +4,4 @@ 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"

View File

@ -1,29 +0,0 @@
@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);
}

View File

@ -1,5 +0,0 @@
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;

View File

@ -1,65 +0,0 @@
#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;
}

View File

@ -1,35 +1,18 @@
use std::collections::HashMap;
use crate::elements::{ElementAttach, Elements, ElementsRef};
use crate::elementvec::ElementVec;
use wgpu::{Backends, Instance};
use wgpu::{core::instance, Backends, Instance, RequestAdapterOptions};
pub struct App {}
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>,
buffer_pool: DataBufferPool,
}
impl App {
pub async fn instant() -> Self {
// 修复方法名称拼写错误
// Instance is a handle to the backend. It is used to create adapters.
pub async fn inistant() -> Self {
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(),
@ -39,279 +22,27 @@ 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();
// 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"),
let texture_size = 256u32;
let texture_desc = wgpu::TextureDescriptor {
size: wgpu::Extent3d {
width: 800,
height: 600,
width: texture_size,
height: texture_size,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
label: None,
view_formats: &[],
});
// 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());
// Buffer pool
let buffer_pool = DataBufferPool::new();
Self {
device,
queue,
common_utils,
pipelines: None,
_texture: texture,
texture_view,
buffer_pool,
}
}
// 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<'b, 'a: 'b> {
pub elements: Vec<(&'b ElementAttach, ElementsRef<'a>)>, // 修复字段访问权限
}
impl<'b, 'a: 'b> DrawList<'b, 'a> {
pub fn new() -> Self {
Self { elements: vec![] }
}
pub fn push<Ele: Into<ElementsRef<'a>>>(&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 DataBufferPool {
buffers: HashMap<BufferKey, wgpu::Buffer>,
}
impl DataBufferPool {
pub fn new() -> Self {
Self {
buffers: HashMap::new(),
}
}
pub fn get_or_create_buffer(&mut self) {}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct BufferKey {
pub size: u64,
pub from: String,
}
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;
app.init().await;
let pipelines = app.pipelines();
let ctx = app.ctx();
let ppi = pipelines.ppi();
if let Ok(data) = data {
let first_block = data.first().unwrap();
// Convert the first block into a PPI struct.
if let Ok(data) = first_block.try_into() {
let buffer_pool = &mut app.buffer_pool;
// Reused buffer is None, so a new attachment is created.
let attachment = ppi.new_attachment(&ctx, buffer_pool);
// Load the data into the attachment.
ppi.load_data(&ctx, data, &attachment);
// Create a new draw list and push the attachment into it.
let mut draw_list = DrawList::new();
draw_list.push(ppi, &attachment);
// Draw the elements in the draw list.
app.draw(draw_list);
}
} else {
panic!("Failed to load data");
}
})
Self {}
}
}

View File

@ -1,111 +0,0 @@
pub mod ppi;
use crate::app::{Ctx, DataBufferPool};
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),)+
}
pub enum ElementsRef<'a> {
$($element_name(&'a $element),)+
}
$(
impl From<$element> for Elements {
fn from(element: $element) -> Self {
Elements::$element_name(element)
}
}
impl<'a> From<&'a $element> for ElementsRef<'a> {
fn from(element: &'a $element) -> Self {
ElementsRef::$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),)+
}
}
}
impl<'a> ElementsRef<'a> {
pub fn pipeline(&'a self) -> &wgpu::RenderPipeline {
match self {
$(ElementsRef::$element_name(element) => element.pipeline(),)+
}
}
pub fn draw(&'a self, attach: &ElementAttach, render_pass: &mut wgpu::RenderPass) {
match self {
$(ElementsRef::$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<'a>(&self, ctx: &Ctx, buffer_pool: &'a mut DataBufferPool) -> 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);
fn load_data(&self, ctx: &Ctx, data: &Self::Data, attach: &ElementAttach);
}
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),
}

View File

@ -1,408 +0,0 @@
use std::{ops::Sub, result};
use crate::{
app::{Ctx, DataBufferPool},
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<'a>(&self, ctx: &Ctx, buffer_pool: &'a mut DataBufferPool) -> ElementAttach {
let device = ctx.device;
// Buffers
let data_buffer = buffer_pool.get_or_create_buffer();
// let data_buffer = if let Some(reused) = reused_buffer {
// reused[0]
// } else {
// &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);
}
fn load_data(&self, ctx: &Ctx, data: &Self::Data, attach: &ElementAttach) {
let (vertex, index) = self.bake(data);
let queue = ctx.queue;
// Update Vertex Buffer
let vertex_buffer = &attach.vertex_buffer;
queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(&vertex));
// Update Index Buffer
if let Some(indices) = index {
let index_buffer = attach.index_buffer.as_ref().unwrap();
queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(&indices));
}
// Load Data
let data_array = data.get_data();
let data_array_f32 = data_array.cast_to::<f32>();
queue.write_buffer(
data_buffer,
0,
bytemuck::cast_slice(data_array_f32.as_slice().unwrap()),
);
}
}
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)
}

View File

@ -1,27 +0,0 @@
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
});

View File

@ -1,5 +1 @@
pub mod app;
pub mod elements;
pub mod elementvec;
pub mod renderer;
mod utils;

View File

@ -1,30 +0,0 @@
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)
}
}

View File

@ -1,2 +0,0 @@
pub mod camera;
pub mod projection;

View File

@ -1,26 +0,0 @@
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)
}
}

View File

@ -1,96 +0,0 @@
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: &regex::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);
}
}