Compare commits
2 Commits
2b7a562b30
...
4caf348587
| Author | SHA1 | Date | |
|---|---|---|---|
| 4caf348587 | |||
| e541933ae1 |
63
Cargo.lock
generated
63
Cargo.lock
generated
@ -233,6 +233,20 @@ name = "bytemuck"
|
|||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
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]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@ -581,6 +595,18 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
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]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -732,8 +758,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -758,6 +786,9 @@ name = "glam"
|
|||||||
version = "0.29.2"
|
version = "0.29.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
@ -808,7 +839,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"presser",
|
"presser",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"windows 0.56.0",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1415,6 +1446,12 @@ dependencies = [
|
|||||||
name = "mp_elements"
|
name = "mp_elements"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"flume",
|
||||||
|
"glam",
|
||||||
|
"mp_core",
|
||||||
|
"pollster",
|
||||||
|
"regex",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1439,6 +1476,15 @@ dependencies = [
|
|||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nanorand"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-derive-backend-ohos"
|
name = "napi-derive-backend-ohos"
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
@ -1711,6 +1757,12 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pollster"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@ -2144,6 +2196,15 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"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]]
|
[[package]]
|
||||||
name = "spirv"
|
name = "spirv"
|
||||||
version = "0.3.0+sdk-1.3.268.0"
|
version = "0.3.0+sdk-1.3.268.0"
|
||||||
|
|||||||
@ -4,4 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bytemuck = { version = "1.19.0", features = ["derive"] }
|
||||||
|
glam = { version = "0.29.2", features = ["bytemuck"] }
|
||||||
|
regex = "1.11.1"
|
||||||
wgpu = "23.0.0"
|
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,35 @@
|
|||||||
use wgpu::{core::instance, Backends, Instance, RequestAdapterOptions};
|
use std::collections::HashMap;
|
||||||
pub struct App {}
|
|
||||||
|
use crate::elements::{ElementAttach, Elements, ElementsRef};
|
||||||
|
use crate::elementvec::ElementVec;
|
||||||
|
use wgpu::{Backends, Instance};
|
||||||
|
|
||||||
const BACKENDS_DEFAULT: u32 = Backends::DX12.bits()
|
const BACKENDS_DEFAULT: u32 = Backends::DX12.bits()
|
||||||
| Backends::METAL.bits()
|
| Backends::METAL.bits()
|
||||||
| Backends::GL.bits()
|
| Backends::GL.bits()
|
||||||
| Backends::BROWSER_WEBGPU.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 {
|
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 {
|
let instance = Instance::new(wgpu::InstanceDescriptor {
|
||||||
backends: Backends::from_bits(BACKENDS_DEFAULT).unwrap(),
|
backends: Backends::from_bits(BACKENDS_DEFAULT).unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Request an adapter, which is a handle to a physical device.
|
||||||
let adapter = instance
|
let adapter = instance
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
power_preference: wgpu::PowerPreference::default(),
|
||||||
@ -22,27 +39,279 @@ impl App {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.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
|
let (device, queue) = adapter
|
||||||
.request_device(&Default::default(), None)
|
.request_device(&Default::default(), None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let texture_size = 256u32;
|
// Create a new instance of the common utils struct. This struct contains the bind group layout, bind group, and uniform buffer.
|
||||||
let texture_desc = wgpu::TextureDescriptor {
|
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 {
|
size: wgpu::Extent3d {
|
||||||
width: texture_size,
|
width: 800,
|
||||||
height: texture_size,
|
height: 600,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
},
|
},
|
||||||
mip_level_count: 1,
|
mip_level_count: 1,
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
||||||
label: None,
|
|
||||||
view_formats: &[],
|
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());
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
mp_elements/src/elements/mod.rs
Normal file
111
mp_elements/src/elements/mod.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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),
|
||||||
|
}
|
||||||
408
mp_elements/src/elements/ppi.rs
Normal file
408
mp_elements/src/elements/ppi.rs
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
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 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