From 876e88ec95418886f43cc769b1ed7fc5a5f6b279 Mon Sep 17 00:00:00 2001 From: Thinkofdeath Date: Mon, 21 Sep 2015 21:11:30 +0100 Subject: [PATCH] Basic logo impl --- resources/assets/minecraft/texts/splashes.txt | 3 +- src/main.rs | 4 +- src/render/ui.rs | 17 +- src/ui/logo.rs | 92 ++++++++- src/ui/mod.rs | 185 +++++++++++++++++- 5 files changed, 283 insertions(+), 18 deletions(-) diff --git a/resources/assets/minecraft/texts/splashes.txt b/resources/assets/minecraft/texts/splashes.txt index 241e3e1..1229825 100644 --- a/resources/assets/minecraft/texts/splashes.txt +++ b/resources/assets/minecraft/texts/splashes.txt @@ -18,7 +18,7 @@ I blame Mojang The logo is totally not ascii art rendered as textures Look, it works on my machine. Open Source! https://github.com/thinkofdeath/steven -Built with Go! +Built with Rust! try { } catch (Exception e) { } panic(recover()) // Abandon hope all ye who enter here @@ -50,3 +50,4 @@ No touchy the topic Ceci n'est pas un splash. Xor is not actually a cat. The MD5 of md_5 is e14cfacdd442a953343ebd8529138680 +I blame Scetch for this! diff --git a/src/main.rs b/src/main.rs index a78b2a5..3552d86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,7 @@ fn main() { let mut last_frame = time::now(); let frame_time = (time::Duration::seconds(1).num_nanoseconds().unwrap() as f64) / 60.0; - let logo = ui::logo::Logo::new(resource_manager.clone(), &mut renderer, &mut ui_container); + let mut logo = ui::logo::Logo::new(resource_manager.clone(), &mut renderer, &mut ui_container); while !window.should_close() { { resource_manager.write().unwrap().tick(); } @@ -71,6 +71,8 @@ fn main() { last_frame = now; let delta = (diff.num_nanoseconds().unwrap() as f64) / frame_time; + logo.tick(&mut renderer, &mut ui_container); + let (width, height) = window.get_framebuffer_size(); ui_container.tick(&mut renderer, delta, width as f64, height as f64); renderer.tick(delta, width as u32, height as u32); diff --git a/src/render/ui.rs b/src/render/ui.rs index 5737e12..e81923a 100644 --- a/src/render/ui.rs +++ b/src/render/ui.rs @@ -28,7 +28,7 @@ const UI_HEIGHT: f64 = 480.0; pub struct UIState { textures: Arc>, resources: Arc>, - version: usize, + pub version: usize, data: Vec, prev_size: usize, @@ -97,7 +97,7 @@ impl UIState { pos += 1; } - UIState { + let mut state = UIState { textures: textures, resources: res, version: 0xFFFF, @@ -126,7 +126,9 @@ impl UIState { char_map: char_map, page_width: 0.0, page_height: 0.0, - } + }; + state.load_font(); + state } pub fn tick(&mut self, width: u32, height: u32) { @@ -168,7 +170,6 @@ impl UIState { gl::draw_elements(gl::TRIANGLES, self.count, self.index_type, 0); } - gl::disable(gl::BLEND); self.data.clear(); self.count = 0; @@ -176,7 +177,7 @@ impl UIState { pub fn add_bytes(&mut self, data: &Vec) { self.data.extend(data); - self.count += (data.len() / (28 + 4)) * 6; + self.count += (data.len() / (28 * 4)) * 6; } pub fn character_texture(&mut self, c: char) -> render::Texture { @@ -195,7 +196,7 @@ impl UIState { let p = self.font_pages[page as usize].clone().unwrap(); let raw = if page == 0 { - self.char_map[&c] as u32 + (*self.char_map.get(&c).unwrap_or(&c)) as u32 } else { raw }; @@ -235,7 +236,7 @@ impl UIState { } let r = c as u32; if r >> 8 == 0 { - let r = self.char_map[&c] as u32; + let r = (*self.char_map.get(&c).unwrap_or(&c)) as u32; let info = self.font_character_info[r as usize]; let sw = self.page_width / 16.0; return (((info.1 - info.0) as f64) / sw) * 16.0; @@ -315,7 +316,7 @@ impl UIState { let page = raw >> 8; if page == 0 { - raw = self.char_map[&ch] as u32; + raw = (*self.char_map.get(&ch).unwrap_or(&ch)) as u32; } let info = self.font_character_info[raw as usize]; let w = if page == 0 { diff --git a/src/ui/logo.rs b/src/ui/logo.rs index efd2f27..23bb41b 100644 --- a/src/ui/logo.rs +++ b/src/ui/logo.rs @@ -1,17 +1,37 @@ use std::sync::{Arc, RwLock}; +use std::f64::consts; use ui; use render; use resources; +use time; +use rand; +use rand::Rng; pub struct Logo { resources: Arc>, + + shadow: ui::ElementRef, + layer0: ui::ElementRef, + + text: ui::ElementRef, + text_base_scale: f64, + text_orig_x: f64, + text_index: isize, + text_strings: Vec, } impl Logo { pub fn new(resources: Arc>, renderer: &mut render::Renderer, ui_container: &mut ui::Container) -> Logo { let mut l = Logo { - resources: resources + resources: resources, + shadow: Default::default(), + layer0: Default::default(), + text: Default::default(), + text_base_scale: 0.0, + text_orig_x: 0.0, + text_index: -1, + text_strings: Vec::new(), }; l.init(renderer, ui_container); l @@ -77,8 +97,74 @@ impl Logo { } row += 1; } + { + let mut splashes = res.open_all("minecraft", "texts/splashes.txt"); + for file in &mut splashes { + let mut texts = String::new(); + file.read_to_string(&mut texts).unwrap(); + for line in texts.lines() { + self.text_strings.push(line.to_owned().replace("\r", "")); + } + } + let mut r: rand::XorShiftRng = rand::SeedableRng::from_seed([45, 64, 32, 12]); + r.shuffle(&mut self.text_strings[..]); + } - ui_container.add(shadow_batch); - ui_container.add(layer0); + shadow_batch.set_height(row as f64 * 8.0); + layer0.set_height(row as f64 * 8.0); + + self.shadow = ui_container.add(shadow_batch); + self.layer0 = ui_container.add(layer0); + + let mut txt = ui::Text::new(renderer, "", 0.0, -8.0, 255, 255, 0); + txt.set_h_attach(ui::HAttach::Right); + txt.set_v_attach(ui::VAttach::Bottom); + txt.set_parent(&self.shadow); + txt.set_rotation(-consts::PI / 8.0); + + let width = txt.get_width(); + self.text_base_scale = 300.0 / width; + if self.text_base_scale > 1.0 { + self.text_base_scale = 1.0; + } + txt.set_x((-width / 2.0) * self.text_base_scale); + self.text_orig_x = txt.get_x(); + self.text = ui_container.add(txt); + } + + pub fn tick(&mut self, renderer: &mut render::Renderer, ui_container: &mut ui::Container) { + let now = time::now().to_timespec(); + + // Splash text + let text = ui_container.get_mut(&self.text); + let text_index = (now.sec / 15) as isize % self.text_strings.len() as isize; + if self.text_index != text_index { + self.text_index = text_index; + text.set_text(renderer, &self.text_strings[text_index as usize]); + let width = text.get_width(); + self.text_base_scale = 300.0 / width; + if self.text_base_scale > 1.0 { + self.text_base_scale = 1.0; + } + text.set_x((-width / 2.0) * self.text_base_scale); + self.text_orig_x = text.get_x(); + } + + let timer = now.nsec as f64 / 1000000000.0; + let mut offset = timer / 0.5; + if offset > 1.0 { + offset = 2.0 - offset; + } + offset = ((offset * consts::PI).cos() + 1.0) / 2.0; + text.set_scale_x((0.7 + (offset / 3.0)) * self.text_base_scale); + text.set_scale_y((0.7 + (offset / 3.0)) * self.text_base_scale); + let scale = text.get_scale_x(); + text.set_x(self.text_orig_x * scale * self.text_base_scale); + } + + pub fn remove(self, ui_container: &mut ui::Container) { + ui_container.remove(&self.shadow); + ui_container.remove(&self.layer0); + ui_container.remove(&self.text); } } \ No newline at end of file diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8dffdbb..3984b56 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -25,6 +25,7 @@ const SCALED_HEIGHT: f64 = 480.0; pub enum Element { Image(Image), Batch(Batch), + Text(Text), None, } @@ -70,7 +71,7 @@ impl Element { fn get_size(&self) -> (f64, f64) { match self { $( - &Element::$name(ref val) => (val.width, val.height), + &Element::$name(ref val) => val.get_size(), )+ _ => unimplemented!(), } @@ -94,6 +95,15 @@ impl Element { } } + fn update(&mut self, renderer: &mut render::Renderer) { + match self { + $( + &mut Element::$name(ref mut val) => val.update(renderer), + )+ + _ => unimplemented!(), + } + } + fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec{ match self { $( @@ -108,7 +118,8 @@ impl Element { element_impl!( Image, - Batch + Batch, + Text ); pub enum Mode { @@ -160,6 +171,15 @@ struct ElementRefInner { index: usize, } +impl Default for ElementRef { + fn default() -> Self { + ElementRef { + inner: ElementRefInner{ index: 0 }, + ty: PhantomData, + } + } +} + const SCREEN: Region = Region{x: 0.0, y: 0.0, w: SCALED_WIDTH, h: SCALED_HEIGHT}; pub struct Container { @@ -167,6 +187,7 @@ pub struct Container { elements: HashMap, // We need the order elements_list: Vec, + version: usize, last_sw: f64, last_sh: f64, @@ -180,6 +201,7 @@ impl Container { mode: Mode::Scaled, elements: HashMap::new(), elements_list: Vec::new(), + version: 0xFFFF, last_sw: 0.0, last_sh: 0.0, last_width: 0.0, @@ -219,14 +241,20 @@ impl Container { Mode::Unscaled(scale) => (scale, scale), }; - if self.last_sw != sw || self.last_sh != sh || self.last_width != width || self.last_height != height { + if self.last_sw != sw || self.last_sh != sh + || self.last_width != width || self.last_height != height + || self.version != renderer.ui.version { self.last_sw = sw; self.last_sh = sh; self.last_width = width; self.last_height = height; for (_, e) in &mut self.elements { e.set_dirty(true); + if self.version != renderer.ui.version { + e.update(renderer); + } } + self.version = renderer.ui.version; } // Borrow rules seem to prevent us from doing this in the first pass @@ -375,6 +403,8 @@ impl Image { } } + fn update(&mut self, renderer: &mut render::Renderer) {} + fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec { if self.dirty { self.dirty = false; @@ -390,7 +420,11 @@ impl Image { &self.data } - pub fn set_parent(&mut self, other: ElementRef) { + pub fn get_size(&self) -> (f64, f64) { + (self.width, self.height) + } + + pub fn set_parent(&mut self, other: &ElementRef) { self.parent = Some(other.inner); self.dirty = true; } @@ -481,6 +515,10 @@ impl Batch { } } + fn update(&mut self, renderer: &mut render::Renderer) { + + } + fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec { if self.dirty { self.dirty = false; @@ -498,7 +536,11 @@ impl Batch { &self.data } - pub fn set_parent(&mut self, other: ElementRef) { + pub fn get_size(&self) -> (f64, f64) { + (self.width, self.height) + } + + pub fn set_parent(&mut self, other: &ElementRef) { self.parent = Some(other.inner); self.dirty = true; } @@ -536,3 +578,136 @@ impl UIElement for Batch { } } } + +pub struct Text { + dirty: bool, + data: Vec, + + parent: Option, + should_draw: bool, + layer: isize, + val: String, + x: f64, + y: f64, + width: f64, + height: f64, + v_attach: VAttach, + h_attach: HAttach, + scale_x: f64, + scale_y: f64, + rotation: f64, + r: u8, + g: u8, + b: u8, + a: u8, +} + +impl Text { + pub fn new(renderer: &render::Renderer, val: &str, x: f64, y: f64, r: u8, g: u8, b: u8) -> Text { + Text { + dirty: true, + data: Vec::new(), + + parent: None, + should_draw: true, + layer: 0, + val: val.to_owned(), + x: x, + y: y, + width: renderer.ui.size_of_string(val), + height: 18.0, + v_attach: VAttach::Top, + h_attach: HAttach::Left, + scale_x: 1.0, + scale_y: 1.0, + rotation: 0.0, + r: r, + g: g, + b: b, + a: 255, + } + } + + fn update(&mut self, renderer: &mut render::Renderer) { + self.width = renderer.ui.size_of_string(&self.val); + } + + fn draw(&mut self, renderer: &mut render::Renderer, r: &Region, width: f64, height: f64, delta: f64) -> &Vec { + if self.dirty { + self.dirty = false; + let sx = r.w / self.width; + let sy = r.h / self.height; + let mut text = if self.rotation == 0.0 { + renderer.ui.new_text_scaled(&self.val, r.x, r.y, sx*self.scale_x, sy*self.scale_y, self.r, self.g, self.b) + } else { + let c = self.rotation.cos(); + let s = self.rotation.sin(); + let tmpx = r.w / 2.0; + let tmpy = r.h / 2.0; + let w = (tmpx*c - tmpy*s).abs(); + let h = (tmpy*c + tmpx*s).abs(); + renderer.ui.new_text_rotated(&self.val, r.x+w-(r.w / 2.0), r.y+h-(r.h / 2.0), sx*self.scale_x, sy*self.scale_y, self.rotation, self.r, self.g, self.b) + }; + for e in &mut text.elements { + e.a = self.a; + e.layer = self.layer; + } + self.data = text.bytes(width, height); + } + &self.data + } + + pub fn get_size(&self) -> (f64, f64) { + ((self.width + 2.0) * self.scale_x, self.height * self.scale_y) + } + + pub fn set_parent(&mut self, other: &ElementRef) { + self.parent = Some(other.inner); + self.dirty = true; + } + + pub fn get_text(&self) -> &str { + &self.val + } + + pub fn set_text(&mut self, renderer: &render::Renderer, val: &str) { + self.dirty = true; + self.val = val.to_owned(); + self.width = renderer.ui.size_of_string(val); + } + + lazy_field!(layer, isize, get_layer, set_layer); + lazy_field!(x, f64, get_x, set_x); + lazy_field!(y, f64, get_y, set_y); + lazy_field!(width, f64, get_width, set_width); + lazy_field!(height, f64, get_height, set_height); + lazy_field!(v_attach, VAttach, get_v_attach, set_v_attach); + lazy_field!(h_attach, HAttach, get_h_attach, set_h_attach); + lazy_field!(scale_x, f64, get_scale_x, set_scale_x); + lazy_field!(scale_y, f64, get_scale_y, set_scale_y); + lazy_field!(rotation, f64, get_rotation, set_rotation); + lazy_field!(r, u8, get_r, set_r); + lazy_field!(g, u8, get_g, set_g); + lazy_field!(b, u8, get_b, set_b); + +} + +impl UIElement for Text { + fn wrap(self) -> Element { + Element::Text(self) + } + + fn unwrap_ref<'a>(e: &'a Element) -> &'a Text { + match e { + &Element::Text(ref val) => val, + _ => panic!("Incorrect type"), + } + } + + fn unwrap_ref_mut<'a>(e: &'a mut Element) -> &'a mut Text { + match e { + &mut Element::Text(ref mut val) => val, + _ => panic!("Incorrect type"), + } + } +}