use std::error::Error;
use async_trait::async_trait;
use log::debug;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
use crate::rendering::Renderer;
use wasm_bindgen::{JsCast, JsValue};
use crate::CLIENT;
use crate::rendering::util::texid_to_html_image_unchecked;
use crate::textures::TextureManager;
#[derive(Debug)]
pub struct WebRenderer {
canvas_element_id: String
}
pub const USERNAME_TEXT_ALIGN: &str = "center";
pub const USERNAME_FONT: &str = "30px Segoe UI";
pub const USERNAME_COLOR: &str = "white";
pub const USERNAME_OFFSET_X: f64 = 0f64;
pub const USERNAME_OFFSET_Y: f64 = -35f64;
pub const HEARTY_OFFSET_X: f64 = -25f64;
pub const HEARTY_OFFSET_Y: f64 = -25f64;
pub const HEARTY_WIDTH: f64 = 50f64;
pub const HEARTY_HEIGHT: f64 = 50f64;
#[async_trait]
impl Renderer for WebRenderer {
async fn get(canvas_element_id: &str) -> Result<Self, Box<dyn Error>> {
Ok(Self {
canvas_element_id: canvas_element_id.to_string()
})
}
async fn render_frame(&self, _time_delta_ms: f64) -> Result<(), Box<dyn Error>> {
// TODO - core is working on this, please no touchy without telling him
// TODO - until this notice is removed
// time_delta_ms is the delta, in ms, from when the last render_frame was called by the browser
let window = web_sys::window().ok_or("window needs to exist")?;
let document = window.document().ok_or("window.document needs to exist")?;
let canvas_element = document.get_element_by_id(&self.canvas_element_id).ok_or("canvas element does not exist")?;
let typed_canvas_element: HtmlCanvasElement = canvas_element.dyn_into::<web_sys::HtmlCanvasElement>().map_err(|_| ()).unwrap();
let context = typed_canvas_element.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap();
let client = CLIENT.read()?;
if client.client_data.is_none() {
return Err("client not yet initialized".into());
}
let client_data = client.client_data.as_ref().unwrap();
context.set_transform(1f64, 0f64, 0f64, 1f64, 0f64, 0f64).map_err(|e: JsValue| e.as_string().unwrap())?;
context.clear_rect(0f64, 0f64, typed_canvas_element.width() as f64, typed_canvas_element.height() as f64);
let hearty = texid_to_html_image_unchecked("hearty");
// draw players
for player in &client.players {
context.save(); // save current position
// teleport to the player's location
context.translate(-player.x, -player.y).map_err(|e| e.as_string().unwrap())?;
debug!("[render] PL: ({}, {}) {}", player.x, player.y, player.username);
// draw username
context.set_text_align(USERNAME_TEXT_ALIGN);
context.set_font(USERNAME_FONT);
context.set_fill_style(&JsValue::from_str(USERNAME_COLOR)); // CssStyleColor
context.fill_text(&player.username, USERNAME_OFFSET_X, USERNAME_OFFSET_Y).map_err(|e: JsValue| e.as_string().unwrap())?;
// rotate the canvas so we can draw hearty
context.rotate(player.rotation).map_err(|e| e.as_string().unwrap())?;
// draw hearty
context.draw_image_with_html_image_element_and_dw_and_dh(&hearty, HEARTY_OFFSET_X, HEARTY_OFFSET_Y, HEARTY_WIDTH, HEARTY_HEIGHT).map_err(|e| e.as_string().unwrap())?;
context.restore(); // return to canvas base
}
// finally, translate to hearty
context.translate(-client.x + ((typed_canvas_element.width() / 2) as f64), -client.y + ((typed_canvas_element.height() / 2) as f64)).map_err(|e| e.as_string().unwrap())?;
/*
context.begin_path();
// Draw the outer circle.
context
.arc(75.0, 75.0, 50.0, 0.0, std::f64::consts::PI * 2.0)
.unwrap();
// Draw the mouth.
context.move_to(110.0, 75.0);
context.arc(75.0, 75.0, 35.0, 0.0, std::f64::consts::PI).unwrap();
// Draw the left eye.
context.move_to(65.0, 65.0);
context
.arc(60.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
.unwrap();
// Draw the right eye.
context.move_to(95.0, 65.0);
context
.arc(90.0, 65.0, 5.0, 0.0, std::f64::consts::PI * 2.0)
.unwrap();
context.stroke();
*/
Ok(())
}
}