use crate::interaction::gizmo::{GizmoAxis, GizmoMode};
use crate::interaction::snap::ConstraintOverlay;
use crate::resources::{CameraUniform, ColormapId};
use crate::scene::material::Material;
pub(super) const INSTANCING_THRESHOLD: usize = 1;
#[derive(Debug, Clone)]
pub(crate) struct InstancedBatch {
pub mesh_index: usize,
pub texture_id: Option<u64>,
pub normal_map_id: Option<u64>,
pub ao_map_id: Option<u64>,
pub instance_offset: u32,
pub instance_count: u32,
pub is_transparent: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct ClipPlane {
pub normal: [f32; 3],
pub distance: f32,
pub enabled: bool,
pub cap_color: Option<[f32; 4]>,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ClipVolume {
None,
Plane {
normal: [f32; 3],
distance: f32,
},
Box {
center: [f32; 3],
half_extents: [f32; 3],
orientation: [[f32; 3]; 3],
},
Sphere {
center: [f32; 3],
radius: f32,
},
}
impl Default for ClipVolume {
fn default() -> Self {
ClipVolume::None
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ToneMapping {
Reinhard,
#[default]
Aces,
KhronosNeutral,
}
#[derive(Clone, Debug)]
pub struct PostProcessSettings {
pub enabled: bool,
pub tone_mapping: ToneMapping,
pub exposure: f32,
pub ssao: bool,
pub bloom: bool,
pub bloom_threshold: f32,
pub bloom_intensity: f32,
pub fxaa: bool,
pub contact_shadows: bool,
pub contact_shadow_max_distance: f32,
pub contact_shadow_steps: u32,
pub contact_shadow_thickness: f32,
}
impl Default for PostProcessSettings {
fn default() -> Self {
Self {
enabled: false,
tone_mapping: ToneMapping::Aces,
exposure: 1.0,
ssao: false,
bloom: false,
bloom_threshold: 1.0,
bloom_intensity: 0.1,
fxaa: false,
contact_shadows: false,
contact_shadow_max_distance: 0.5,
contact_shadow_steps: 16,
contact_shadow_thickness: 0.1,
}
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum LightKind {
Directional {
direction: [f32; 3],
},
Point {
position: [f32; 3],
range: f32,
},
Spot {
position: [f32; 3],
direction: [f32; 3],
range: f32,
inner_angle: f32,
outer_angle: f32,
},
}
#[derive(Clone, Debug)]
pub struct LightSource {
pub kind: LightKind,
pub color: [f32; 3],
pub intensity: f32,
}
impl Default for LightSource {
fn default() -> Self {
Self {
kind: LightKind::Directional {
direction: [0.3, 1.0, 0.5],
},
color: [1.0, 1.0, 1.0],
intensity: 1.0,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ShadowFilter {
#[default]
Pcf,
Pcss,
}
#[derive(Clone, Debug)]
pub struct LightingSettings {
pub lights: Vec<LightSource>,
pub shadow_bias: f32,
pub shadows_enabled: bool,
pub sky_color: [f32; 3],
pub ground_color: [f32; 3],
pub hemisphere_intensity: f32,
pub shadow_extent_override: Option<f32>,
pub shadow_cascade_count: u32,
pub cascade_split_lambda: f32,
pub shadow_atlas_resolution: u32,
pub shadow_filter: ShadowFilter,
pub pcss_light_radius: f32,
}
impl Default for LightingSettings {
fn default() -> Self {
Self {
lights: vec![LightSource::default()],
shadow_bias: 0.0001,
shadows_enabled: true,
sky_color: [0.8, 0.9, 1.0],
ground_color: [0.3, 0.2, 0.1],
hemisphere_intensity: 0.0,
shadow_extent_override: None,
shadow_cascade_count: 4,
cascade_split_lambda: 0.75,
shadow_atlas_resolution: 4096,
shadow_filter: ShadowFilter::Pcf,
pcss_light_radius: 0.02,
}
}
}
#[derive(Clone)]
#[non_exhaustive]
pub struct SceneRenderItem {
pub mesh_index: usize,
pub model: [[f32; 4]; 4],
pub selected: bool,
pub visible: bool,
pub show_normals: bool,
pub material: Material,
pub active_attribute: Option<crate::resources::AttributeRef>,
pub scalar_range: Option<(f32, f32)>,
pub colormap_id: Option<crate::resources::ColormapId>,
pub nan_color: Option<[f32; 4]>,
pub two_sided: bool,
}
impl Default for SceneRenderItem {
fn default() -> Self {
Self {
mesh_index: 0,
model: glam::Mat4::IDENTITY.to_cols_array_2d(),
selected: false,
visible: true,
show_normals: false,
material: Material::default(),
active_attribute: None,
scalar_range: None,
colormap_id: None,
nan_color: None,
two_sided: false,
}
}
}
#[derive(Debug, Clone)]
pub struct ScalarBar {
pub colormap_id: crate::resources::ColormapId,
pub scalar_min: f32,
pub scalar_max: f32,
pub title: String,
pub anchor: ScalarBarAnchor,
pub orientation: ScalarBarOrientation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScalarBarAnchor {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScalarBarOrientation {
Vertical,
Horizontal,
}
pub struct OverlayQuad {
pub corners: [[f32; 3]; 4],
pub color: [f32; 4],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PointRenderMode {
#[default]
ScreenSpaceCircle,
}
#[non_exhaustive]
pub struct PointCloudItem {
pub positions: Vec<[f32; 3]>,
pub colors: Vec<[f32; 4]>,
pub scalars: Vec<f32>,
pub scalar_range: Option<(f32, f32)>,
pub colormap_id: Option<ColormapId>,
pub point_size: f32,
pub default_color: [f32; 4],
pub model: [[f32; 4]; 4],
pub render_mode: PointRenderMode,
pub id: u64,
}
impl Default for PointCloudItem {
fn default() -> Self {
Self {
positions: Vec::new(),
colors: Vec::new(),
scalars: Vec::new(),
scalar_range: None,
colormap_id: None,
point_size: 4.0,
default_color: [1.0, 1.0, 1.0, 1.0],
model: glam::Mat4::IDENTITY.to_cols_array_2d(),
render_mode: PointRenderMode::ScreenSpaceCircle,
id: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GlyphType {
#[default]
Arrow,
Sphere,
Cube,
}
#[non_exhaustive]
pub struct GlyphItem {
pub positions: Vec<[f32; 3]>,
pub vectors: Vec<[f32; 3]>,
pub scale: f32,
pub scale_by_magnitude: bool,
pub magnitude_clamp: Option<(f32, f32)>,
pub scalars: Vec<f32>,
pub scalar_range: Option<(f32, f32)>,
pub colormap_id: Option<ColormapId>,
pub glyph_type: GlyphType,
pub model: [[f32; 4]; 4],
pub id: u64,
}
impl Default for GlyphItem {
fn default() -> Self {
Self {
positions: Vec::new(),
vectors: Vec::new(),
scale: 1.0,
scale_by_magnitude: true,
magnitude_clamp: None,
scalars: Vec::new(),
scalar_range: None,
colormap_id: None,
glyph_type: GlyphType::Arrow,
model: glam::Mat4::IDENTITY.to_cols_array_2d(),
id: 0,
}
}
}
#[non_exhaustive]
pub struct VolumeItem {
pub volume_id: crate::resources::VolumeId,
pub color_lut: Option<ColormapId>,
pub opacity_lut: Option<ColormapId>,
pub scalar_range: (f32, f32),
pub bbox_min: [f32; 3],
pub bbox_max: [f32; 3],
pub step_scale: f32,
pub model: [[f32; 4]; 4],
pub enable_shading: bool,
pub opacity_scale: f32,
pub threshold_min: f32,
pub threshold_max: f32,
pub nan_color: Option<[f32; 4]>,
}
impl Default for VolumeItem {
fn default() -> Self {
Self {
volume_id: crate::resources::VolumeId(0),
color_lut: None,
opacity_lut: None,
scalar_range: (0.0, 1.0),
bbox_min: [0.0, 0.0, 0.0],
bbox_max: [1.0, 1.0, 1.0],
step_scale: 1.0,
model: glam::Mat4::IDENTITY.to_cols_array_2d(),
enable_shading: false,
opacity_scale: 1.0,
threshold_min: 0.0,
threshold_max: 1.0,
nan_color: None,
}
}
}
#[non_exhaustive]
pub struct PolylineItem {
pub positions: Vec<[f32; 3]>,
pub scalars: Vec<f32>,
pub strip_lengths: Vec<u32>,
pub scalar_range: Option<(f32, f32)>,
pub colormap_id: Option<ColormapId>,
pub default_color: [f32; 4],
pub line_width: f32,
pub id: u64,
}
impl Default for PolylineItem {
fn default() -> Self {
Self {
positions: Vec::new(),
scalars: Vec::new(),
strip_lengths: Vec::new(),
scalar_range: None,
colormap_id: None,
default_color: [0.9, 0.92, 0.96, 1.0],
line_width: 2.0,
id: 0,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct StreamtubeItem {
pub positions: Vec<[f32; 3]>,
pub strip_lengths: Vec<u32>,
pub radius: f32,
pub color: [f32; 4],
pub id: u64,
}
impl Default for StreamtubeItem {
fn default() -> Self {
Self {
positions: Vec::new(),
strip_lengths: Vec::new(),
radius: 0.05,
color: [1.0, 1.0, 1.0, 1.0],
id: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FilterMode {
#[default]
Cpu,
Gpu,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ComputeFilterKind {
Clip {
plane_normal: [f32; 3],
plane_dist: f32,
},
ClipBox {
center: [f32; 3],
half_extents: [f32; 3],
orientation: [[f32; 3]; 3],
},
ClipSphere {
center: [f32; 3],
radius: f32,
},
Threshold {
min: f32,
max: f32,
},
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ComputeFilterItem {
pub mesh_index: usize,
pub kind: ComputeFilterKind,
pub attribute_name: Option<String>,
}
impl Default for ComputeFilterItem {
fn default() -> Self {
Self {
mesh_index: 0,
kind: ComputeFilterKind::Clip {
plane_normal: [0.0, 0.0, 1.0],
plane_dist: 0.0,
},
attribute_name: None,
}
}
}
#[non_exhaustive]
pub struct FrameData {
pub camera_uniform: CameraUniform,
pub lighting: LightingSettings,
pub eye_pos: [f32; 3],
pub scene_items: Vec<SceneRenderItem>,
pub wireframe_mode: bool,
pub gizmo_model: Option<glam::Mat4>,
pub gizmo_mode: GizmoMode,
pub gizmo_hovered: GizmoAxis,
pub gizmo_space_orientation: glam::Quat,
pub overlay_quads: Vec<OverlayQuad>,
pub constraint_overlays: Vec<ConstraintOverlay>,
pub show_grid: bool,
pub grid_cell_size: f32,
pub grid_half_extent: f32,
pub grid_y: f32,
pub show_axes_indicator: bool,
pub is_2d: bool,
pub viewport_size: [f32; 2],
pub camera_orientation: glam::Quat,
pub background_color: Option<[f32; 4]>,
pub clip_planes: Vec<ClipPlane>,
pub cap_fill_enabled: bool,
pub outline_selected: bool,
pub outline_color: [f32; 4],
pub outline_width_px: f32,
pub xray_selected: bool,
pub xray_color: [f32; 4],
pub post_process: PostProcessSettings,
pub camera_proj: glam::Mat4,
pub camera_view: glam::Mat4,
pub camera_near: f32,
pub camera_far: f32,
pub camera_fov: f32,
pub camera_aspect: f32,
pub scene_generation: u64,
pub selection_generation: u64,
pub point_clouds: Vec<PointCloudItem>,
pub glyphs: Vec<GlyphItem>,
pub polylines: Vec<PolylineItem>,
pub volumes: Vec<VolumeItem>,
pub viewport_index: usize,
pub compute_filter_items: Vec<ComputeFilterItem>,
pub isoline_items: Vec<crate::geometry::isoline::IsolineItem>,
pub streamtube_items: Vec<StreamtubeItem>,
pub clip_volume: ClipVolume,
}
impl Default for FrameData {
fn default() -> Self {
Self {
camera_uniform: CameraUniform {
view_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
eye_pos: [0.0, 0.0, 5.0],
_pad: 0.0,
forward: [0.0, 0.0, -1.0],
_pad1: 0.0,
},
lighting: LightingSettings::default(),
eye_pos: [0.0, 0.0, 5.0],
scene_items: Vec::new(),
wireframe_mode: false,
gizmo_model: None,
gizmo_mode: GizmoMode::Translate,
gizmo_hovered: GizmoAxis::None,
gizmo_space_orientation: glam::Quat::IDENTITY,
overlay_quads: Vec::new(),
constraint_overlays: Vec::new(),
show_grid: false,
grid_cell_size: 0.0,
grid_half_extent: 0.0,
grid_y: 0.0,
show_axes_indicator: true,
is_2d: false,
viewport_size: [800.0, 600.0],
camera_orientation: glam::Quat::IDENTITY,
background_color: None,
clip_planes: Vec::new(),
cap_fill_enabled: true,
outline_selected: false,
outline_color: [1.0, 0.5, 0.0, 1.0],
outline_width_px: 2.0,
xray_selected: false,
xray_color: [0.3, 0.7, 1.0, 0.25],
post_process: PostProcessSettings::default(),
camera_proj: glam::Mat4::IDENTITY,
camera_view: glam::Mat4::IDENTITY,
camera_near: 0.1,
camera_far: 1000.0,
camera_fov: std::f32::consts::FRAC_PI_4,
camera_aspect: 1.333,
scene_generation: 0,
selection_generation: 0,
point_clouds: Vec::new(),
glyphs: Vec::new(),
polylines: Vec::new(),
volumes: Vec::new(),
viewport_index: 0,
compute_filter_items: Vec::new(),
isoline_items: Vec::new(),
streamtube_items: Vec::new(),
clip_volume: ClipVolume::None,
}
}
}
macro_rules! emit_draw_calls {
($resources:expr, $render_pass:expr, $frame:expr, $use_instancing:expr, $batches:expr, $camera_bg:expr, $compute_filter_results:expr) => {{
let resources = $resources;
let render_pass = $render_pass;
let frame = $frame;
let use_instancing: bool = $use_instancing;
let compute_filter_results: &[crate::resources::ComputeFilterResult] = $compute_filter_results;
let batches: &[InstancedBatch] = $batches;
let camera_bg: &wgpu::BindGroup = $camera_bg;
render_pass.set_bind_group(0, camera_bg, &[]);
if frame.show_grid && !frame.is_2d {
render_pass.set_pipeline(&resources.grid_pipeline);
render_pass.set_bind_group(0, &resources.grid_bind_group, &[]);
render_pass.draw(0..3, 0..1);
render_pass.set_bind_group(0, camera_bg, &[]);
}
if !frame.scene_items.is_empty() {
if use_instancing && !batches.is_empty() {
let excluded_items: Vec<&SceneRenderItem> = frame
.scene_items
.iter()
.filter(|item| {
item.visible
&& (item.active_attribute.is_some() || item.two_sided)
&& resources
.mesh_store
.get(crate::resources::mesh_store::MeshId(item.mesh_index))
.is_some()
})
.collect();
let mut opaque_batches: Vec<&InstancedBatch> = Vec::new();
let mut transparent_batches: Vec<&InstancedBatch> = Vec::new();
for batch in batches {
if batch.is_transparent {
transparent_batches.push(batch);
} else {
opaque_batches.push(batch);
}
}
if !opaque_batches.is_empty() && !frame.wireframe_mode {
if let Some(ref pipeline) = resources.solid_instanced_pipeline {
render_pass.set_pipeline(pipeline);
for batch in &opaque_batches {
let Some(mesh) = resources.mesh_store.get(crate::resources::mesh_store::MeshId(batch.mesh_index)) else { continue };
let mat_key = (
batch.texture_id.unwrap_or(u64::MAX),
batch.normal_map_id.unwrap_or(u64::MAX),
batch.ao_map_id.unwrap_or(u64::MAX),
);
let Some(inst_tex_bg) = resources.instance_bind_groups.get(&mat_key) else { continue };
render_pass.set_bind_group(1, inst_tex_bg, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(
0..mesh.index_count,
0,
batch.instance_offset..batch.instance_offset + batch.instance_count,
);
}
}
}
if !transparent_batches.is_empty() && !frame.wireframe_mode {
if let Some(ref pipeline) = resources.transparent_instanced_pipeline {
render_pass.set_pipeline(pipeline);
for batch in &transparent_batches {
let Some(mesh) = resources.mesh_store.get(crate::resources::mesh_store::MeshId(batch.mesh_index)) else { continue };
let mat_key = (
batch.texture_id.unwrap_or(u64::MAX),
batch.normal_map_id.unwrap_or(u64::MAX),
batch.ao_map_id.unwrap_or(u64::MAX),
);
let Some(inst_tex_bg) = resources.instance_bind_groups.get(&mat_key) else { continue };
render_pass.set_bind_group(1, inst_tex_bg, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(
0..mesh.index_count,
0,
batch.instance_offset..batch.instance_offset + batch.instance_count,
);
}
}
}
if frame.wireframe_mode {
for item in &frame.scene_items {
if !item.visible { continue; }
let Some(mesh) = resources.mesh_store.get(crate::resources::mesh_store::MeshId(item.mesh_index)) else { continue };
render_pass.set_pipeline(&resources.wireframe_pipeline);
render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(mesh.edge_index_buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
}
} else {
for item in &excluded_items {
let Some(mesh) = resources
.mesh_store
.get(crate::resources::mesh_store::MeshId(item.mesh_index))
else {
continue;
};
let pipeline = if item.material.opacity < 1.0 {
&resources.transparent_pipeline
} else if item.two_sided {
&resources.solid_two_sided_pipeline
} else {
&resources.solid_pipeline
};
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(
mesh.index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
}
}
} else {
let eye = glam::Vec3::from(frame.eye_pos);
let dist_from_eye = |item: &&SceneRenderItem| -> f32 {
let pos = glam::Vec3::new(
item.model[3][0],
item.model[3][1],
item.model[3][2],
);
(pos - eye).length()
};
let mut opaque: Vec<&SceneRenderItem> = Vec::new();
let mut transparent: Vec<&SceneRenderItem> = Vec::new();
for item in &frame.scene_items {
if !item.visible || resources.mesh_store.get(crate::resources::mesh_store::MeshId(item.mesh_index)).is_none() {
continue;
}
if item.material.opacity < 1.0 {
transparent.push(item);
} else {
opaque.push(item);
}
}
opaque.sort_by(|a, b| dist_from_eye(a).partial_cmp(&dist_from_eye(b)).unwrap_or(std::cmp::Ordering::Equal));
transparent.sort_by(|a, b| dist_from_eye(b).partial_cmp(&dist_from_eye(a)).unwrap_or(std::cmp::Ordering::Equal));
macro_rules! draw_item {
($item:expr, $pipeline:expr) => {{
let item = $item;
let mesh = resources.mesh_store.get(crate::resources::mesh_store::MeshId(item.mesh_index)).unwrap();
render_pass.set_bind_group(1, &mesh.object_bind_group, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
if frame.wireframe_mode {
render_pass.set_pipeline(&resources.wireframe_pipeline);
render_pass.set_index_buffer(
mesh.edge_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..mesh.edge_index_count, 0, 0..1);
} else {
let filter_result = compute_filter_results
.iter()
.find(|r| r.mesh_index == item.mesh_index);
render_pass.set_pipeline($pipeline);
if let Some(fr) = filter_result {
render_pass.set_index_buffer(
fr.index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..fr.index_count, 0, 0..1);
} else {
render_pass.set_index_buffer(
mesh.index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
}
}
if item.show_normals {
if let Some(ref nl_buf) = mesh.normal_line_buffer {
if mesh.normal_line_count > 0 {
render_pass.set_pipeline(&resources.wireframe_pipeline);
render_pass.set_bind_group(1, &mesh.normal_bind_group, &[]);
render_pass.set_vertex_buffer(0, nl_buf.slice(..));
render_pass.draw(0..mesh.normal_line_count, 0..1);
}
}
}
}};
}
for item in &opaque {
let pl = if item.two_sided {
&resources.solid_two_sided_pipeline
} else {
&resources.solid_pipeline
};
draw_item!(item, pl);
}
for item in &transparent {
draw_item!(item, &resources.transparent_pipeline);
}
}
}
if frame.gizmo_model.is_some() && resources.gizmo_index_count > 0 {
render_pass.set_pipeline(&resources.gizmo_pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
render_pass.set_bind_group(1, &resources.gizmo_bind_group, &[]);
render_pass.set_vertex_buffer(0, resources.gizmo_vertex_buffer.slice(..));
render_pass.set_index_buffer(
resources.gizmo_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..resources.gizmo_index_count, 0, 0..1);
}
if !resources.bc_quad_buffers.is_empty() {
render_pass.set_pipeline(&resources.overlay_pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for (vbuf, ibuf, _ubuf, bg) in &resources.bc_quad_buffers {
render_pass.set_bind_group(1, bg, &[]);
render_pass.set_vertex_buffer(0, vbuf.slice(..));
render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..6, 0, 0..1);
}
}
if !resources.constraint_line_buffers.is_empty() {
render_pass.set_pipeline(&resources.overlay_line_pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for (vbuf, ibuf, index_count, _ubuf, bg) in &resources.constraint_line_buffers {
render_pass.set_bind_group(1, bg, &[]);
render_pass.set_vertex_buffer(0, vbuf.slice(..));
render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..*index_count, 0, 0..1);
}
}
if !resources.cap_buffers.is_empty() {
render_pass.set_pipeline(&resources.overlay_pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for (vbuf, ibuf, idx_count, _ubuf, bg) in &resources.cap_buffers {
render_pass.set_bind_group(1, bg, &[]);
render_pass.set_vertex_buffer(0, vbuf.slice(..));
render_pass.set_index_buffer(ibuf.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..*idx_count, 0, 0..1);
}
}
if !resources.xray_object_buffers.is_empty() {
render_pass.set_pipeline(&resources.xray_pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for (mesh_idx, _buf, bg) in &resources.xray_object_buffers {
let Some(mesh) = resources.mesh_store.get(crate::resources::mesh_store::MeshId(*mesh_idx)) else { continue };
render_pass.set_bind_group(1, bg, &[]);
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..mesh.index_count, 0, 0..1);
}
}
if !resources.outline_object_buffers.is_empty() {
if let (Some(pipeline), Some(bg)) = (
&resources.outline_composite_pipeline_msaa,
&resources.outline_composite_bind_group,
) {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, bg, &[]);
render_pass.draw(0..3, 0..1);
}
}
if frame.show_axes_indicator && resources.axes_vertex_count > 0 {
render_pass.set_pipeline(&resources.axes_pipeline);
render_pass.set_vertex_buffer(0, resources.axes_vertex_buffer.slice(..));
render_pass.draw(0..resources.axes_vertex_count, 0..1);
}
}};
}
macro_rules! emit_scivis_draw_calls {
($resources:expr, $render_pass:expr, $pc_gpu_data:expr, $glyph_gpu_data:expr, $polyline_gpu_data:expr, $volume_gpu_data:expr, $streamtube_gpu_data:expr, $camera_bg:expr) => {{
let resources = $resources;
let render_pass = $render_pass;
let camera_bg: &wgpu::BindGroup = $camera_bg;
if !$pc_gpu_data.is_empty() {
if let Some(ref pipeline) = resources.point_cloud_pipeline {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for pc in $pc_gpu_data.iter() {
render_pass.set_bind_group(1, &pc.bind_group, &[]);
render_pass.set_vertex_buffer(0, pc.vertex_buffer.slice(..));
render_pass.draw(0..6, 0..pc.point_count);
}
}
}
if !$glyph_gpu_data.is_empty() {
if let Some(ref pipeline) = resources.glyph_pipeline {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for glyph in $glyph_gpu_data.iter() {
render_pass.set_bind_group(1, &glyph.uniform_bind_group, &[]);
render_pass.set_bind_group(2, &glyph.instance_bind_group, &[]);
render_pass.set_vertex_buffer(0, glyph.mesh_vertex_buffer.slice(..));
render_pass.set_index_buffer(
glyph.mesh_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..glyph.mesh_index_count, 0, 0..glyph.instance_count);
}
}
}
if !$polyline_gpu_data.is_empty() {
if let Some(ref pipeline) = resources.polyline_pipeline {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for pl in $polyline_gpu_data.iter() {
render_pass.set_bind_group(1, &pl.bind_group, &[]);
render_pass.set_vertex_buffer(0, pl.vertex_buffer.slice(..));
for range in &pl.strip_ranges {
render_pass.draw(range.clone(), 0..1);
}
}
}
}
if !$volume_gpu_data.is_empty() {
if let Some(ref pipeline) = resources.volume_pipeline {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for vol in $volume_gpu_data.iter() {
render_pass.set_bind_group(1, &vol.bind_group, &[]);
render_pass.set_vertex_buffer(0, vol.vertex_buffer.slice(..));
render_pass
.set_index_buffer(vol.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
render_pass.draw_indexed(0..36, 0, 0..1);
}
}
}
if !$streamtube_gpu_data.is_empty() {
if let Some(ref pipeline) = resources.streamtube_pipeline {
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, camera_bg, &[]);
for tube in $streamtube_gpu_data.iter() {
render_pass.set_bind_group(1, &tube.uniform_bind_group, &[]);
render_pass.set_bind_group(2, &tube.instance_bind_group, &[]);
render_pass.set_vertex_buffer(0, tube.mesh_vertex_buffer.slice(..));
render_pass.set_index_buffer(
tube.mesh_index_buffer.slice(..),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..tube.mesh_index_count, 0, 0..tube.instance_count);
}
}
}
}};
}