| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265 |
- use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
- use freetype as ft;
- use miniquad::{
- conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferId, BufferLayout,
- BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
- PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TextureId,
- UniformDesc, UniformType, VertexAttribute, VertexFormat,
- };
- use std::{
- array::IntoIter,
- fmt,
- io::Cursor,
- sync::{mpsc, Arc, MutexGuard},
- time::{Duration, Instant},
- };
- use crate::{
- error::{Error, Result},
- chatview,
- editbox,
- expr::{SExprMachine, SExprVal},
- keysym::{KeyCodeAsStr, MouseButtonAsU8},
- prop::{Property, PropertySubType, PropertyType},
- res::{ResourceId, ResourceManager},
- scene::{
- MethodResponseFn, SceneGraph, SceneGraphPtr, SceneNode, SceneNodeId, SceneNodeInfo,
- SceneNodeType, Pimpl
- },
- shader,
- };
- type Color = [f32; 4];
- pub const COLOR_RED: Color = [1., 0., 0., 1.];
- pub const COLOR_DARKGREY: Color = [0.2, 0.2, 0.2, 1.];
- pub const COLOR_GREEN: Color = [0., 1., 0., 1.];
- pub const COLOR_BLUE: Color = [0., 0., 1., 1.];
- pub const COLOR_WHITE: Color = [1., 1., 1., 1.];
- #[derive(Debug, SerialEncodable, SerialDecodable)]
- #[repr(C)]
- struct Vertex {
- pos: [f32; 2],
- color: [f32; 4],
- uv: [f32; 2],
- }
- #[derive(SerialEncodable, SerialDecodable)]
- #[repr(C)]
- struct Face {
- idxs: [u32; 3],
- }
- impl fmt::Debug for Face {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self.idxs)
- }
- }
- struct Mesh {
- pub verts: Vec<Vertex>,
- pub faces: Vec<Face>,
- pub vertex_buffer: BufferId,
- pub index_buffer: BufferId,
- }
- pub struct Point<T> {
- pub x: T,
- pub y: T,
- }
- #[derive(Debug, Clone)]
- pub struct Rectangle<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::cmp::PartialOrd> {
- pub x: T,
- pub y: T,
- pub w: T,
- pub h: T,
- }
- impl<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::ops::AddAssign + std::cmp::PartialOrd> Rectangle<T> {
- fn from_array(arr: [T; 4]) -> Self {
- let mut iter = IntoIter::new(arr);
- Self {
- x: iter.next().unwrap(),
- y: iter.next().unwrap(),
- w: iter.next().unwrap(),
- h: iter.next().unwrap(),
- }
- }
- pub fn clip(&self, other: &Self) -> Option<Self> {
- if other.x + other.w < self.x ||
- other.x > self.x + self.w ||
- other.y + other.h < self.y ||
- other.y > self.y + self.h
- {
- return None
- }
- let mut clipped = other.clone();
- if clipped.x < self.x {
- clipped.x = self.x;
- clipped.w = other.x + other.w - clipped.x;
- }
- if clipped.y < self.y {
- clipped.y = self.y;
- clipped.h = other.y + other.h - clipped.y;
- }
- if clipped.x + clipped.w > self.x + self.w {
- clipped.w = self.x + self.w - clipped.x;
- }
- if clipped.y + clipped.h > self.y + self.h {
- clipped.h = self.y + self.h - clipped.y;
- }
- Some(clipped)
- }
- pub fn contains(&self, point: &Point<T>) -> bool {
- self.x < point.x && point.x < self.x + self.w &&
- self.y < point.y && point.y < self.y + self.h
- }
- }
- pub type FreetypeFace = ft::Face<&'static [u8]>;
- #[derive(Debug)]
- enum GraphicsMethodEvent {
- LoadTexture,
- DeleteTexture,
- CreateChatView,
- CreateEditBox,
- }
- struct Stage {
- ctx: Box<dyn RenderingBackend>,
- pipeline: Pipeline,
- scene_graph: SceneGraphPtr,
- textures: ResourceManager<TextureId>,
- font_faces: Vec<FreetypeFace>,
- method_recvr: mpsc::Receiver<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
- method_sender: mpsc::SyncSender<(GraphicsMethodEvent, SceneNodeId, Vec<u8>, MethodResponseFn)>,
- last_draw_time: Option<Instant>,
- }
- impl Stage {
- const WHITE_TEXTURE_ID: ResourceId = 0;
- pub fn new(scene_graph: SceneGraphPtr) -> Self {
- let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
- let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
- let mut textures = ResourceManager::new();
- let white_texture_id = textures.alloc(white_texture);
- assert_eq!(white_texture_id, Self::WHITE_TEXTURE_ID);
- let mut shader_meta: ShaderMeta = shader::meta();
- shader_meta.uniforms.uniforms.push(UniformDesc::new("Projection", UniformType::Mat4));
- shader_meta.uniforms.uniforms.push(UniformDesc::new("Model", UniformType::Mat4));
- let shader = ctx
- .new_shader(
- match ctx.info().backend {
- Backend::OpenGl => ShaderSource::Glsl {
- vertex: shader::GL_VERTEX,
- fragment: shader::GL_FRAGMENT,
- },
- Backend::Metal => ShaderSource::Msl { program: shader::METAL },
- },
- shader_meta,
- )
- .unwrap();
- let params = PipelineParams {
- color_blend: Some(BlendState::new(
- Equation::Add,
- BlendFactor::Value(BlendValue::SourceAlpha),
- BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
- )),
- ..Default::default()
- };
- let pipeline = ctx.new_pipeline(
- &[BufferLayout::default()],
- &[
- VertexAttribute::new("in_pos", VertexFormat::Float2),
- VertexAttribute::new("in_color", VertexFormat::Float4),
- VertexAttribute::new("in_uv", VertexFormat::Float2),
- ],
- shader,
- params,
- );
- let (method_sender, method_recvr) = mpsc::sync_channel(100);
- let ftlib = ft::Library::init().unwrap();
- let mut font_faces = vec![];
- let font_data = include_bytes!("../ibm-plex-mono-light.otf") as &[u8];
- let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
- font_faces.push(ft_face);
- let font_data = include_bytes!("../NotoColorEmoji.ttf") as &[u8];
- let ft_face = ftlib.new_memory_face2(font_data, 0).unwrap();
- font_faces.push(ft_face);
- let mut stage = Stage {
- ctx,
- pipeline,
- scene_graph,
- textures,
- font_faces,
- method_recvr,
- method_sender,
- last_draw_time: None,
- };
- stage.setup_scene_graph_window();
- debug!("Finished loading GUI");
- stage
- }
- fn setup_scene_graph_window(&mut self) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let window = scene_graph.add_node("window", SceneNodeType::Window);
- let (screen_width, screen_height) = window::screen_size();
- let mut prop = Property::new("screen_size", PropertyType::Float32, PropertySubType::Pixel);
- prop.set_array_len(2);
- prop.set_f32(0, screen_width);
- prop.set_f32(1, screen_height);
- window.add_property(prop).unwrap();
- window
- .add_signal(
- "resize",
- "Screen resize event",
- vec![
- ("screen_width", "", PropertyType::Float32),
- ("screen_height", "", PropertyType::Float32),
- ],
- )
- .unwrap();
- let window_id = window.id;
- let sender = self.method_sender.clone();
- let method_fn = Box::new(move |arg_data, response_fn| {
- sender.send((GraphicsMethodEvent::LoadTexture, window_id, arg_data, response_fn));
- });
- window
- .add_method(
- "load_texture",
- vec![("node_name", "", PropertyType::Str), ("path", "", PropertyType::Str)],
- vec![("node_id", "", PropertyType::SceneNodeId)],
- method_fn,
- )
- .unwrap();
- let sender = self.method_sender.clone();
- let method_fn = Box::new(move |arg_data, response_fn| {
- sender.send((GraphicsMethodEvent::CreateChatView, window_id, arg_data, response_fn));
- });
- window
- .add_method(
- "create_chat_view",
- vec![("node_id", "", PropertyType::SceneNodeId)],
- vec![],
- method_fn,
- )
- .unwrap();
- let sender = self.method_sender.clone();
- let method_fn = Box::new(move |arg_data, response_fn| {
- sender.send((GraphicsMethodEvent::CreateEditBox, window_id, arg_data, response_fn));
- });
- window
- .add_method(
- "create_edit_box",
- vec![("node_id", "", PropertyType::SceneNodeId)],
- vec![],
- method_fn,
- )
- .unwrap();
- scene_graph.link(window_id, SceneGraph::ROOT_ID).unwrap();
- let input = scene_graph.add_node("input", SceneNodeType::WindowInput);
- let input_id = input.id;
- scene_graph.link(input_id, window_id).unwrap();
- let keyb = scene_graph.add_node("keyboard", SceneNodeType::Keyboard);
- keyb.add_signal(
- "key_down",
- "Key press down event",
- vec![
- ("shift", "", PropertyType::Bool),
- ("ctrl", "", PropertyType::Bool),
- ("alt", "", PropertyType::Bool),
- ("logo", "", PropertyType::Bool),
- ("repeat", "", PropertyType::Bool),
- ("keycode", "", PropertyType::Enum),
- ],
- )
- .unwrap();
- keyb.add_signal(
- "key_up",
- "Key press up event",
- vec![
- ("shift", "", PropertyType::Bool),
- ("ctrl", "", PropertyType::Bool),
- ("alt", "", PropertyType::Bool),
- ("logo", "", PropertyType::Bool),
- ("repeat", "", PropertyType::Bool),
- ("keycode", "", PropertyType::Enum),
- ],
- )
- .unwrap();
- let keyb_id = keyb.id;
- scene_graph.link(keyb_id, input_id).unwrap();
- let mouse = scene_graph.add_node("mouse", SceneNodeType::Mouse);
- mouse
- .add_signal(
- "button_up",
- "Mouse button up event",
- vec![
- ("button", "", PropertyType::Enum),
- ("x", "", PropertyType::Float32),
- ("y", "", PropertyType::Float32),
- ],
- )
- .unwrap();
- mouse
- .add_signal(
- "button_down",
- "Mouse button down event",
- vec![
- ("button", "", PropertyType::Enum),
- ("x", "", PropertyType::Float32),
- ("y", "", PropertyType::Float32),
- ],
- )
- .unwrap();
- mouse
- .add_signal(
- "wheel",
- "Mouse wheel scroll event",
- vec![("x", "", PropertyType::Float32), ("y", "", PropertyType::Float32)],
- )
- .unwrap();
- mouse
- .add_signal(
- "move",
- "Mouse cursor move event",
- vec![("x", "", PropertyType::Float32), ("y", "", PropertyType::Float32)],
- )
- .unwrap();
- let mouse_id = mouse.id;
- scene_graph.link(mouse_id, input_id).unwrap();
- }
- fn method_load_texture(&mut self, node_id: SceneNodeId, arg_data: Vec<u8>) -> Result<Vec<u8>> {
- let mut cur = Cursor::new(&arg_data);
- let node_name = String::decode(&mut cur).unwrap();
- let filepath = String::decode(&mut cur).unwrap();
- let Ok(img) = image::open(filepath) else { return Err(Error::FileNotFound) };
- let img = img.to_rgba8();
- let width = img.width();
- let height = img.height();
- let bmp = img.into_raw();
- let texture = self.ctx.new_texture_from_rgba8(width as u16, height as u16, &bmp);
- let id = self.textures.alloc(texture);
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let img_node = scene_graph.add_node(node_name, SceneNodeType::RenderTexture);
- let mut prop = Property::new("size", PropertyType::Uint32, PropertySubType::Pixel);
- prop.set_array_len(2);
- prop.set_u32(0, width).unwrap();
- prop.set_u32(1, height).unwrap();
- img_node.add_property(prop)?;
- let mut prop =
- Property::new("texture_rid", PropertyType::Uint32, PropertySubType::ResourceId);
- prop.set_u32(0, id).unwrap();
- img_node.add_property(prop)?;
- let mut reply = vec![];
- img_node.id.encode(&mut reply).unwrap();
- Ok(reply)
- }
- fn method_delete_texture(
- &mut self,
- node_id: SceneNodeId,
- arg_data: Vec<u8>,
- ) -> Result<Vec<u8>> {
- let mut cur = Cursor::new(&arg_data);
- let texture_id = ResourceId::decode(&mut cur).unwrap();
- let texture = self.textures.get(texture_id).ok_or(Error::ResourceNotFound)?;
- self.ctx.delete_texture(*texture);
- self.textures.free(texture_id);
- Ok(vec![])
- }
- fn method_create_chatview(
- &mut self,
- _: SceneNodeId,
- arg_data: Vec<u8>,
- ) -> Result<Vec<u8>> {
- let mut cur = Cursor::new(&arg_data);
- let node_id = SceneNodeId::decode(&mut cur).unwrap();
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let editbox = chatview::ChatView::new(&mut scene_graph, node_id, self.font_faces.clone())?;
- let node = scene_graph.get_node_mut(node_id).ok_or(Error::NodeNotFound)?;
- node.pimpl = editbox;
- let mut reply = vec![];
- Ok(reply)
- }
- fn method_create_editbox(
- &mut self,
- _: SceneNodeId,
- arg_data: Vec<u8>,
- ) -> Result<Vec<u8>> {
- let mut cur = Cursor::new(&arg_data);
- let node_id = SceneNodeId::decode(&mut cur).unwrap();
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let editbox = editbox::EditBox::new(&mut scene_graph, node_id, self.font_faces.clone())?;
- let node = scene_graph.get_node_mut(node_id).ok_or(Error::NodeNotFound)?;
- node.pimpl = editbox;
- let mut reply = vec![];
- Ok(reply)
- }
- }
- pub struct RenderContext<'a> {
- pub scene_graph: MutexGuard<'a, SceneGraph>,
- pub ctx: &'a mut Box<dyn RenderingBackend>,
- pub pipeline: &'a Pipeline,
- pub proj: glam::Mat4,
- pub textures: &'a ResourceManager<TextureId>,
- pub font_faces: &'a Vec<FreetypeFace>,
- }
- impl<'a> RenderContext<'a> {
- fn render_window(&mut self) {
- for layer in self
- .scene_graph
- .lookup_node("/window")
- .expect("no window attached!")
- .get_children(&[SceneNodeType::RenderLayer])
- {
- if let Err(err) = self.render_layer(layer.id) {
- error!("error rendering layer '{}': {}", layer.name, err)
- }
- }
- self.ctx.commit_frame();
- }
- fn get_rect(layer: &SceneNode) -> Result<Rectangle<i32>> {
- let prop = layer.get_property("rect").ok_or(Error::PropertyNotFound)?;
- if prop.array_len != 4 {
- return Err(Error::PropertyWrongLen)
- }
- let mut rect = [0; 4];
- for i in 0..4 {
- if prop.is_expr(i)? {
- let (screen_width, screen_height) = window::screen_size();
- let expr = prop.get_expr(i).unwrap();
- let machine = SExprMachine {
- globals: vec![
- ("sw".to_string(), SExprVal::Float32(screen_width)),
- ("sh".to_string(), SExprVal::Float32(screen_height)),
- ],
- stmts: &expr,
- };
- rect[i] = machine.call()?.as_u32()? as i32;
- } else {
- rect[i] = prop.get_u32(i)? as i32;
- }
- }
- Ok(Rectangle::from_array(rect))
- }
- fn render_layer(
- &mut self,
- layer_id: SceneNodeId,
- // parent rect
- ) -> Result<()> {
- let layer = self.scene_graph.get_node(layer_id).unwrap();
- if !layer.get_property_bool("is_visible")? {
- return Ok(())
- }
- self.ctx.begin_default_pass(PassAction::Nothing);
- self.ctx.apply_pipeline(&self.pipeline);
- let (_, screen_height) = window::screen_size();
- let rect = Self::get_rect(&layer)?;
- let mut view = rect.clone();
- view.y = screen_height as i32 - (rect.y + rect.h);
- self.ctx.apply_viewport(view.x, view.y, view.w, view.h);
- self.ctx.apply_scissor_rect(view.x, view.y, view.w, view.h);
- let rect = Rectangle {
- x: rect.x as f32,
- y: rect.y as f32,
- w: rect.w as f32,
- h: rect.h as f32,
- };
- let layer_children =
- layer.get_children(&[SceneNodeType::RenderMesh, SceneNodeType::RenderText, SceneNodeType::EditBox, SceneNodeType::ChatView]);
- let layer_children = self.order_by_z_index(layer_children);
- // get the rectangle
- // make sure it's inside the parent's rect
- for child in layer_children {
- // x, y, w, h as pixels
- // note that (x, y) is offset by layer rect so it is the pos within layer
- // layer coords are (0, 0) -> (1, 1)
- // optionally evaluated using sexpr
- // mesh data is (0, 0) to (1, 1)
- // so scale by (w, h)
- match child.typ {
- SceneNodeType::RenderMesh => {
- if let Err(err) = self.render_mesh(child.id, &rect) {
- error!("error rendering mesh '{}': {}", child.name, err);
- }
- }
- SceneNodeType::RenderText => {
- if let Err(err) = self.render_text(child.id, &rect) {
- error!("error rendering text '{}': {}", child.name, err);
- }
- }
- SceneNodeType::ChatView => {
- let node = self.scene_graph.get_node(child.id).unwrap();
- let chatview = match &node.pimpl {
- Pimpl::ChatView(e) => e.clone(),
- _ => panic!("wrong pimpl for editbox")
- };
- if let Err(err) = chatview.render(self, child.id, &rect) {
- error!("error rendering chatview '{}': {}", child.name, err);
- }
- }
- SceneNodeType::EditBox => {
- let node = self.scene_graph.get_node(child.id).unwrap();
- let editbox = match &node.pimpl {
- Pimpl::EditBox(e) => e.clone(),
- _ => panic!("wrong pimpl for editbox")
- };
- if let Err(err) = editbox.render(self, child.id, &rect) {
- error!("error rendering editbox '{}': {}", child.name, err);
- }
- }
- _ => panic!("render_layer(): unknown type"),
- }
- }
- Ok(())
- }
- fn order_by_z_index(&self, nodes: Vec<SceneNodeInfo>) -> Vec<SceneNodeInfo> {
- let mut nodes: Vec<_> = nodes
- .into_iter()
- .filter_map(|node_inf| {
- let node = self.scene_graph.get_node(node_inf.id).unwrap();
- //if !node.get_property_bool("is_visible").ok()? {
- // return None
- //}
- let z_index = node.get_property_u32("z_index").ok()?;
- Some((z_index, node_inf))
- })
- .collect();
- nodes.sort_unstable_by_key(|(z_index, node_inf)| *z_index);
- nodes.into_iter().map(|(z_index, node_inf)| node_inf).collect()
- }
- pub fn get_dim(mesh: &SceneNode, layer_rect: &Rectangle<f32>) -> Result<Rectangle<f32>> {
- let prop = mesh.get_property("rect").ok_or(Error::PropertyNotFound)?;
- if prop.array_len != 4 {
- return Err(Error::PropertyWrongLen)
- }
- let mut rect = [0.; 4];
- for i in 0..4 {
- if prop.is_expr(i)? {
- let expr = prop.get_expr(i).unwrap();
- let machine = SExprMachine {
- globals: vec![
- ("lw".to_string(), SExprVal::Uint32(layer_rect.w as u32)),
- ("lh".to_string(), SExprVal::Uint32(layer_rect.h as u32)),
- ],
- stmts: &expr,
- };
- rect[i] = machine.call()?.coerce_f32()?;
- } else {
- rect[i] = prop.get_f32(i)?;
- }
- }
- Ok(Rectangle::from_array(rect))
- }
- fn render_mesh(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<f32>) -> Result<()> {
- let mesh = self.scene_graph.get_node(node_id).unwrap();
- let z_index = mesh.get_property_u32("z_index")?;
- let data = mesh.get_property("data").ok_or(Error::PropertyNotFound)?;
- let verts = data.get_buf(0)?;
- let faces = data.get_buf(1)?;
- let vertex_buffer = self.ctx.new_buffer(
- BufferType::VertexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&verts),
- );
- let bufsrc = unsafe {
- BufferSource::pointer(
- faces.as_ptr() as _,
- std::mem::size_of_val(&faces[..]),
- std::mem::size_of::<u32>(),
- )
- };
- let index_buffer =
- self.ctx.new_buffer(BufferType::IndexBuffer, BufferUsage::Immutable, bufsrc);
- // temp
- let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap();
- let rect = Self::get_dim(mesh, layer_rect)?;
- //debug!("mesh rect: {:?}", rect);
- let layer_w = layer_rect.w as f32;
- let layer_h = layer_rect.h as f32;
- let off_x = rect.x / layer_w;
- let off_y = rect.y / layer_h;
- let scale_x = rect.w / layer_w;
- let scale_y = rect.h / layer_h;
- //let model = glam::Mat4::IDENTITY;
- let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
- glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
- let mut uniforms_data = [0u8; 128];
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) };
- uniforms_data[0..64].copy_from_slice(&data);
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
- uniforms_data[64..].copy_from_slice(&data);
- assert_eq!(128, 2 * UniformType::Mat4.size());
- let bindings =
- Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![*texture] };
- self.ctx.apply_bindings(&bindings);
- self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
- self.ctx.draw(0, 3 * faces.len() as i32, 1);
- self.ctx.delete_buffer(index_buffer);
- self.ctx.delete_buffer(vertex_buffer);
- Ok(())
- }
- pub fn render_clipped_box_with_texture2(
- &mut self,
- bound: &Rectangle<f32>,
- obj: &Rectangle<f32>,
- color: Color,
- texture: TextureId,
- ) {
- let Some(clipped) = bound.clip(&obj) else {
- return
- };
- let x1 = clipped.x;
- let y1 = clipped.y;
- let x2 = clipped.x + clipped.w;
- let y2 = clipped.y + clipped.h;
- let u1 = (clipped.x - obj.x) / obj.w;
- let u2 = (clipped.x + clipped.w - obj.x) / obj.w;
- let v1 = (clipped.y - obj.y) / obj.h;
- let v2 = (clipped.y + clipped.h - obj.y) / obj.h;
- let vertices: [Vertex; 4] = [
- // top left
- Vertex { pos: [x1, y1], color, uv: [u1, v1] },
- // top right
- Vertex { pos: [x2, y1], color, uv: [u2, v1] },
- // bottom left
- Vertex { pos: [x1, y2], color, uv: [u1, v2] },
- // bottom right
- Vertex { pos: [x2, y2], color, uv: [u2, v2] },
- ];
- //debug!("screen size: {:?}", window::screen_size());
- let vertex_buffer = self.ctx.new_buffer(
- BufferType::VertexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&vertices),
- );
- let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
- let index_buffer = self.ctx.new_buffer(
- BufferType::IndexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&indices),
- );
- let bindings =
- Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
- self.ctx.apply_bindings(&bindings);
- self.ctx.draw(0, 6, 1);
- }
- pub fn render_clipped_box_with_texture(
- &mut self,
- bound_rect: &Rectangle<f32>,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- color: Color,
- texture: TextureId,
- ) {
- let obj = Rectangle {
- x: x1,
- y: y1,
- w: x2 - x1,
- h: y2 - y1
- };
- if obj.w == 0. || obj.h == 0. {
- return
- }
- let Some(clipped) = bound_rect.clip(&obj) else {
- return
- };
- let x1 = clipped.x;
- let y1 = clipped.y;
- let x2 = clipped.x + clipped.w;
- let y2 = clipped.y + clipped.h;
- let u1 = (clipped.x - obj.x) / obj.w;
- let u2 = (clipped.x + clipped.w - obj.x) / obj.w;
- let v1 = (clipped.y - obj.y) / obj.h;
- let v2 = (clipped.y + clipped.h - obj.y) / obj.h;
- let vertices: [Vertex; 4] = [
- // top left
- Vertex { pos: [x1, y1], color, uv: [u1, v1] },
- // top right
- Vertex { pos: [x2, y1], color, uv: [u2, v1] },
- // bottom left
- Vertex { pos: [x1, y2], color, uv: [u1, v2] },
- // bottom right
- Vertex { pos: [x2, y2], color, uv: [u2, v2] },
- ];
- //debug!("screen size: {:?}", window::screen_size());
- let vertex_buffer = self.ctx.new_buffer(
- BufferType::VertexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&vertices),
- );
- let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
- let index_buffer = self.ctx.new_buffer(
- BufferType::IndexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&indices),
- );
- let bindings =
- Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
- self.ctx.apply_bindings(&bindings);
- self.ctx.draw(0, 6, 1);
- }
- pub fn render_box_with_texture(
- &mut self,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- color: Color,
- texture: TextureId,
- ) {
- let vertices: [Vertex; 4] = [
- // top left
- Vertex { pos: [x1, y1], color, uv: [0., 0.] },
- // top right
- Vertex { pos: [x2, y1], color, uv: [1., 0.] },
- // bottom left
- Vertex { pos: [x1, y2], color, uv: [0., 1.] },
- // bottom right
- Vertex { pos: [x2, y2], color, uv: [1., 1.] },
- ];
- //debug!("screen size: {:?}", window::screen_size());
- let vertex_buffer = self.ctx.new_buffer(
- BufferType::VertexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&vertices),
- );
- let indices: [u16; 6] = [0, 2, 1, 1, 2, 3];
- let index_buffer = self.ctx.new_buffer(
- BufferType::IndexBuffer,
- BufferUsage::Immutable,
- BufferSource::slice(&indices),
- );
- let bindings =
- Bindings { vertex_buffers: vec![vertex_buffer], index_buffer, images: vec![texture] };
- self.ctx.apply_bindings(&bindings);
- self.ctx.draw(0, 6, 1);
- }
- pub fn render_box(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
- let texture = self.textures.get(Stage::WHITE_TEXTURE_ID).unwrap();
- self.render_box_with_texture(x1, y1, x2, y2, color, *texture)
- }
- pub fn hline(&mut self, min_x: f32, max_x: f32, y: f32, color: Color, w: f32) {
- self.render_box(min_x, y - w / 2., max_x, y + w / 2., color)
- }
- pub fn vline(&mut self, x: f32, min_y: f32, max_y: f32, color: Color, w: f32) {
- self.render_box(x - w / 2., min_y, x + w / 2., max_y, color)
- }
- pub fn outline(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, color: Color, w: f32) {
- // top
- self.render_box(x1, y1, x2, y1 + w, color);
- // left
- self.render_box(x1, y1, x1 + w, y2, color);
- // right
- self.render_box(x2 - w, y1, x2, y2, color);
- // bottom
- self.render_box(x1, y2 - w, x2, y2, color);
- }
- fn render_text(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<f32>) -> Result<()> {
- let node = self.scene_graph.get_node(node_id).unwrap();
- let text = node.get_property_str("text")?;
- let font_size = node.get_property_f32("font_size")?;
- let debug = node.get_property_bool("debug")?;
- let rect = Self::get_dim(node, layer_rect)?;
- let baseline = node.get_property_f32("baseline")?;
- let color_prop = node.get_property("color").ok_or(Error::PropertyNotFound)?;
- let color_r = color_prop.get_f32(0)?;
- let color_g = color_prop.get_f32(1)?;
- let color_b = color_prop.get_f32(2)?;
- let color_a = color_prop.get_f32(3)?;
- let layer_w = layer_rect.w as f32;
- let layer_h = layer_rect.h as f32;
- let off_x = rect.x / layer_w;
- let off_y = rect.y / layer_h;
- // Use absolute pixel scale
- let scale_x = 1. / layer_w;
- let scale_y = 1. / layer_h;
- //let model = glam::Mat4::IDENTITY;
- let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
- glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
- let mut uniforms_data = [0u8; 128];
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&self.proj) };
- uniforms_data[0..64].copy_from_slice(&data);
- let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
- uniforms_data[64..].copy_from_slice(&data);
- assert_eq!(128, 2 * UniformType::Mat4.size());
- self.ctx.apply_uniforms_from_bytes(uniforms_data.as_ptr(), uniforms_data.len());
- //let mut strings = vec![];
- //let mut current_str = String::new();
- //let mut current_idx = 0;
- //for chr in text.chars() {
- // let ft_face = self.font_faces[current_idx];
- // if ft_face.get_char_index(chr as usize).is_some() {
- // }
- //}
- let mut current_idx = 0;
- let mut current_str = String::new();
- let mut substrs = vec![];
- 'next_char: for chr in text.chars() {
- let idx = 'get_idx: {
- for i in 0..self.font_faces.len() {
- let ft_face = &self.font_faces[i];
- if ft_face.get_char_index(chr as usize).is_some() {
- break 'get_idx i
- }
- }
- warn!("no font fallback for char: {}", chr);
- // Skip this char
- continue 'next_char
- };
- if current_idx != idx {
- if !current_str.is_empty() {
- // Push
- substrs.push((current_idx, current_str.clone()));
- }
- current_str.clear();
- current_idx = idx;
- }
- current_str.push(chr);
- }
- if !current_str.is_empty() {
- // Push
- substrs.push((current_idx, current_str));
- }
- let mut current_x = 0.;
- let mut current_y = baseline;
- for (face_idx, text) in substrs {
- let face = &self.font_faces[face_idx];
- if face.has_fixed_sizes() {
- // emojis required a fixed size
- //face.set_char_size(109 * 64, 0, 72, 72).unwrap();
- face.select_size(0).unwrap();
- } else {
- face.set_char_size(font_size as isize * 64, 0, 72, 72).unwrap();
- }
- let hb_font = harfbuzz_rs::Font::from_freetype_face(face.clone());
- let buffer = harfbuzz_rs::UnicodeBuffer::new().add_str(&text);
- let output = harfbuzz_rs::shape(&hb_font, buffer, &[]);
- let positions = output.get_glyph_positions();
- let infos = output.get_glyph_infos();
- for (position, info) in positions.iter().zip(infos) {
- let gid = info.codepoint;
- // Index within this substr
- // let cluster = info.cluster;
- let mut flags = ft::face::LoadFlag::DEFAULT;
- if face.has_color() {
- flags |= ft::face::LoadFlag::COLOR;
- }
- face.load_glyph(gid, flags).unwrap();
- let glyph = face.glyph();
- glyph.render_glyph(ft::RenderMode::Normal).unwrap();
- // https://gist.github.com/jokertarot/7583938?permalink_comment_id=3327566#gistcomment-3327566
- let bmp = glyph.bitmap();
- let buffer = bmp.buffer();
- let bmp_width = bmp.width() as usize;
- let bmp_height = bmp.rows() as usize;
- let bearing_x = glyph.bitmap_left() as f32;
- let bearing_y = glyph.bitmap_top() as f32;
- //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Bgra);
- //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Lcd);
- //assert_eq!(bmp.pixel_mode().unwrap(), ft::bitmap::PixelMode::Gray);
- let pixel_mode = bmp.pixel_mode().unwrap();
- let tdata = match pixel_mode {
- ft::bitmap::PixelMode::Bgra => {
- let mut tdata = vec![];
- tdata.resize(4 * bmp_width * bmp_height, 0);
- // Convert from BGRA to RGBA
- for i in 0..bmp_width*bmp_height as usize {
- let idx = i*4;
- let b = buffer[idx];
- let g = buffer[idx + 1];
- let r = buffer[idx + 2];
- let a = buffer[idx + 3];
- tdata[idx] = r;
- tdata[idx + 1] = g;
- tdata[idx + 2] = b;
- tdata[idx + 3] = a;
- }
- tdata
- }
- ft::bitmap::PixelMode::Gray => {
- // Convert from greyscale to RGBA8
- let tdata: Vec<_> = buffer
- .iter()
- .flat_map(|coverage| {
- let r = (255. * color_r) as u8;
- let g = (255. * color_g) as u8;
- let b = (255. * color_b) as u8;
- let α = ((*coverage as f32) * color_a) as u8;
- vec![r, g, b, α]
- })
- .collect();
- tdata
- }
- _ => panic!("unsupport pixel mode: {:?}", pixel_mode)
- };
- let (x1, y1, x2, y2) = if face.has_fixed_sizes() {
- // Downscale by height
- let width = (bmp_width as f32 * font_size) / bmp_height as f32;
- let height = font_size;
- let x1 = current_x;
- let y1 = current_y - height;
- let x2 = current_x + width;
- let y2 = current_y;
- current_x += width;
- (x1, y1, x2, y2)
- } else {
- let (width, height) = (bmp_width as f32, bmp_height as f32);
- let off_x = position.x_offset as f32 / 64.;
- let off_y = position.y_offset as f32 / 64.;
- let x1 = current_x + off_x + bearing_x;
- let y1 = current_y - off_y - bearing_y;
- let x2 = x1 + width as f32;
- let y2 = y1 + height as f32;
- let x_advance = position.x_advance as f32 / 64.;
- let y_advance = position.y_advance as f32 / 64.;
- current_x += x_advance;
- current_y += y_advance;
- (x1, y1, x2, y2)
- };
- let texture = self.ctx.new_texture_from_rgba8(bmp_width as u16, bmp_height as u16, &tdata);
- self.render_box_with_texture(x1, y1, x2, y2, COLOR_WHITE, texture);
- self.ctx.delete_texture(texture);
- if debug {
- self.outline(x1, y1, x2, y2, COLOR_BLUE, 1.);
- }
- }
- if debug {
- self.hline(0., current_x, 0., COLOR_RED, 1.);
- }
- }
- Ok(())
- }
- fn render_glyph(&mut self, glyph_id: u32, font_size: f32, x: f32, y: f32) -> Result<()> {
- Ok(())
- }
- }
- impl EventHandler for Stage {
- fn update(&mut self) {
- if self.last_draw_time.is_none() {
- return
- }
- // Only allow 20 ms, process as much as we can during that time
- let elapsed_since_draw = self.last_draw_time.unwrap().elapsed();
- // We're long overdue a redraw. Exit for now
- if elapsed_since_draw > Duration::from_millis(20) {
- return
- }
- // The next redraw must happen 20ms since its last one.
- // Calculate how much time is remaining until then.
- let allowed_time = Duration::from_millis(20) - elapsed_since_draw;
- let deadline = Instant::now() + allowed_time;
- loop {
- let Ok((event, node_id, arg_data, response_fn)) =
- self.method_recvr.recv_deadline(deadline)
- else {
- break
- };
- let res = match event {
- GraphicsMethodEvent::LoadTexture => self.method_load_texture(node_id, arg_data),
- GraphicsMethodEvent::DeleteTexture => self.method_delete_texture(node_id, arg_data),
- GraphicsMethodEvent::CreateChatView => self.method_create_chatview(node_id, arg_data),
- GraphicsMethodEvent::CreateEditBox => self.method_create_editbox(node_id, arg_data),
- };
- response_fn(res);
- }
- }
- // Only do drawing here. Apps might not call this when minimized.
- fn draw(&mut self) {
- self.last_draw_time = Some(Instant::now());
- let (screen_width, screen_height) = window::screen_size();
- // This will make the top left (0, 0) and the bottom right (1, 1)
- // Default is (-1, 1) -> (1, -1)
- let proj = glam::Mat4::from_translation(glam::Vec3::new(-1., 1., 0.)) *
- glam::Mat4::from_scale(glam::Vec3::new(2., -2., 1.));
- //let proj = glam::Mat4::IDENTITY;
- let scene_graph = self.scene_graph.lock().unwrap();
- // We need this because scene_graph must remain locked for the duration of the rendering
- let mut render_context = RenderContext {
- scene_graph,
- ctx: &mut self.ctx,
- pipeline: &self.pipeline,
- proj,
- textures: &self.textures,
- font_faces: &self.font_faces,
- };
- render_context.render_window();
- drop(render_context);
- }
- fn key_down_event(&mut self, keycode: KeyCode, modifiers: KeyMods, repeat: bool) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let win = scene_graph.lookup_node_mut("/window/input/keyboard").unwrap();
- let key = keycode.to_str();
- let mut data = vec![];
- modifiers.shift.encode(&mut data).unwrap();
- modifiers.ctrl.encode(&mut data).unwrap();
- modifiers.alt.encode(&mut data).unwrap();
- modifiers.logo.encode(&mut data).unwrap();
- repeat.encode(&mut data).unwrap();
- key.encode(&mut data).unwrap();
- win.trigger("key_down", data).unwrap();
- }
- fn key_up_event(&mut self, keycode: KeyCode, modifiers: KeyMods) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let win = scene_graph.lookup_node_mut("/window/input/keyboard").unwrap();
- let key = keycode.to_str();
- let mut data = vec![];
- modifiers.shift.encode(&mut data).unwrap();
- modifiers.ctrl.encode(&mut data).unwrap();
- modifiers.alt.encode(&mut data).unwrap();
- modifiers.logo.encode(&mut data).unwrap();
- key.encode(&mut data).unwrap();
- win.trigger("key_up", data).unwrap();
- }
- fn mouse_motion_event(&mut self, x: f32, y: f32) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let mut data = vec![];
- x.encode(&mut data).unwrap();
- y.encode(&mut data).unwrap();
- let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap();
- mouse.trigger("move", data).unwrap();
- }
- fn mouse_wheel_event(&mut self, x: f32, y: f32) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let mut data = vec![];
- x.encode(&mut data).unwrap();
- y.encode(&mut data).unwrap();
- let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap();
- mouse.trigger("wheel", data).unwrap();
- }
- fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let mut data = vec![];
- button.to_u8().encode(&mut data).unwrap();
- x.encode(&mut data).unwrap();
- y.encode(&mut data).unwrap();
- let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap();
- mouse.trigger("button_down", data).unwrap();
- }
- fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) {
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let mut data = vec![];
- button.to_u8().encode(&mut data).unwrap();
- x.encode(&mut data).unwrap();
- y.encode(&mut data).unwrap();
- let mouse = scene_graph.lookup_node_mut("/window/input/mouse").unwrap();
- mouse.trigger("button_up", data).unwrap();
- }
- fn resize_event(&mut self, width: f32, height: f32) {
- let mut data = vec![];
- width.encode(&mut data).unwrap();
- height.encode(&mut data).unwrap();
- let mut scene_graph = self.scene_graph.lock().unwrap();
- let win = scene_graph.lookup_node_mut("/window").unwrap();
- let prop = win.get_property("screen_size").unwrap();
- prop.set_f32(0, width).unwrap();
- prop.set_f32(1, height).unwrap();
- win.trigger("resize", data).unwrap();
- }
- }
- pub fn run_gui(scene_graph: SceneGraphPtr) {
- #[cfg(target_os = "android")]
- {
- android_logger::init_once(
- android_logger::Config::default().with_max_level(LevelFilter::Debug).with_tag("fagman"),
- );
- }
- #[cfg(target_os = "linux")]
- {
- let term_logger = simplelog::TermLogger::new(
- simplelog::LevelFilter::Debug,
- simplelog::Config::default(),
- simplelog::TerminalMode::Mixed,
- simplelog::ColorChoice::Auto,
- );
- simplelog::CombinedLogger::init(vec![term_logger]).expect("logger");
- }
- let mut conf = miniquad::conf::Conf {
- high_dpi: true,
- window_resizable: true,
- platform: miniquad::conf::Platform {
- linux_backend: miniquad::conf::LinuxBackend::WaylandWithX11Fallback,
- wayland_use_fallback_decorations: false,
- ..Default::default()
- },
- ..Default::default()
- };
- let metal = std::env::args().nth(1).as_deref() == Some("metal");
- conf.platform.apple_gfx_api =
- if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
- miniquad::start(conf, || Box::new(Stage::new(scene_graph)));
- }
|