use crate::{
utils, AttributeJs, AttributeMap, BufferJs, BufferMap, FramebufferJs, RenderCallback,
RendererData, RendererDataBuilderJs, RendererJs, RendererJsInner, StringArray, TextureJs,
TextureJsArray, TextureMap, UniformJs, UniformMap, WebGlProgramMap, WebGlShaderMap,
};
use js_sys::{Array, Map, Object};
use log::error;
use std::{
cell::RefCell,
ops::{Deref, DerefMut},
rc::Rc,
};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use web_sys::{
HtmlCanvasElement, WebGl2RenderingContext, WebGlProgram, WebGlShader, WebGlTransformFeedback,
WebGlVertexArrayObject,
};
pub type RendererDataJsInner = RendererData<
String,
String,
String,
String,
String,
String,
String,
String,
String,
String,
Object,
>;
#[wasm_bindgen(inspectable, js_name = RendererData)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RendererDataJs(Rc<RefCell<RendererDataJsInner>>);
#[wasm_bindgen(js_class = RendererData)]
impl RendererDataJs {
pub fn builder() -> RendererDataBuilderJs {
RendererDataBuilderJs::default()
}
pub fn canvas(&self) -> HtmlCanvasElement {
self.deref().borrow().canvas().clone()
}
pub fn gl(&self) -> WebGl2RenderingContext {
self.deref().borrow().gl().clone()
}
#[wasm_bindgen(js_name = fragmentShader)]
pub fn fragment_shader(&self, fragment_shader_id: String) -> Option<WebGlShader> {
self.deref()
.borrow()
.fragment_shader(&fragment_shader_id)
.map(Clone::clone)
}
#[wasm_bindgen(js_name = fragmentShaders)]
pub fn fragment_shaders(&self) -> WebGlShaderMap {
let map = Map::new();
for (key, value) in self.deref().borrow().fragment_shaders().iter() {
map.set(&JsValue::from_str(key), value.as_ref());
}
map.dyn_into()
.expect("Should be able to convert Map into WebGlShaderMap")
}
#[wasm_bindgen(js_name = vertexShader)]
pub fn vertex_shader(&self, vertex_shader_id: String) -> Option<WebGlShader> {
self.deref()
.borrow()
.vertex_shader(&vertex_shader_id)
.map(Clone::clone)
}
#[wasm_bindgen(js_name = vertexShaders)]
pub fn vertex_shaders(&self) -> WebGlShaderMap {
let map = Map::new();
for (key, value) in self.deref().borrow().vertex_shaders().iter() {
map.set(&JsValue::from_str(key), value.as_ref());
}
map.dyn_into()
.expect("Should be able to convert Map into WebGlShaderMap")
}
pub fn program(&self, program_id: String) -> Option<WebGlProgram> {
self.deref().borrow().program(&program_id).map(Clone::clone)
}
pub fn programs(&self) -> WebGlProgramMap {
let map = Map::new();
for (key, value) in self.deref().borrow().programs().iter() {
map.set(&JsValue::from_str(key), value.as_ref());
}
map.dyn_into()
.expect("Should be able to convert Map into WebGlProgramMap")
}
pub fn uniform(&self, uniform_id: String) -> Option<UniformJs> {
self.deref().borrow().uniform(&uniform_id).map(Into::into)
}
pub fn uniforms(&self) -> UniformMap {
let map = Map::new();
for (key, uniform) in self.deref().borrow().uniforms().iter() {
let js_uniform: UniformJs = uniform.into();
map.set(&JsValue::from_str(key), &js_uniform.into());
}
map.dyn_into()
.expect("Should be able to convert Map into UniformMap")
}
pub fn buffer(&self, buffer_id: String) -> Option<BufferJs> {
self.deref().borrow().buffer(&buffer_id).map(Into::into)
}
pub fn buffers(&self) -> BufferMap {
let map = Map::new();
for (key, buffer) in self.deref().borrow().buffers().iter() {
let js_buffer: BufferJs = buffer.into();
map.set(&JsValue::from_str(key), &js_buffer.into());
}
map.dyn_into()
.expect("Should be able to convert Map into BufferMap")
}
pub fn attribute(&self, attribute_id: String) -> Option<AttributeJs> {
self.deref()
.borrow()
.attribute(&attribute_id)
.map(Into::into)
}
pub fn attributes(&self) -> AttributeMap {
let map = Map::new();
for (key, value) in self.deref().borrow().attributes().iter() {
let attribute: AttributeJs = value.into();
map.set(&JsValue::from_str(key), &attribute.into());
}
map.dyn_into()
.expect("Should be able to convert Map into AttributeMap")
}
pub fn texture(&self, texture_id: String) -> Option<TextureJs> {
self.deref().borrow().texture(&texture_id).map(Into::into)
}
pub fn textures(&self) -> TextureMap {
let map = Map::new();
for (key, texture) in self.deref().borrow().textures().iter() {
let js_texture: TextureJs = texture.into();
map.set(&JsValue::from_str(key), &js_texture.into());
}
map.dyn_into()
.expect("Should be able to convert Map into TextureMap")
}
#[wasm_bindgen(js_name = textureById)]
pub fn textures_by_id(&self, texture_ids: StringArray) -> TextureJsArray {
let string_vec: Vec<String> = utils::js_array_to_vec_strings(&texture_ids);
let textures: Vec<JsValue> = self
.deref()
.borrow()
.textures_by_id(string_vec)
.iter()
.map(|texture| {
let js_texture: TextureJs = (*texture).into();
js_texture.into()
})
.collect();
Array::from_iter(textures)
.dyn_into()
.expect("Should be able to convert Array of Textures into TextureJsArray")
}
pub fn framebuffer(&self, framebuffer_id: String) -> Option<FramebufferJs> {
self.deref()
.borrow()
.framebuffer(&framebuffer_id)
.map(Into::into)
}
#[wasm_bindgen(js_name = transformFeedback)]
pub fn transform_feedback(
&self,
transform_feedback_id: String,
) -> Option<WebGlTransformFeedback> {
self.deref()
.borrow()
.transform_feedback(&transform_feedback_id)
.map(Clone::clone)
}
#[wasm_bindgen(js_name = VAO)]
pub fn vao(&self, vao_id: String) -> Option<WebGlVertexArrayObject> {
self.deref().borrow().vao(&vao_id).map(Clone::clone)
}
#[wasm_bindgen(js_name = userCtx)]
pub fn user_ctx(&self) -> Option<Object> {
self.deref().borrow().user_ctx().map(Clone::clone)
}
#[wasm_bindgen(js_name = useProgram)]
pub fn use_program(&self, program_id: String) {
self.deref().borrow().use_program(&program_id);
}
#[wasm_bindgen(js_name = useVAO)]
pub fn use_vao(&self, vao_id: String) {
self.deref().borrow().use_vao(&vao_id);
}
#[wasm_bindgen(js_name = updateUniform)]
pub fn update_uniform(&self, uniform_id: String) {
self.deref().borrow().update_uniform(&uniform_id);
}
#[wasm_bindgen(js_name = updateUniforms)]
pub fn update_uniforms(&self) {
self.deref().borrow().update_uniforms();
}
pub fn render(&self) {
let render_callback = self.deref().borrow().render_callback();
if let Some(js_callback) = render_callback.js().as_ref() {
let js_value: JsValue = self.clone().into();
if let Err(err) = js_callback.call1(&JsValue::NULL, &js_value) {
error!("Error occurred while calling JavaScript `render` callback: {err:?}");
}
} else {
error!("`render_js` was called without any `js_callback` to call. This is a no-op.")
}
}
#[wasm_bindgen(js_name = saveImage)]
pub fn save_image(&self) {
self.deref().borrow().save_image();
}
#[wasm_bindgen(js_name = intoRendererHandle)]
pub fn into_renderer_handle(self) -> RendererJs {
self.into()
}
}
impl RendererDataJs {
pub fn into_inner(self) -> Rc<RefCell<RendererDataJsInner>> {
self.0
}
pub fn render_callback(
&self,
) -> RenderCallback<
String,
String,
String,
String,
String,
String,
String,
String,
String,
String,
Object,
> {
self.deref().borrow().render_callback()
}
}
impl Deref for RendererDataJs {
type Target = Rc<RefCell<RendererDataJsInner>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RendererDataJs {
fn deref_mut(&mut self) -> &mut Rc<RefCell<RendererDataJsInner>> {
&mut self.0
}
}
impl From<RendererDataJsInner> for RendererDataJs {
fn from(renderer_data_js_inner: RendererDataJsInner) -> Self {
Self(Rc::new(RefCell::new(renderer_data_js_inner)))
}
}
impl From<Rc<RefCell<RendererDataJsInner>>> for RendererDataJs {
fn from(renderer_data_js_inner: Rc<RefCell<RendererDataJsInner>>) -> Self {
Self(renderer_data_js_inner)
}
}
impl From<RendererDataJs> for RendererJs {
fn from(renderer_data_js: RendererDataJs) -> Self {
let renderer_data = Rc::clone(renderer_data_js.deref());
let renderer: RendererJsInner = RendererJsInner::new_with_rc_renderer(renderer_data);
renderer.into()
}
}