use crate::Bundle;
use rinecs::{EntitiesThreadLocal, ResourcesThreadLocal, Read};
use ringui::{self, resizable_container, Label, sep_h, sep_v, styled};
use rin::events::{StreamT, StreamRc, Property};
use rin::window::{self, WindowT, Events, KeyEvents, KeyModifiers};
use std::ops::{Deref, DerefMut};
use rin::gl::{self, Renderer3d, Renderer2d};
use rin::color::consts::WHITE;
use rin::graphics::Mvp;
use rin::math::Rect;
use na::{pnt2, vec2, convert};
use crate::renderer::{
shadow,
components::RenderPlane,
resources::ScreenRenderBuffer,
};
use glin::OffscreenBuffer;
use crate::Scene;
pub struct Gui{
gui: ringui::ResizableContainer<ringui::Tabs>,
render: Property<'static, bool>,
#[cfg(feature="stats")]
gui_render_time: Property<'static, f32>,
#[cfg(feature="stats")]
gui_compute_time: Property<'static, f32>
}
impl Deref for Gui{
type Target = ringui::Tabs;
fn deref(&self) -> &ringui::Tabs{
self.gui.control()
}
}
impl DerefMut for Gui{
fn deref_mut(&mut self) -> &mut ringui::Tabs{
self.gui.control_mut()
}
}
impl Gui{
pub fn new<S: StreamT<'static, window::Event>>(mode: resizable_container::Mode, events: S, viewport: Property<'static, Rect<i32>>) -> Gui{
let events = events.rc();
let (events, gui_compute_time) = events.clone().benchmark(events.update().map(|_| ()));
let events = events.rc();
Gui{
render: events.clone().keys()
.pressed_with_mods('g', KeyModifiers::CONTROL)
.fold(true, |render, _| !render)
.to_property(true),
gui: ringui::ResizableContainer::new_tabs(mode, events, viewport),
#[cfg(feature="stats")]
gui_render_time: Property::new(0.),
#[cfg(feature="stats")]
gui_compute_time: gui_compute_time.map(|d| d.as_secs() as f32 * 1000. + d.subsec_nanos() as f32 / 1_000_000.)
.fold(0., |prev, new| ((prev * 0.95 + new * 0.05) * 1000.) as i32 as f32 / 1000.)
.to_property(0.)
}
}
pub fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.gui.non_attended()
}
}
impl Bundle for Gui{
fn register(_world: &mut Scene){
}
fn setup(mut self, world: &mut Scene){
world.add_system_with_name_thread_local_gpu(gui_renderer, "gui renderer");
#[cfg(feature="stats")]
{
let image_enabled_handle = ringui::utils::load_texture_from_memory(include_bytes!("textures/onoff_enabled.png"))
.unwrap();
let image_hovered_handle = ringui::utils::load_texture_from_memory(include_bytes!("textures/onoff_hovered.png"))
.unwrap();
let image_pressed_handle = ringui::utils::load_texture_from_memory(include_bytes!("textures/onoff_pressed.png"))
.unwrap();
#[derive(Serialize, Deserialize)]
struct ButtonImages{
image_enabled_handle: ringui::utils::TextureHandle,
image_hovered_handle: ringui::utils::TextureHandle,
image_pressed_handle: ringui::utils::TextureHandle,
}
let buttons_json_style = serde_json::to_string(&ButtonImages{
image_enabled_handle,
image_hovered_handle,
image_pressed_handle,
}).unwrap();
let compute_time = ringui::RangedParameter::new(self.gui_compute_time.clone(), 0. .. 16.);
let stats = self.new_group("cpu stats");
let mut times = world.stats().map(|(n,p)| (n.to_owned(), p)).collect::<Vec<_>>();
let mut enableds = world.enabled_systems().collect::<Vec<_>>();
times.sort_by(|(n1,_), (n2,_)| n1.cmp(n2));
enableds.sort_by(|(n1,_), (n2,_)| n1.cmp(n2));
for ((name1, time), (name2, enabled)) in times.into_iter().zip(enableds) {
assert_eq!(name1, name2);
let time_prop = time
.map(|d| d.as_secs() as f32 * 1000. + d.subsec_nanos() as f32 / 1_000_000.)
.stream()
.fold(0., |prev, new| ((prev * 0.95 + new * 0.05) * 1000.) as i32 as f32 / 1000.)
.to_property(0.);
let label = time_prop.clone().zip(enabled.clone())
.map(move|(t, e)| if e {
format!("{}: {}", name1, t)
}else{
format!("{} [disabled]", name1)
});
let time = ringui::RangedParameter::new(time_prop, 0. .. 16.);
let enabled = styled(enabled, &buttons_json_style);
stats.add(name2, group_v!{
group_h!(enabled, sep_h(10.), time),
sep_v(2.),
as_control!(Label, label)
});
}
stats.add("gui compute time", compute_time);
if let Some((_n, p)) = world.stats().find(|(name, _p)| *name == "gui renderer"){
self.gui_render_time = p
.map(|d| d.as_secs() as f32 * 1000. + d.subsec_nanos() as f32 / 1_000_000.)
.stream()
.fold(0., |prev, new| ((prev * 0.95 + new * 0.05) * 1000.) as i32 as f32 / 1000.)
.to_property(0.);
}
let gpu_stats = self.new_group("gpu stats");
let gl = world.resource::<gl::Renderer>().unwrap().creation_proxy();
let mut times = world.gpu_stats(&gl).map(|(n,p)| (n.to_owned(), p)).collect::<Vec<_>>();
times.sort_by(|(n1,_), (n2,_)| n1.cmp(n2));
for (name, time) in times.into_iter() {
let time_prop = time
.map(|d| d.as_secs() as f32 * 1000. + d.subsec_nanos() as f32 / 1_000_000.)
.stream()
.fold(0., |prev, new| ((prev * 0.95 + new * 0.05) * 1000.) as i32 as f32 / 1000.)
.to_property(0.);
let time = ringui::RangedParameter::new(time_prop, 0. .. 16.);
gpu_stats.add(&name, time);
}
}
world.add_resource_thread_local(self);
}
}
fn gui_renderer(entities: EntitiesThreadLocal, resources: ResourcesThreadLocal){
let mut gui = resources.get_mut::<Gui>().unwrap();
let glin = resources.get::<gl::Renderer<'static>>().unwrap();
let mut window = resources.get_mut::<window::Window>().unwrap();
let glin = glin.with_mvp(Mvp::ortho_top_left(window.viewport()));
#[cfg(feature="stats")]
glin.draw_bitmap_string(&gui.gui_render_time.to_string(), window.width() as f32 - 60., 60., &WHITE);
#[cfg(feature="stats")]
glin.draw_bitmap_string(&gui.gui_compute_time.to_string(), window.width() as f32 - 60., 80., &WHITE);
if !*gui.render { return; }
gui.gui.update(&mut window);
glin.draw(&**gui);
#[cfg(all(feature = "blender", feature="gl"))]
{
let show_allocators = false;
if show_allocators{
let h = 18.;
let y = window.height() as f32 - (h * 3. + 30.);
let pos = pnt2(10., y);
let w = window.width() as f32 - 20.;
use crate::blender;
type B = glin::SharedBufferStorage<u8>;
let allocator = resources.get::<super::renderer::Allocator<blender::Vertex, B>>()
.unwrap();
allocator.debug_draw(&glin, &pos, w, h);
}
}
let show_depth_prepass = false;
if show_depth_prepass{
if let Some(fbo) = resources.get::<ScreenRenderBuffer>(){
if let Some(depth_prepass) = fbo.depth_prepass() {
let pos = pnt2(window.width() as f32 - 256. * depth_prepass.aspect_ratio() - 10., 10.);
glin.draw_size(depth_prepass.depth_tex().unwrap(), &pos, &vec2(256. * depth_prepass.aspect_ratio(), 256.));
}
}
}
let show_shadow_maps = false;
if show_shadow_maps {
let mut pos = pnt2(window.width() - 266, 10);
let shadows_pool = resources.get::<shadow::ShadowMapPool>().unwrap();
for shadow_map in entities.iter_for::<(Read<shadow::Map>)>() {
let shadow_map = shadows_pool[**shadow_map].debug_draw_sampler();
glin.draw_size(shadow_map, &convert(pos), &vec2(256., 256.));
pos += vec2(0, 266);
if pos.y + 256 > window.height() {
pos.x -= 266;
pos.y = 10;
}
}
for shadow_map in entities.iter_for::<(Read<shadow::StaticMap>)>() {
let shadow_map = shadows_pool[**shadow_map].debug_draw_sampler();
glin.draw_size(shadow_map, &convert(pos), &vec2(256., 256.));
pos += vec2(0, 266);
if pos.y + 256 > window.height() {
pos.x -= 266;
pos.y = 10;
}
}
}
let render_planes = false;
if render_planes{
let mut pos = pnt2(10, 10);
for renderplanes in entities.iter_for::<(Read<RenderPlane>)>() {
for renderplane in renderplanes {
let map = renderplane.render_buffer().color_tex(0).unwrap();
let ratio = map.width() as f32 / map.height() as f32;
glin.draw_size(map, &convert(pos), &vec2(256., 256. / ratio));
let hi = (256. / ratio) as i32;
pos += vec2(0, hi);
if pos.y + hi > window.height() {
pos.x -= 266;
pos.y = 10;
}
}
}
}
}