use std::{collections::BTreeMap, fs::File, io::Read}; use skia_safe::{ canvas::SrcRectConstraint, utils::text_utils::Align, Canvas, Color4f, ColorSpace, Data, Font, Image, Paint, Point, Rect, }; use twclient::world::{tee::Tee, World}; // const TEE_COLL_RADIUS: f32 = 16.0; const TEE_REND_RADIUS: f32 = 32.0; const TEE_EYE_ROTATION_RADIUS: f32 = 8.0; const TEE_EYE_SIZE: f32 = 12.0; const TEE_EYE_DISTANCE: f32 = 8.0; pub struct TeeRenderer { skins: BTreeMap, } impl TeeRenderer { pub fn new() -> Self { Self { skins: BTreeMap::new(), } } pub fn draw(&mut self, world: &World, canvas: &mut Canvas) { for t in world.tees.inner.values() { canvas.save(); canvas.translate((t.x as f32, t.y as f32)); self.draw_tee(canvas, t); canvas.restore(); } } pub fn draw_tee(&mut self, canvas: &mut Canvas, tee: &Tee) { let tee_paint = Paint::new( Color4f { a: 1.0, r: 1.0, g: 1.0, b: 1.0, }, &ColorSpace::new_srgb(), ); let name_paint = Paint::new( Color4f { a: 1.0, r: 1.0, g: 1.0, b: 0.0, }, &ColorSpace::new_srgb(), ); let skin_texture = self .skins .entry(tee.skin.clone()) .or_insert_with(|| TeeRenderer::load_skin(tee.name.as_str()).unwrap()); let origin = Point { x: tee.x as f32, y: tee.y as f32, }; canvas.draw_image_rect( &skin_texture, Some((&SKIN_BODY_SHADOW, SrcRectConstraint::Strict)), Rect { top: -TEE_REND_RADIUS, left: -TEE_REND_RADIUS, right: TEE_REND_RADIUS, bottom: TEE_REND_RADIUS, }, &tee_paint, ); canvas.draw_image_rect( &skin_texture, Some((&SKIN_BODY, SrcRectConstraint::Strict)), Rect { top: -TEE_REND_RADIUS, left: -TEE_REND_RADIUS, right: TEE_REND_RADIUS, bottom: TEE_REND_RADIUS, }, &tee_paint, ); { canvas.save(); // println!("{}", tee.angle); canvas.translate(( tee.angle.cos() * TEE_EYE_ROTATION_RADIUS, tee.angle.sin() * TEE_EYE_ROTATION_RADIUS, )); canvas.draw_image_rect( &skin_texture, Some((&SKIN_EYE_NORMAL, SrcRectConstraint::Strict)), Rect { top: -TEE_EYE_SIZE, left: -TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, right: TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, bottom: TEE_EYE_SIZE, }, &tee_paint, ); { canvas.save(); canvas.scale((-1.0, 1.0)); canvas.draw_image_rect( &skin_texture, Some((&SKIN_EYE_NORMAL, SrcRectConstraint::Strict)), Rect { top: -TEE_EYE_SIZE, left: -TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, right: TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, bottom: TEE_EYE_SIZE, }, &tee_paint, ); canvas.restore(); } canvas.restore(); } { canvas.save(); canvas.draw_image_rect( &skin_texture, Some((&SKIN_FOOT, SrcRectConstraint::Strict)), Rect { top: -TEE_EYE_SIZE, left: -TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, right: TEE_EYE_SIZE - TEE_EYE_DISTANCE / 2.0, bottom: TEE_EYE_SIZE, }, &tee_paint, ); canvas.restore(); } canvas.draw_str_align( tee.name.as_str(), (origin.x, origin.y - 20.0), &Font::default(), &name_paint, Align::Center, ); } pub fn load_skin(_name: &str) -> Option { // let path = "/usr/share/ddnet/data/skins/limekitty.png"; let path = "/home/muffin/.teeworlds/downloadedskins/limekittygirl.png"; let mut file = File::open(path).unwrap(); let mut data = Vec::new(); file.read_to_end(&mut data).unwrap(); let data = Data::new_copy(&data); Image::from_encoded(data) } } const SKIN_BODY: Rect = Rect { top: 0.0, left: 0.0, right: 96.0, bottom: 96.0, }; const SKIN_BODY_SHADOW: Rect = Rect { top: 0.0, left: 96.0, right: 192.0, bottom: 96.0, }; const SKIN_FOOT: Rect = Rect { left: 196.0, right: 256.0, top: 32.0, bottom: 64.0, }; const SKIN_EYE_NORMAL: Rect = Rect { left: 64.0, right: 96.0, top: 96.0, bottom: 128.0, };