use draw_state::state::{Blend, BlendChannel, BlendValue, Equation, Factor};
use gfx_glutin::WindowUpdateExt;
use glutin::{PossiblyCurrent, WindowedContext};
use parking_lot::Mutex;
use std::cmp::max;
use std::iter::Extend;
use std::sync::Arc;
use gfx::format::RenderFormat;
use gfx::handle::{DepthStencilView, RenderTargetView};
use gfx::traits::FactoryExt;
use gfx::{self, *};
use gfx_text::{Error as GfxTextError, HorizontalAnchor, Renderer as TextRenderer, VerticalAnchor};
use old_school_gfx_glutin_ext as gfx_glutin;
use super::ref_values::RefValuesWrapper;
use crate::primitive::create_quad;
use crate::renderer::{ColorFormat, DepthFormat, R};
use crate::vertex::*;
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLEND: Blend = Blend {
color: BlendChannel {
equation: Equation::Add,
source: Factor::Zero,
destination: Factor::ZeroPlus(BlendValue::ConstAlpha),
},
alpha: BlendChannel {
equation: Equation::Add,
source: Factor::Zero,
destination: Factor::Zero,
},
};
gfx_defines! {
pipeline bg_pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
out: gfx::BlendTarget<ColorFormat> = (
"Target0",
gfx::state::ColorMask::all(),
BLEND,
),
ref_values: RefValuesWrapper = RefValuesWrapper::new(),
}
}
pub struct StatsRenderer<F: Factory<R> + Clone> {
#[allow(unused)]
corner: Corner,
offset: (u32, u32),
padding: i32,
col_spacing: i32,
text: Arc<Mutex<String>>,
renderer: Option<TextRenderer<R, F>>,
factory: Option<F>,
window_dimensions: Option<(f32, f32)>,
bg_depth: Option<DepthStencilView<R, DepthFormat>>,
bg_pso: Option<PipelineState<R, bg_pipe::Meta>>,
bg_slice: Option<Slice<R>>,
bg_data: Option<bg_pipe::Data<R>>,
}
impl<F: Factory<R> + Clone> StatsRenderer<F> {
pub fn new(corner: Corner) -> Self {
StatsRenderer {
corner,
offset: (0, 0),
padding: 0,
col_spacing: 0,
text: Arc::new(Mutex::new(String::new())),
renderer: None,
factory: None,
window_dimensions: None,
bg_depth: None,
bg_pso: None,
bg_slice: None,
bg_data: None,
}
}
pub fn init(
&mut self,
mut factory: F,
window_dimensions: (f32, f32),
main_color: RenderTargetView<R, ColorFormat>,
main_depth: DepthStencilView<R, DepthFormat>,
font_size: u8,
offset: (u32, u32),
padding: i32,
col_spacing: i32,
) -> Result<(), GfxTextError> {
self.window_dimensions = Some(window_dimensions);
self.offset = offset;
self.padding = padding;
self.col_spacing = col_spacing;
self.renderer = Some(
gfx_text::new(factory.clone())
.with_size(font_size)
.build()?,
);
self.bg_pso = Some(
factory
.create_pipeline_simple(
include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/stats_bg.glslv"
)),
include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/stats_bg.glslf"
)),
bg_pipe::new(),
)
.unwrap(),
);
let bg_plane = create_quad((-1f32, 0f32), (0.2f32, 0.95f32));
let (vertex_buffer, slice) = bg_plane.create_vertex_buffer(&mut factory);
self.bg_slice = Some(slice);
self.bg_data = Some(bg_pipe::Data {
vbuf: vertex_buffer,
out: main_color,
ref_values: (),
});
self.factory = Some(factory);
self.bg_depth = Some(main_depth);
Ok(())
}
pub fn text(&self) -> Arc<Mutex<String>> {
self.text.clone()
}
pub fn has_text(&self) -> bool {
self.text.lock().trim().is_empty()
}
pub fn set_text(&self, text: String) {
*self.text.lock() = text;
}
pub fn draw<C: CommandBuffer<R>, T: RenderFormat>(
&mut self,
encoder: &mut Encoder<R, C>,
target: &RenderTargetView<R, T>,
) -> Result<(), GfxTextError> {
if self.renderer.is_none() || self.has_text() {
return Ok(());
}
let renderer = self.renderer.as_mut().unwrap();
let bounds = Self::scene_draw_format(
self.offset,
self.padding,
self.col_spacing,
renderer,
&self.text.lock(),
);
if bounds != (0f32, 0f32) {
if self.bg_slice.is_some() && self.bg_pso.is_some() && self.bg_data.is_some() {
let win = self.window_dimensions.unwrap();
let w = bounds.0 / win.0 * 2f32;
let h = bounds.1 / win.1 * 2f32;
let x = -1f32 + self.offset.0 as f32 / win.0 * 2f32;
let y = 1f32 - self.offset.1 as f32 / win.1 * 2f32 - h;
let (vertex_buffer, slice) = create_quad((x, y), (w, h))
.create_vertex_buffer(self.factory.as_mut().unwrap());
*self.bg_slice.as_mut().unwrap() = slice;
self.bg_data.as_mut().unwrap().vbuf = vertex_buffer;
encoder.draw(
self.bg_slice.as_ref().unwrap(),
self.bg_pso.as_ref().unwrap(),
self.bg_data.as_ref().unwrap(),
);
}
}
renderer.draw(encoder, target)
}
fn scene_draw_format(
pos: (u32, u32),
padding: i32,
col_spacing: i32,
renderer: &mut TextRenderer<R, F>,
text: &str,
) -> (f32, f32) {
Self::scene_draw_table(
pos,
padding,
col_spacing,
renderer,
text.split("\n")
.map(|row| row.split("\t").collect())
.collect(),
)
}
fn scene_draw_table(
pos: (u32, u32),
padding: i32,
col_spacing: i32,
renderer: &mut TextRenderer<R, F>,
text: Vec<Vec<&str>>,
) -> (f32, f32) {
let bounds: Vec<Vec<(i32, i32)>> = text
.iter()
.map(|col| col.iter().map(|text| renderer.measure(text)).collect())
.collect();
let rows_max: Vec<i32> = bounds
.iter()
.map(|col| col.iter().map(|size| size.1).max().unwrap_or(0))
.collect();
let mut cols_max: Vec<i32> = bounds
.iter()
.map(|row| row.iter().map(|size| size.0).collect())
.fold(Vec::new(), |acc: Vec<i32>, row: Vec<i32>| {
let mut out: Vec<i32> = acc
.iter()
.zip(row.iter())
.map(|(a, b)| max(*a, *b))
.collect();
let out_len = out.len();
if out_len < acc.len() || out_len < row.len() {
out.extend(acc.iter().skip(out_len));
out.extend(row.iter().skip(out_len));
}
out
});
cols_max
.iter_mut()
.rev()
.skip(1)
.map(|width| *width += col_spacing)
.count();
for (row, text) in text.iter().enumerate() {
for (col, text) in text.iter().enumerate() {
let (mut x, mut y): (i32, i32) = (
cols_max.iter().take(col).sum::<i32>(),
rows_max.iter().take(row).sum::<i32>(),
);
x += pos.0 as i32 + padding;
y += pos.1 as i32 + padding;
renderer.add_anchored(
text,
[x, y],
HorizontalAnchor::Left,
VerticalAnchor::Top,
WHITE,
);
}
}
(
cols_max.iter().sum::<i32>() as f32 + padding as f32 * 2f32,
rows_max.iter().sum::<i32>() as f32 + padding as f32 * 2f32,
)
}
pub fn update_views(
&mut self,
window: &WindowedContext<PossiblyCurrent>,
dimensions: (f32, f32),
) {
if let Some(data) = self.bg_data.as_mut() {
window.update_gfx(&mut data.out, self.bg_depth.as_mut().unwrap());
}
self.window_dimensions = Some(dimensions);
}
}
pub enum Corner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}