use crate::{
core::{
math::{Matrix4Ext, Rect},
scope_profile,
sstorage::ImmutableString,
},
renderer::{
framework::{
error::FrameworkError,
framebuffer::{CullFace, DrawParameters, FrameBuffer},
geometry_buffer::{GeometryBuffer, GeometryBufferKind},
gpu_program::{GpuProgram, UniformLocation},
gpu_texture::GpuTexture,
state::{BlendFactor, BlendFunc, PipelineState},
},
RenderPassStatistics, TextureCache,
},
scene::{camera::Camera, graph::Graph, mesh::surface::SurfaceData, node::Node},
};
use std::{cell::RefCell, rc::Rc};
struct SpriteShader {
program: GpuProgram,
view_projection_matrix: UniformLocation,
world_matrix: UniformLocation,
camera_side_vector: UniformLocation,
camera_up_vector: UniformLocation,
color: UniformLocation,
diffuse_texture: UniformLocation,
size: UniformLocation,
rotation: UniformLocation,
}
impl SpriteShader {
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
let fragment_source = include_str!("shaders/sprite_fs.glsl");
let vertex_source = include_str!("shaders/sprite_vs.glsl");
let program =
GpuProgram::from_source(state, "SpriteShader", 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"))?,
size: program.uniform_location(state, &ImmutableString::new("size"))?,
diffuse_texture: program
.uniform_location(state, &ImmutableString::new("diffuseTexture"))?,
color: program.uniform_location(state, &ImmutableString::new("color"))?,
rotation: program.uniform_location(state, &ImmutableString::new("rotation"))?,
program,
})
}
}
pub struct SpriteRenderer {
shader: SpriteShader,
collapsed_quad: GeometryBuffer,
}
pub(in crate) struct SpriteRenderContext<'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 viewport: Rect<i32>,
pub textures: &'a mut TextureCache,
}
impl SpriteRenderer {
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
let surface = GeometryBuffer::from_surface_data(
&SurfaceData::make_collapsed_xy_quad(),
GeometryBufferKind::StaticDraw,
state,
);
Ok(Self {
shader: SpriteShader::new(state)?,
collapsed_quad: surface,
})
}
#[must_use]
pub(in crate) fn render(&mut self, args: SpriteRenderContext) -> RenderPassStatistics {
scope_profile!();
let mut statistics = RenderPassStatistics::default();
let SpriteRenderContext {
state,
framebuffer,
graph,
camera,
white_dummy,
viewport,
textures,
} = args;
let initial_view_projection = camera.view_projection_matrix();
let inv_view = camera.inv_view_matrix().unwrap();
let camera_up = inv_view.up();
let camera_side = inv_view.side();
for sprite in graph.linear_iter().filter_map(|node| {
if !node.global_visibility() {
return None;
}
if let Node::Sprite(sprite) = node {
Some(sprite)
} else {
None
}
}) {
let view_projection = if sprite.depth_offset_factor() != 0.0 {
let mut projection = camera.projection_matrix();
projection[14] -= sprite.depth_offset_factor();
projection * camera.view_matrix()
} else {
initial_view_projection
};
let diffuse_texture = if let Some(texture) = sprite.texture_ref() {
if let Some(texture) = textures.get(state, texture) {
texture
} else {
white_dummy.clone()
}
} else {
white_dummy.clone()
};
statistics += framebuffer.draw(
&self.collapsed_quad,
state,
viewport,
&self.shader.program,
&DrawParameters {
cull_face: Some(CullFace::Back),
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(),
},
|mut program_binding| {
program_binding
.set_texture(&self.shader.diffuse_texture, &diffuse_texture)
.set_matrix4(&self.shader.view_projection_matrix, &view_projection)
.set_matrix4(&self.shader.world_matrix, &sprite.global_transform())
.set_vector3(&self.shader.camera_up_vector, &camera_up)
.set_vector3(&self.shader.camera_side_vector, &camera_side)
.set_f32(&self.shader.size, sprite.size())
.set_linear_color(&self.shader.color, &sprite.color())
.set_f32(&self.shader.rotation, sprite.rotation());
},
);
}
statistics
}
}