use crate::core::sstorage::ImmutableString;
use crate::renderer::framework::state::{BlendFactor, BlendFunc};
use crate::{
core::{algebra::Vector2, math::Matrix4Ext, math::Rect, scope_profile},
renderer::framework::{
error::FrameworkError,
framebuffer::{DrawParameters, FrameBuffer},
geometry_buffer::{
AttributeDefinition, AttributeKind, BufferBuilder, ElementKind, GeometryBuffer,
GeometryBufferBuilder, GeometryBufferKind,
},
gpu_program::{GpuProgram, UniformLocation},
gpu_texture::GpuTexture,
state::PipelineState,
},
renderer::{RenderPassStatistics, TextureCache},
scene::{camera::Camera, graph::Graph, node::Node, particle_system},
};
use std::{cell::RefCell, rc::Rc};
struct ParticleSystemShader {
program: GpuProgram,
view_projection_matrix: UniformLocation,
world_matrix: UniformLocation,
camera_side_vector: UniformLocation,
camera_up_vector: UniformLocation,
diffuse_texture: UniformLocation,
depth_buffer_texture: UniformLocation,
inv_screen_size: UniformLocation,
proj_params: UniformLocation,
soft_boundary_sharpness_factor: UniformLocation,
}
impl ParticleSystemShader {
fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
let vertex_source = include_str!("shaders/particle_system_vs.glsl");
let fragment_source = include_str!("shaders/particle_system_fs.glsl");
let program = GpuProgram::from_source(
state,
"ParticleSystemShader",
vertex_source,
fragment_source,
)?;
Ok(Self {
view_projection_matrix: program
.uniform_location(state, &ImmutableString::new("viewProjectionMatrix"))?,
world_matrix: program.uniform_location(state, &ImmutableString::new("worldMatrix"))?,
camera_side_vector: program
.uniform_location(state, &ImmutableString::new("cameraSideVector"))?,
camera_up_vector: program
.uniform_location(state, &ImmutableString::new("cameraUpVector"))?,
diffuse_texture: program
.uniform_location(state, &ImmutableString::new("diffuseTexture"))?,
depth_buffer_texture: program
.uniform_location(state, &ImmutableString::new("depthBufferTexture"))?,
inv_screen_size: program
.uniform_location(state, &ImmutableString::new("invScreenSize"))?,
proj_params: program.uniform_location(state, &ImmutableString::new("projParams"))?,
soft_boundary_sharpness_factor: program
.uniform_location(state, &ImmutableString::new("softBoundarySharpnessFactor"))?,
program,
})
}
}
pub struct ParticleSystemRenderer {
shader: ParticleSystemShader,
draw_data: particle_system::draw::DrawData,
geometry_buffer: GeometryBuffer,
sorted_particles: Vec<u32>,
}
pub(in crate) struct ParticleSystemRenderContext<'a, 'b, 'c> {
pub state: &'a mut PipelineState,
pub framebuffer: &'b mut FrameBuffer,
pub graph: &'c Graph,
pub camera: &'c Camera,
pub white_dummy: Rc<RefCell<GpuTexture>>,
pub depth: Rc<RefCell<GpuTexture>>,
pub frame_width: f32,
pub frame_height: f32,
pub viewport: Rect<i32>,
pub texture_cache: &'a mut TextureCache,
}
impl ParticleSystemRenderer {
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
let geometry_buffer = GeometryBufferBuilder::new(ElementKind::Triangle)
.with_buffer_builder(
BufferBuilder::new::<crate::scene::particle_system::draw::Vertex>(
GeometryBufferKind::DynamicDraw,
None,
)
.with_attribute(AttributeDefinition {
location: 0,
kind: AttributeKind::Float3,
normalized: false,
divisor: 0,
})
.with_attribute(AttributeDefinition {
location: 1,
kind: AttributeKind::Float2,
normalized: false,
divisor: 0,
})
.with_attribute(AttributeDefinition {
location: 2,
kind: AttributeKind::Float,
normalized: false,
divisor: 0,
})
.with_attribute(AttributeDefinition {
location: 3,
kind: AttributeKind::Float,
normalized: false,
divisor: 0,
})
.with_attribute(AttributeDefinition {
location: 4,
kind: AttributeKind::UnsignedByte4,
normalized: true,
divisor: 0,
}),
)
.build(state)?;
Ok(Self {
shader: ParticleSystemShader::new(state)?,
draw_data: Default::default(),
geometry_buffer,
sorted_particles: Vec::new(),
})
}
#[must_use]
pub(in crate) fn render(&mut self, args: ParticleSystemRenderContext) -> RenderPassStatistics {
scope_profile!();
let mut statistics = RenderPassStatistics::default();
let ParticleSystemRenderContext {
state,
framebuffer,
graph,
camera,
white_dummy,
depth,
frame_width,
frame_height,
viewport,
texture_cache,
} = args;
let inv_view = camera.inv_view_matrix().unwrap();
let view_proj = camera.view_projection_matrix();
let camera_up = inv_view.up();
let camera_side = inv_view.side();
let inv_screen_size = Vector2::new(1.0 / frame_width, 1.0 / frame_height);
let proj_params = Vector2::new(camera.projection().z_far(), camera.projection().z_near());
for node in graph.linear_iter() {
let particle_system = if let Node::ParticleSystem(particle_system) = node {
particle_system
} else {
continue;
};
particle_system.generate_draw_data(
&mut self.sorted_particles,
&mut self.draw_data,
&camera.global_position(),
);
self.geometry_buffer
.set_buffer_data(state, 0, self.draw_data.vertices());
self.geometry_buffer
.bind(state)
.set_triangles(self.draw_data.triangles());
let global_transform = node.global_transform();
let draw_params = DrawParameters {
cull_face: None,
color_write: Default::default(),
depth_write: false,
stencil_test: None,
depth_test: true,
blend: Some(BlendFunc {
sfactor: BlendFactor::SrcAlpha,
dfactor: BlendFactor::OneMinusSrcAlpha,
}),
stencil_op: Default::default(),
};
let diffuse_texture = particle_system
.texture_ref()
.and_then(|t| texture_cache.get(state, t))
.unwrap_or_else(|| white_dummy.clone());
statistics += framebuffer.draw(
&self.geometry_buffer,
state,
viewport,
&self.shader.program,
&draw_params,
|mut program_binding| {
program_binding
.set_texture(&self.shader.depth_buffer_texture, &depth)
.set_texture(&self.shader.diffuse_texture, &diffuse_texture)
.set_vector3(&self.shader.camera_side_vector, &camera_side)
.set_vector3(&self.shader.camera_up_vector, &camera_up)
.set_matrix4(&self.shader.view_projection_matrix, &view_proj)
.set_matrix4(&self.shader.world_matrix, &global_transform)
.set_vector2(&self.shader.inv_screen_size, &inv_screen_size)
.set_vector2(&self.shader.proj_params, &proj_params)
.set_f32(
&self.shader.soft_boundary_sharpness_factor,
particle_system.soft_boundary_sharpness_factor(),
);
},
);
}
statistics
}
}